JdbcMapper/querymapper/src/main/java/com/moparisthebest/jdbc/UpdateableDTO.java

245 lines
8.5 KiB
Java

package com.moparisthebest.jdbc;
import com.moparisthebest.jdbc.util.ResultSetUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import static com.moparisthebest.jdbc.TryClose.tryClose;
public abstract class UpdateableDTO implements Finishable {
private boolean finished = false;
private Map<String, Object> changes = null;
private String tableName;
private String whereClause;
public static final String YES = ResultSetUtil.TRUE;
public static final String NO = ResultSetUtil.FALSE;
/**
* Will always return YES or NO from this class, so they CAN be compared with object equality '==' instead of '.equals()'
* @param bool
* @return
*/
public static String booleanToString(boolean bool){
return bool ? YES : NO;
}
public static boolean changed(Object oldValue, Object newValue){
return oldValue != newValue && ((oldValue != null && !oldValue.equals(newValue)) || !newValue.equals(oldValue));
}
public static <K, E> Object[] getUpdate(final StringBuilder sb, final Map<K, E> changes, final String tableName, final String whereClause){
if(changes.isEmpty())
return new Object[0];
sb.append("UPDATE ").append(tableName).append(" SET ");
int count = -1;
Object[] objectsToBind = new Object[changes.size()];
boolean notFirst = false;
for (Map.Entry<K, E> column : changes.entrySet()) {
if (column.getKey() == null) continue;
if (notFirst) sb.append(",");
else notFirst = true;
sb.append(column.getKey()).append("=?");
objectsToBind[++count] = column.getValue();
}
if(whereClause != null)
sb.append(" ").append(whereClause);
//System.out.println("updateRow: "+sb);
return objectsToBind;
}
public static <K, E> Object[] getInsert(final StringBuilder sb, final Map<K, E> changes, final String tableName){
if(changes.isEmpty())
return new Object[0];
sb.append("INSERT INTO ").append(tableName).append(" (");
int count = -1;
Object[] objectsToBind = new Object[changes.size()];
boolean notFirst = false;
for (Map.Entry<K, E> column : changes.entrySet()) {
if (column.getKey() == null) continue;
if (notFirst) sb.append(",");
else notFirst = true;
sb.append(column.getKey());
objectsToBind[++count] = column.getValue();
}
sb.append(") VALUES (?");
for(int x = 1; x < objectsToBind.length; ++x)
sb.append(",?");
sb.append(")");
//System.out.println("insertRow: "+sb);
return objectsToBind;
}
public static <K, E> int updateRow(final Connection conn, final Map<K, E> changes, final String tableName, final String whereClause, final Object... additionalBindObjects) throws SQLException {
StringBuilder sb = new StringBuilder();
Object[] objectsToBind = getUpdate(sb, changes, tableName, whereClause);
PreparedStatement ps = null;
try {
ps = conn.prepareStatement(sb.toString());
QueryMapper.bindStatement(ps, objectsToBind, additionalBindObjects == null ? new Object[0] : additionalBindObjects);
return ps.executeUpdate();
} finally {
tryClose(ps);
}
}
public static <K, E> int updateRow(final QueryMapper qm, final Map<K, E> changes, final String tableName, final String whereClause, final Object... additionalBindObjects) throws SQLException {
StringBuilder sb = new StringBuilder();
Object[] objectsToBind = getUpdate(sb, changes, tableName, whereClause);
return qm.executeUpdate(sb.toString(), objectsToBind, additionalBindObjects == null ? new Object[0] : additionalBindObjects);
}
public static <K, E> int insertRow(final Connection conn, final Map<K, E> changes, final String tableName) throws SQLException {
StringBuilder sb = new StringBuilder();
Object[] objectsToBind = getInsert(sb, changes, tableName);
PreparedStatement ps = null;
try {
ps = conn.prepareStatement(sb.toString());
QueryMapper.bindStatement(ps, objectsToBind);
return ps.executeUpdate();
} finally {
tryClose(ps);
}
}
public static <K, E> int insertRow(final QueryMapper qm, final Map<K, E> changes, final String tableName) throws SQLException {
StringBuilder sb = new StringBuilder();
Object[] objectsToBind = getInsert(sb, changes, tableName);
return qm.executeUpdate(sb.toString(), objectsToBind);
}
public static <K, E> int deleteRow(final Connection conn, final String tableName, final String whereClause, final Object... additionalBindObjects) throws SQLException {
PreparedStatement ps = null;
try {
ps = conn.prepareStatement(String.format("DELETE FROM %s %s", tableName, whereClause == null ? "" : whereClause));
if(additionalBindObjects != null)
QueryMapper.bindStatement(ps, additionalBindObjects);
return ps.executeUpdate();
} finally {
tryClose(ps);
}
}
public static <K, E> int deleteRow(final QueryMapper qm, final String tableName, final String whereClause, final Object... additionalBindObjects) throws SQLException {
return qm.executeUpdate(String.format("DELETE FROM %s %s", tableName, whereClause == null ? "" : whereClause), additionalBindObjects == null ? new Object[0] : additionalBindObjects);
}
protected UpdateableDTO(String tableName, String whereClause) {
setTableName(tableName);
this.whereClause = whereClause;
}
protected String getTableName() {
return tableName;
}
protected void setTableName(String tableName) {
if(tableName == null)
throw new NullPointerException("tableName can never be null! Insert, update, or delete would never work!");
this.tableName = tableName;
}
protected String getWhereClause() {
return whereClause;
}
protected void setWhereClause(String whereClause) {
this.whereClause = whereClause;
}
protected final void change(String columnName, boolean oldValue, boolean newValue) {
change(columnName, booleanToString(oldValue), booleanToString(newValue));
}
/**
* Right now this is NOT thread-safe, if this class is to be used concurrently, it needs synchronized
*
* This never saves the ORIGINAL value, so if you change a certain column to something new, then change it back
* to the original, it is still counted as a change. The way to fix this would be another Map<String, Object>
* of ORIGINAL values, but I'm not sure this is desired.
*
* @param columnName
* @param newValue
*/
protected final boolean change(String columnName, Object oldValue, Object newValue) {
if(finished && changed(oldValue, newValue)){
/*
System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++");
System.out.println("before: "+this);
System.out.printf("changing column '%s' from '%s' to '%s'\n", columnName, oldValue, newValue);
new Exception("Stack trace").printStackTrace(System.out);
System.out.println("----------------------------------------------");
*/
if (changes == null)
changes = new HashMap<String, Object>();
changes.put(columnName, newValue);
return true;
}
return false;
}
public boolean hasChanged() {
return changes != null && !changes.isEmpty(); // not null would probably suffice?
}
public int updateRow(Connection conn) throws SQLException {
if (!hasChanged())
return 0;
return updateRow(conn, changes, this.tableName, this.whereClause, this.getAdditionalBindObjects());
}
public int updateRow(QueryMapper qm) throws SQLException {
if (!hasChanged())
return 0;
return updateRow(qm, changes, this.tableName, this.whereClause, this.getAdditionalBindObjects());
}
public int insertRow(Connection conn) throws SQLException {
if (!hasChanged())
return 0;
return insertRow(conn, changes, this.tableName);
}
public int insertRow(QueryMapper qm) throws SQLException {
if (!hasChanged())
return 0;
return insertRow(qm, changes, this.tableName);
}
public int deleteRow(Connection conn) throws SQLException {
return deleteRow(conn, this.tableName, this.whereClause, this.getAdditionalBindObjects());
}
public int deleteRow(QueryMapper qm) throws SQLException {
return deleteRow(qm, this.tableName, this.whereClause, this.getAdditionalBindObjects());
}
@Override
public void finish(ResultSet rs) throws SQLException {
this.finished = true;
}
/**
* You will almost always want to return some objects here for your where clause
*
* if you need to bind a single null, return new Object[]{null}, otherwise nothing will be binded
* if you only need to bind a single non-null object, return that object here
* if you need to bind multiple objects, return an Object[] or a Collection
*/
public abstract Object getAdditionalBindObjects();
@Override
public String toString() {
return "UpdateableDTO{" +
"finished=" + finished +
", changes=" + changes +
'}';
}
}