package org.quickbundle.base.dao;
import java.io.ByteArrayInputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.quickbundle.ICoreConstants;
import org.quickbundle.config.RmBaseConfig;
import org.quickbundle.tools.helper.RmSqlHelper;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ParameterDisposer;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.SqlProvider;
import org.springframework.jdbc.core.StatementCallback;
import org.springframework.jdbc.core.StatementCreatorUtils;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.util.Assert;
public class RmJdbcTemplate extends JdbcTemplate {
static enum EnumBoolean {
NULL,
TRUE,
FALSE
}
private EnumBoolean absolutePage = EnumBoolean.NULL;
public EnumBoolean getAbsolutePage() {
return absolutePage;
}
/**
* is query page by ResultSet.absolute() mode? not spelling new sql
* @param absolutePage
*/
public void setAbsolutePage(boolean absolutePage) {
if(absolutePage) {
this.absolutePage = EnumBoolean.TRUE;
} else {
this.absolutePage = EnumBoolean.FALSE;
}
}
// -------------------------------------------------------------------------
// Methods dealing with static SQL (java.sql.Statement)
// -------------------------------------------------------------------------
public Object execute(StatementCallback action) throws DataAccessException {
Assert.notNull(action, "Callback object must not be null");
Connection con = DataSourceUtils.getConnection(getDataSource());
Statement stmt = null;
try {
Connection conToUse = con;
if (getNativeJdbcExtractor() != null &&
getNativeJdbcExtractor().isNativeConnectionNecessaryForNativeStatements()) {
conToUse = getNativeJdbcExtractor().getNativeConnection(con);
}
stmt = conToUse.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
applyStatementSettings(stmt);
Statement stmtToUse = stmt;
if (getNativeJdbcExtractor() != null) {
stmtToUse = getNativeJdbcExtractor().getNativeStatement(stmt);
}
Object result = action.doInStatement(stmtToUse);
handleWarnings(stmt);
return result;
}
catch (SQLException ex) {
// Release Connection early, to avoid potential connection pool deadlock
// in the case when the exception translator hasn't been initialized yet.
JdbcUtils.closeStatement(stmt);
stmt = null;
DataSourceUtils.releaseConnection(con, getDataSource());
con = null;
throw getExceptionTranslator().translate("StatementCallback", getSql(action), ex);
}
finally {
JdbcUtils.closeStatement(stmt);
DataSourceUtils.releaseConnection(con, getDataSource());
}
}
/**
* Determine SQL from potential provider object.
* @param sqlProvider object that's potentially a SqlProvider
* @return the SQL string, or <code>null</code>
* @see SqlProvider
*/
private static String getSql(Object sqlProvider) {
if (sqlProvider instanceof SqlProvider) {
return ((SqlProvider) sqlProvider).getSql();
}
else {
return null;
}
}
public int update(final String sql) throws DataAccessException {
return super.update(appendTs(sql));
}
public int update(String sql, Object[] args) throws DataAccessException {
return super.update(appendTs(sql), args, getSqlTypeFromArgs(args));
}
/**
* parse byte[] to Clob 对byte[]类型的字段,直接以流方式写入Blob
* @param sql
* @param args
* @return
* @throws DataAccessException
*/
public int updateWithBlob(String sql, final Object[] args) throws DataAccessException {
return super.update(sql, new RmArgTypePreparedStatementSetter(args, getSqlTypeFromArgs(args)));
}
private static class RmArgTypePreparedStatementSetter implements PreparedStatementSetter, ParameterDisposer {
private final Object[] args;
private final int[] argTypes;
/**
* Create a new ArgTypePreparedStatementSetter for the given arguments.
* @param args the arguments to set
* @param argTypes the corresponding SQL types of the arguments
*/
public RmArgTypePreparedStatementSetter(Object[] args, int[] argTypes) {
if ((args != null && argTypes == null) || (args == null && argTypes != null) ||
(args != null && args.length != argTypes.length)) {
throw new InvalidDataAccessApiUsageException("args and argTypes parameters must match");
}
this.args = args;
this.argTypes = argTypes;
}
public void setValues(PreparedStatement ps) throws SQLException {
int argIndx = 1;
if (this.args != null) {
for (int i = 0; i < this.args.length; i++) {
Object arg = this.args[i];
if (arg instanceof Collection && this.argTypes[i] != Types.ARRAY) {
Collection entries = (Collection) arg;
for (Iterator it = entries.iterator(); it.hasNext();) {
Object entry = it.next();
if (entry instanceof Object[]) {
Object[] valueArray = ((Object[])entry);
for (int k = 0; k < valueArray.length; k++) {
Object argValue = valueArray[k];
StatementCreatorUtils.setParameterValue(ps, argIndx++, this.argTypes[i], argValue);
}
}
else {
StatementCreatorUtils.setParameterValue(ps, argIndx++, this.argTypes[i], entry);
}
}
}
else {
if(this.argTypes[i] == Types.BLOB) {
byte[] bytes = (byte[])this.args[i];
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ps.setBinaryStream(argIndx++, bais, bytes.length);
} else {
StatementCreatorUtils.setParameterValue(ps, argIndx++, this.argTypes[i], arg);
}
}
}
}
}
public void cleanupParameters() {
StatementCreatorUtils.cleanupParameters(this.args);
}
}
public int[] batchUpdate(final String[] sql) throws DataAccessException {
for (int i = 0; i < sql.length; i++) {
sql[i] = appendTs(sql[i]);
}
return super.batchUpdate(sql);
}
public int[] batchUpdate(String sql, final Object[] aObj, final CircleVoArray cva) throws DataAccessException {
if (aObj.length == 0) {
return new int[0];
}
sql = appendTs(sql);
int[] aCount = super.batchUpdate(sql, new BatchPreparedStatementSetter() {
public void setValues(PreparedStatement ps, int i) throws SQLException {
Object[] args = cva.getArgs(aObj[i]);
ArgTypePreparedStatementSetter atpss = new ArgTypePreparedStatementSetter(args, getSqlTypeFromArgs(args));
atpss.setValues(ps);
}
public int getBatchSize() {
return aObj.length;
}
});
return aCount;
}
/**
* 自动加TS更新戳
*
* @param sql
* @return
*/
private String appendTs(String sql) {
// if(RmBaseConfig.sqlUpdateAutoAppendTs()) {
// //检测是否有TS列
// if(RmTableTsDetector.containTs(sql)) {
// //sql = CrossDBObject.translate(sql);
// }
// }
return sql;
}
/**
* @param args
* @return
*/
public static int[] getSqlTypeFromArgs(Object[] args) {
int types[] = new int[args.length];
for (int i = 0; i < args.length; i++) {
if (args[i] == null) {
types[i] = Types.VARCHAR;
} else if (args[i] instanceof java.sql.Timestamp) {
types[i] = Types.TIMESTAMP;
} else if (args[i] instanceof java.sql.Date) {
types[i] = Types.DATE;
} else if (args[i] instanceof java.sql.Time) {
types[i] = Types.TIME;
} else if (args[i] instanceof java.math.BigDecimal) {
types[i] = Types.DECIMAL;
} else if (args[i] instanceof Integer) {
types[i] = Types.INTEGER;
} else if (args[i] instanceof Long) {
types[i] = Types.BIGINT;
} else if (args[i] instanceof Short) {
types[i] = Types.SMALLINT;
} else if (args[i] instanceof Float) {
types[i] = Types.FLOAT;
} else if (args[i] instanceof Double) {
types[i] = Types.DOUBLE;
} else if (args[i] instanceof byte[]) {
types[i] = Types.BLOB;
} else {
types[i] = Types.VARCHAR;
}
}
return types;
}
/**
* 功能:
*
* @param strsql
* @param rowMapper
* @param startIndex 开始位置(第一条是1,第二条是2...)
* @param size
* @return
*/
public List query(String strsql, RowMapper rowMapper, int startIndex, int size) {
return query(strsql, rowMapper, startIndex, size, false);
}
/**
* 功能:
*
* @param strsql
* @param rowMapper
* @param startIndex 开始位置(第一条是1,第二条是2...)
* @param size
* @param absoluteByNext circle ResultSet.next() instead of ResultSet.absolute(), because of JDBC driver not support so, such as DB2's CLOB
* @return
*/
public List query(String strsql, RowMapper rowMapper, int startIndex, int size, boolean absoluteByNext) {
if (RmBaseConfig.getSingleton().getDatabaseProductName() != null &&
(getAbsolutePage().equals(EnumBoolean.FALSE) || (getAbsolutePage().equals(EnumBoolean.NULL) && !RmBaseConfig.getSingleton().isAbsolutePage()))) {
if (ICoreConstants.DatabaseProductType.ORACLE.getDatabaseProductName().equalsIgnoreCase(RmBaseConfig.getSingleton().getDatabaseProductName())) {
return (List) query(RmSqlHelper.getSqlPage4Oracle(strsql, startIndex, size), rowMapper);
} else if (ICoreConstants.DatabaseProductType.MYSQL.getDatabaseProductName().equalsIgnoreCase(RmBaseConfig.getSingleton().getDatabaseProductName())) {
return (List) query(RmSqlHelper.getSqlPage4Mysql(strsql, startIndex, size), rowMapper);
}
}
return (List) query(strsql, new RmRowMapperResultSetExtractor(rowMapper, startIndex, size, absoluteByNext));
}
private static class RmRowMapperResultSetExtractor implements ResultSetExtractor {
private final RowMapper rowMapper;
private final int startIndex;
private final int size;
private boolean absoluteByNext;
/**
* Create a new RowMapperResultSetExtractor.
* @param rowMapper the RowMapper which creates an object for each row
* @param startIndex
* @param size
* @param absolute
*/
public RmRowMapperResultSetExtractor(RowMapper rowMapper, int startIndex, int size, boolean absoluteByNext) {
this.rowMapper = rowMapper;
this.startIndex = startIndex;
this.size = size;
this.absoluteByNext = absoluteByNext;
}
public Object extractData(ResultSet rs) throws SQLException {
List<Object> results = new ArrayList<Object>();
int rowProcessed = 0;
if(absoluteByNext) {
if(startIndex > 0) {
int pos = 1;
while(pos < startIndex) {
rs.next();
pos ++;
}
}
} else {
if(startIndex > 0) {
//moves to the given row number with respect to the beginning of the result set
if(rs.getRow() != (startIndex - 1)) {
rs.absolute(startIndex - 1);
}
} else if(startIndex < 0) {
//moves to an absolute row position with respect to the end of the result set
rs.absolute(startIndex - 1);
}
}
while (rs.next() && rowProcessed < size) {
results.add(this.rowMapper.mapRow(rs, rs.getRow()));
rowProcessed++;
}
return results;
}
}
public interface CircleVoArray {
public Object[] getArgs(Object obj);
}
}