package com.github.walker.easydb.dao; import java.sql.BatchUpdateException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.Vector; import com.github.walker.easydb.assistant.EasyConfig; import com.github.walker.easydb.assistant.LogFactory; import com.github.walker.easydb.connection.ConnectionPool; import com.github.walker.easydb.criterion.Criteria; import com.github.walker.easydb.criterion.Exp; import com.github.walker.easydb.dao.mysql.MysqlDAO; import com.github.walker.easydb.dao.oracle.OracleDAO; import com.github.walker.easydb.dao.sqlserver.SqlserverDAO; import org.apache.log4j.Logger; import com.github.walker.easydb.exception.BaseException; import com.github.walker.easydb.exception.CriteriaException; import com.github.walker.easydb.exception.DataAccessException; import com.github.walker.easydb.exception.FileAccessException; import com.github.walker.easydb.exception.IllegalEntityException; import com.github.walker.easydb.exception.IllegalParamException; /** * EasyDB的功能接口 * * @author HuQingmiao */ public abstract class EasyDao { protected Logger log = LogFactory.getLogger(this.getClass()); protected ConnectionPool connPool = null; protected Connection conn = null; private int transCnt = 0; // transaction count that already launched. protected EasyDao() { this.connPool = ConnectionPool.getInstance(); } protected EasyDao(String jndiKey) { this.connPool = ConnectionPool.getInstance(jndiKey); } /** * 获取默认的JNDI键名对应的EasyDao对象. * * @return 返回EasyDao对象, 该对象将采用默认的JNDI键值, 即'jndiName'及其对应值获取数据库连接 */ public static EasyDao getInstance() { String jndiKey = "jndi.name"; String jndiDBTypeKey = jndiKey.substring(0, jndiKey.indexOf('.')) + ".DBType"; String dbType = EasyConfig.getProperty(jndiDBTypeKey); // 如果该jndiKey不存在, 则取自实现的连接配置 if (dbType == null || "".equals(dbType.trim())) { dbType = EasyConfig.getProperty("DBType"); } if ("mysql".equalsIgnoreCase(dbType)) { return new MysqlDAO(); } else if ("oracle".equalsIgnoreCase(dbType)) { return new OracleDAO(); } else if ("sqlserver".equalsIgnoreCase(dbType)) { return new SqlserverDAO(); } Logger log = LogFactory.getLogger(LogFactory.MODULE_EASYDB); log.error("Getting the 'DBType' from '" + EasyConfig.CONFIG_FILENAME + "'... FAILED! Please check it. "); return null; } /** * 获取指定JNDI键名对应的EasyDao对象 * * @param jndiKey 指定的配置文件中的某个JNDI键 * @return 返回EasyDao对象, 该对象将采用指定的JNDI键值获取数据库连接 */ public static EasyDao getInstance(String jndiKey) { String jndiDBTypeKey = jndiKey.substring(0, jndiKey.indexOf('.')) + ".DBType"; String dbType = EasyConfig.getProperty(jndiDBTypeKey); // 如果该jndiKey不存在, 则取自实现的连接配置 if (dbType == null || "".equals(dbType.trim())) { dbType = EasyConfig.getProperty("DBType"); } if ("mysql".equalsIgnoreCase(dbType)) { return new MysqlDAO(jndiKey); } else if ("oracle".equalsIgnoreCase(dbType)) { return new OracleDAO(jndiKey); } else if ("sqlserver".equalsIgnoreCase(dbType)) { return new SqlserverDAO(jndiKey); } Logger log = LogFactory.getLogger(LogFactory.MODULE_EASYDB); log.error("Getting the " + jndiDBTypeKey + " from 'easydb.properties'... FAILED! Please check it. "); return null; } /** * 获取数据库类型 * * @return 数据库类型, 如'mysql', 'oracle' */ public abstract String getDBType(); /** * 开启事务。 支持嵌套事务,需要与endTrans(boolean)成对使用。 * <p/> * Gets a database connection and begin a transaction. Only when transCount * is 0, will the transaction start. This method supports for nested * transaction with method endTrans(). * * @throws com.github.walker.easydb.exception.BaseException * @see EasyDao#endTrans(boolean) */ public void beginTrans() throws BaseException { try { if (transCnt == 0) { // if no transaction started conn = connPool.getConnection(); conn.setAutoCommit(false); } transCnt++; } catch (SQLException e) { log.error(e.getErrorCode(), e); throw new DataAccessException(this.getDBType(), e.getErrorCode(), e.getMessage()); } } /** * 结束事务。 支持嵌套事务,需要与beginTrans()成对使用。 * <p/> * Ends a database transaction and close the database connection. Only when * transCount is 1, will the transaction end. This method supports for * nested transaction with method beginTrans(). * * @param commit If true, commit the transaction, else rollback the * transaction. * @throws BaseException * @see EasyDao#beginTrans() */ public void endTrans(boolean commit) throws BaseException { try { // if no transaction started, calling this method is nonsensical if (transCnt == 0) { return; } if (transCnt == 1) { if (commit) { conn.commit(); } else { conn.rollback(); } connPool.freeConnection(conn); } transCnt--; } catch (SQLException e) { log.error(e.getErrorCode(), e); throw new DataAccessException(this.getDBType(), e.getErrorCode(), e.getMessage()); } } /** * 插入一条记录。 * <p/> * Persist the given entity instance, Inserts one data into database. * * @param entity BaseEntity object which need to be persisted. * @return the row count for INSERT * @throws BaseException */ public int save(BaseEntity entity) throws BaseException { PreparedStatement stmt = null; EntityParser parser = null; try { // parse the entity parser = new EntityParser(getDBType(), this.conn, entity); // build the sql SqlParamIndexer sqlParamIndex = getSqlConstructor().buildInsert(parser); log.info("SQL: " + sqlParamIndex.getSql()); // set the parameter value to the '?' mark stmt = this.conn.prepareStatement(sqlParamIndex.getSql()); this.fillParamToMark(stmt, sqlParamIndex.getIndexedFieldVec(), parser); // insert int rowCnt = stmt.executeUpdate(); stmt.close(); // 处理大数据类型的写入 if (rowCnt > 0) { this.ProcessBigDataType(parser); } return rowCnt; } catch (SQLException e) { log.error(e.getErrorCode(), e); throw new DataAccessException(this.getDBType(), e.getErrorCode(), e.getMessage()); } catch (DataAccessException e) { log.error("", e); throw e; } catch (IllegalEntityException e) { log.error("", e); throw e; } catch (IllegalParamException e) { log.error("", e); throw e; } catch (FileAccessException e) { log.error("", e); throw e; } finally { if (parser != null) { parser.myFinalize(); } this.freeResource(stmt); } } /** * 批量插入多条记录。 * <p/> * 注意: 1.参数Entity数组中列值的存放结构必须一致,即:数组中的某列要么都需要写入,要么都不需要写入! * 2.此方法不支持大字段列(e.g.BLOB/CLOB)的写入. * <p/> * Persist the given entity instances, Inserts multiple rows of data into * database. * * @param entityArray the array of BaseEntity object which need to be persisted. * @return an array of INSERT counts containing one element for each * BaseEntity object in the parameter array. The elements of the * array are ordered according to the order of the parameter array. * @throws BaseException */ public int[] save(BaseEntity[] entityArray) throws BaseException { if (entityArray.length == 0 || entityArray[0] == null) { return new int[]{}; } PreparedStatement stmt = null; EntityParser parser = null; try { // 解析第一个实体,从而得到共用的解析器 parser = new EntityParser(getDBType(), this.conn, entityArray[0]); // build the sql SqlParamIndexer sqlParamIndex = getSqlConstructor().buildInsert(parser); log.info("SQL: " + sqlParamIndex.getSql()); // set the parameter to the '?' mark stmt = this.conn.prepareStatement(sqlParamIndex.getSql()); // filled the '?' with paramter values Vector<String> indexedFieldVec = sqlParamIndex.getIndexedFieldVec(); this.fillParamToMark(stmt, indexedFieldVec, parser); stmt.addBatch(); // 采用共用的解析器,处理后面的实体 int size = entityArray.length; for (int i = 1; i < size; i++) { if (entityArray[i] == null) { continue; } EntityParser p = new EntityParser(parser, entityArray[i]); this.fillParamToMark(stmt, indexedFieldVec, p); stmt.addBatch(); } indexedFieldVec.clear(); int[] rowCnt = stmt.executeBatch(); // 由于JDBC在执行INSERT操作时,返回的插入条数是未知的, 因此还需要计算真正插入的条数 for (int i = 0; i < rowCnt.length; i++) { // 如果返回成功标识 或 插入条数>0 if (rowCnt[i] == Statement.SUCCESS_NO_INFO || rowCnt[i] > 0) { rowCnt[i] = 1; } } return rowCnt; } catch (SQLException e) { log.error(e.getErrorCode(), e); throw new DataAccessException(this.getDBType(), e.getErrorCode(), e.getMessage()); } catch (DataAccessException e) { log.error("", e); throw e; } catch (IllegalEntityException e) { log.error("", e); throw e; } catch (IllegalParamException e) { log.error("", e); throw e; } finally { if (parser != null) { parser.myFinalize(); } this.freeResource(stmt); } } /** * 以参数entity中的主键值为条件, 更新一条记录. <br> * Updates one record, the updating criteria is the primary key contained in * the entity. * * @param entity BaseEntity object which data will override the old data in the * database. * @return the row count for UPDATE * @throws BaseException */ public int update(BaseEntity entity) throws BaseException { PreparedStatement stmt = null; EntityParser parser = null; try { parser = new EntityParser(getDBType(), this.conn, entity); // build the sql SqlParamIndexer sqlParamIndex = getSqlConstructor().buildUpdateByPk(parser); log.info("SQL: " + sqlParamIndex.getSql()); // set the parameter to the '?' mark stmt = this.conn.prepareStatement(sqlParamIndex.getSql()); this.fillParamToMark(stmt, sqlParamIndex.getIndexedFieldVec(), parser); // update int rowCnt = stmt.executeUpdate(); // 处理大数据类型的写入 if (rowCnt > 0) { this.ProcessBigDataType(parser); } return rowCnt; } catch (SQLException e) { log.error(e.getErrorCode(), e); throw new DataAccessException(this.getDBType(), e.getErrorCode(), e.getMessage()); } catch (DataAccessException e) { log.error("", e); throw e; } catch (IllegalEntityException e) { log.error("", e); throw e; } catch (FileAccessException e) { log.error("", e); throw e; } finally { if (parser != null) { parser.myFinalize(); } this.freeResource(stmt); } } /** * 以参数entityArray中各元素对应实体的主键值为条件, 批量更新多条记录. * 注意: * 1.参数Entity数组中列值的存放结构必须一致,即:数组中的某列要么都需要更新,要么都不需要更新! * 2.此方法不支持大字段列(e.g.BLOB/CLOB)的写入. <br> * Updates records by batch, the updating criteria is the primary key * contained in the elements of entity array. * * @param entityArray the array of BaseEntity object which data will override the * old data in the database. * @return an array of UPDATE counts containing one element for each * BaseEntity object in the parameter array. The elements of the * array are ordered according to the order of the parameter array. * @throws BaseException */ public int[] update(BaseEntity[] entityArray) throws BaseException { // //参数不能为空, 且长度不能为0 // if(entityArray==null||entityArray.length==0){ // throw new // IllegalParamException(IllegalParamException.PARAM_CANNOT_NULL); // } if (entityArray.length == 0 || entityArray[0] == null) { return new int[]{}; } PreparedStatement stmt = null; EntityParser parser = null; try { // 解析第一个实体,从而得到共用的解析器 parser = new EntityParser(getDBType(), this.conn, entityArray[0]); // build the sql SqlParamIndexer sqlParamIndex = getSqlConstructor().buildUpdateByPk(parser); log.info("SQL: " + sqlParamIndex.getSql()); // set the parameter to the '?' mark stmt = this.conn.prepareStatement(sqlParamIndex.getSql()); // filled the '?' with paramter values Vector<String> indexedFieldVec = sqlParamIndex.getIndexedFieldVec(); this.fillParamToMark(stmt, indexedFieldVec, parser); stmt.addBatch(); // 采用共用的解析器,处理后面的实体 int size = entityArray.length; for (int i = 1; i < size; i++) { if (entityArray[i] == null) { continue; } EntityParser p = new EntityParser(parser, entityArray[i]); this.fillParamToMark(stmt, indexedFieldVec, p); stmt.addBatch(); } indexedFieldVec.clear(); int[] rowCnt = stmt.executeBatch(); // 由于JDBC在执行UPDATE操作时,返回的插入条数是未知的, 因此还需要计算真正插入的条数 for (int i = 0; i < rowCnt.length; i++) { // 如果返回成功标识 或 插入条数>0 if (rowCnt[i] == Statement.SUCCESS_NO_INFO || rowCnt[i] > 0) { rowCnt[i] = 1; } } return rowCnt; } catch (SQLException e) { log.error(e.getErrorCode(), e); throw new DataAccessException(this.getDBType(), e.getErrorCode(), e.getMessage()); } catch (DataAccessException e) { log.error("", e); throw e; } catch (IllegalEntityException e) { log.error("", e); throw e; } catch (IllegalParamException e) { log.error("", e); throw e; } finally { if (parser != null) { parser.myFinalize(); } this.freeResource(stmt); } } /** * 指定criteria为条件或更新范围,以参数entity中的数据项更新相应表的记录。 * 注意:如果参数entity装载有文件类型的数据,或者说需要向某个表的大字段列(e.g.BLOB/CLOB)写入数据, * 则参数criteria必须含有主键值以唯一标识需要更新的那条记录。 <br> * Updates some records, Using the property values in the entity to override * correponding columns of records. <br> * <p/> * This method support updating for BLOB/CLOB column, but when you call this * method and need writing data to BLOB/CLOB column,the criteria must * contain the primary key value. * * @param entity BaseEntity object which data will override the old data in the * database. * @param criteria Criteria object which is the criteria of updating. * @return the row count for UPDATE * @throws BaseException */ public int update(BaseEntity entity, Criteria criteria) throws BaseException { PreparedStatement stmt = null; EntityParser parser = null; try { parser = new EntityParser(getDBType(), this.conn, entity); // build the sql SqlParamIndexer sqlParamIndex = getSqlConstructor().buildUpdateByCriteria(parser, criteria); log.info("SQL: " + sqlParamIndex.getSql()); // set the parameter to the '?' mark stmt = this.conn.prepareStatement(sqlParamIndex.getSql()); this.fillParamToMark(stmt, sqlParamIndex.getIndexedFieldVec(), parser); // update int rowCnt = stmt.executeUpdate(); // 处理大数据类型的写入 if (rowCnt > 0) { this.ProcessBigDataType(parser, criteria); } return rowCnt; } catch (SQLException e) { log.error(e.getErrorCode(), e); throw new DataAccessException(this.getDBType(), e.getErrorCode(), e.getMessage()); } catch (DataAccessException e) { log.error("", e); throw e; } catch (IllegalEntityException e) { log.error("", e); throw e; } catch (FileAccessException e) { log.error("", e); throw e; } finally { if (parser != null) { parser.myFinalize(); } this.freeResource(stmt); } } /** * 以参数entity中的数据项更新相应表的所有记录。 <br> * Updates all records, Using the property values in the entity to override * correponding columns of all records. * * @param entity BaseEntity object which data will override the old data in the * database. * @return the row count for UPDATE * @throws BaseException */ public int updateAll(BaseEntity entity) throws BaseException { return this.update(entity, Exp.eq("1", 1)); } /** * 删除一条记录, 其删除依据是参数entity中的主键值. * <p/> * Deletes one record, the deleteing criteria is the primary key contained * in the entity. * * @param entity BaseEntity object which data will be deleted from database. * @return the row count for DELETE * @throws BaseException */ public int delete(BaseEntity entity) throws BaseException { PreparedStatement stmt = null; EntityParser parser = null; try { parser = new EntityParser(getDBType(), this.conn, entity); // build the sql SqlParamIndexer sqlParamIndex = getSqlConstructor().buildDeleteByPk(parser); log.info("SQL: " + sqlParamIndex.getSql()); // set the parameter to the '?' mark stmt = this.conn.prepareStatement(sqlParamIndex.getSql()); this.fillParamToMark(stmt, sqlParamIndex.getIndexedFieldVec(), parser); // delete int rowCnt = stmt.executeUpdate(); return rowCnt; } catch (SQLException e) { log.error(e.getErrorCode(), e); throw new DataAccessException(this.getDBType(), e.getErrorCode(), e.getMessage()); } catch (DataAccessException e) { log.error("", e); throw e; } catch (IllegalEntityException e) { log.error("", e); throw e; } catch (FileAccessException e) { log.error("", e); throw e; } finally { if (parser != null) { parser.myFinalize(); } this.freeResource(stmt); } } /** * 批量删除多条记录, 其删除依据是参数entityArray中各元素对应实体的主键值. * <p/> * Deletes records by batch, the deleteing criteria is the primary key * contained in the elements of entity array. * * @param entityArray the array of BaseEntity which need to be delete. * @return an array of DELETE counts containing one element for each * BaseEntity object in the parameter array. The elements of the * array are ordered according to the order of the parameter array. * @throws BaseException */ public int[] delete(BaseEntity[] entityArray) throws BaseException { // //参数不能为空, 且长度不能为0 // if(entityArray==null||entityArray.length==0){ // throw new // IllegalParamException(IllegalParamException.PARAM_CANNOT_NULL); // } if (entityArray.length == 0 || entityArray[0] == null) { return new int[]{}; } PreparedStatement stmt = null; EntityParser parser = null; try { // 解析第一个实体,从而得到共用的解析器 parser = new EntityParser(getDBType(), this.conn, entityArray[0]); // build the sql SqlParamIndexer sqlParamIndex = getSqlConstructor().buildDeleteByPk(parser); log.info("SQL: " + sqlParamIndex.getSql()); // set the parameter to the '?' mark stmt = this.conn.prepareStatement(sqlParamIndex.getSql()); // filled the '?' with paramter values Vector<String> indexedFieldVec = sqlParamIndex.getIndexedFieldVec(); this.fillParamToMark(stmt, indexedFieldVec, parser); stmt.addBatch(); // 采用共用的解析器,处理后面的实体 int size = entityArray.length; for (int i = 1; i < size; i++) { if (entityArray[i] == null) { continue; } EntityParser p = new EntityParser(parser, entityArray[i]); this.fillParamToMark(stmt, indexedFieldVec, p); stmt.addBatch(); } indexedFieldVec.clear(); int[] rowCnt = stmt.executeBatch(); // 由于JDBC在执行DELETE操作时,返回的删除条数是未知的, 因此还需要计算真正删除的条数 for (int i = 0; i < rowCnt.length; i++) { // 如果返回成功标识 或 删除条数>0 if (rowCnt[i] == Statement.SUCCESS_NO_INFO || rowCnt[i] > 0) { rowCnt[i] = 1; } } return rowCnt; } catch (SQLException e) { log.error(e.getErrorCode(), e); throw new DataAccessException(this.getDBType(), e.getErrorCode(), e.getMessage()); } catch (DataAccessException e) { log.error("", e); throw e; } catch (IllegalEntityException e) { log.error("", e); throw e; } catch (IllegalParamException e) { log.error("", e); throw e; } finally { if (parser != null) { parser.myFinalize(); } this.freeResource(stmt); } } /** * 指定criteria为删除条件,删除entityClass所表示的某个表的部分记录. * <p/> * <br> * Deletes some records from the table specified by the 'entityClass'. * * @param entityClass Class object which represented the table, such as * 'Book.class'. * @param criteria the criteria of delete. * @return the count of rows affected by the delete operation. * @throws BaseException */ public int delete(Class<?> entityClass, Criteria criteria) throws BaseException { PreparedStatement stmt = null; try { // build the sql String sql = getSqlConstructor().buildDeleteByCriteria(entityClass, criteria); log.info("SQL: " + sql); // fill the question mark with data stmt = this.conn.prepareStatement(sql); return stmt.executeUpdate(); } catch (SQLException e) { log.error(e.getErrorCode(), e); throw new DataAccessException(this.getDBType(), e.getErrorCode(), e.getMessage()); } finally { this.freeResource(stmt); } } /** * 删除entityClass所表示的某个表的所有记录. <br> * Deletes all records from the table specified by the 'entityClass'. * * @param entityClass Class object which represented the table, such as * 'Book.class'. * @return the count of rows affected by the delete operation. * @throws BaseException */ public int deleteAll(Class<?> entityClass) throws BaseException { return this.delete(entityClass, Exp.eq("1", 1)); } /** * 执行给定的SQL语句. * <p/> * 注意:此方法不支持大字段列(e.g.BLOB/CLOB)的写入. * <p/> * Executes the given sql. Notice, this method doesn't support writing * BLOB/CLOB column. * * @param eSql the executed sql, such as: "INSERT INTO * TABLEX(COLUMN1,COLUMN2) VALUES(1,'aa') * @return the row count for INSERT/UPDATE/DELETE * @throws BaseException */ public int exec(String eSql) throws BaseException { eSql = eSql.replace(' ', ' '); PreparedStatement stmt = null; try { log.info("SQL: " + eSql); stmt = this.conn.prepareStatement(eSql); return stmt.executeUpdate(); } catch (SQLException e) { log.error("", e); throw new DataAccessException(e.getMessage()); } finally { this.freeResource(stmt); } } /** * 执行给定的存储过程调用语句SQL语句. * * Executes the given procedure calling sql. * * @param pSql * the procedure calling sql, such as:"{?=CALL STAT_USERS(?,?)}"; * * @param paramMap * SqlParamMap object, which loads the prameter values for the * sql. * * @return the SqlParamMap object, which loads the return values after this * call. * * @throws BaseException * */ /* * public void callProcedure(String pSql, SqlParamMap paramMap) throws * BaseException { * * CallableStatement stmt = null; try { log.info("SQL: " + pSql); stmt = * this.conn.prepareCall(pSql); * * int index = pSql.indexOf("="); * * int retValueIdx = 0; * * if (index > 0) { * * //对'='前的的各个?, 注册返回值类型 for (int i = 0; i <= index; i++) { if * (pSql.charAt(i) == '?') { retValueIdx++; UpdateIdentifier obj = * (UpdateIdentifier) paramMap .get(retValueIdx); * * if (obj instanceof EString) { stmt.registerOutParameter(retValueIdx, * Types.VARCHAR); } if (obj instanceof EInteger || obj instanceof ELong) { * stmt.registerOutParameter(retValueIdx, Types.NUMERIC); } else if (obj * instanceof EDouble || obj instanceof EFloat) { * stmt.registerOutParameter(retValueIdx, java.sql.Types.NUMERIC); * * } else if (obj instanceof EDouble || obj instanceof EFloat) { * stmt.registerOutParameter(retValueIdx, java.sql.Types.DOUBLE); * * } else if (obj instanceof ETimestamp) { * stmt.registerOutParameter(retValueIdx, java.sql.Types.TIMESTAMP); } } } * }// end if(index>0) * * //存储过程的参数 this.fillParamToMark(stmt, paramMap); stmt.executeUpdate(); * * //设置返回值 if (index > 0) { * * for (int i = 1; i <= retValueIdx; i++) { UpdateIdentifier obj = * (UpdateIdentifier) paramMap .get(i); * * if (obj instanceof EString) { paramMap.put(i, new * EString(stmt.getString(i))); } if (obj instanceof EInteger) { * paramMap.put(i, new EInteger(stmt.getInt(i))); } if (obj instanceof * ELong) { paramMap.put(i, new ELong(stmt.getLong(i))); * * } else if (obj instanceof EDouble) { paramMap.put(i, new * EDouble(stmt.getDouble(i))); * * } else if (obj instanceof EFloat) { paramMap.put(i, new * EFloat(stmt.getFloat(i))); * * } else if (obj instanceof ETimestamp) { paramMap.put(i, new * ETimestamp(stmt.getTimestamp(i))); } } } * * } catch (SQLException e) { log.error("", e); throw new * DataAccessException(this.getDBType(),e); } finally { * this.freeResource(stmt); } } */ /** * 执行给定的SQL语句. * <p/> * 注意:此方法不支持大字段列(e.g.BLOB/CLOB)的写入. * <p/> * Executes the given sql. Notice, this method doesn't support writing * BLOB/CLOB column. * * @param pSql the parameterize sql, such as: "INSERT INTO * TABLEX(COLUMN1,COLUMN2) VALUES(?,?) * @param paramMap SqlParamMap object, which loads the prameter values for the * sql. * @return the row count for INSERT/UPDATE/DELETE * @throws BaseException */ public int exec(String pSql, SqlParamMap paramMap) throws BaseException { if (pSql == null) { pSql = ""; } pSql = pSql.replace(' ', ' '); PreparedStatement stmt = null; try { log.info("SQL: " + pSql); stmt = this.conn.prepareStatement(pSql); // set the parameter to the '?' mark this.fillParamToMark(stmt, paramMap); return stmt.executeUpdate(); } catch (SQLException e) { log.error(e.getErrorCode(), e); throw new DataAccessException(this.getDBType(), e.getErrorCode(), e.getMessage()); } catch (DataAccessException e) { log.error("", e); throw e; } catch (IllegalParamException e) { log.error("", e); throw e; } finally { this.freeResource(stmt); } } /** * 执行给定的SQL。 注意:该方法不支持对大字段列(e.g. BLOB/CLOB)的写入。 * <p/> * Executes the given sql. Notice, this method doesn't support writing * BLOB/CLOB column. * * @param pSql parameterized sql, such as: "INSERT INTO * TABLEX(COLUMN1,COLUMN2) VALUES(?,?) * @param paramMapArray the array of SqlParamMap object. * @return an array of INSERT/UPDATE/DELETE counts containing one element * for each SqlParamMap object in the parameter array. The elements * of the array are ordered according to the order of the parameter * array. * @throws BaseException */ public int[] exec(String pSql, SqlParamMap[] paramMapArray) throws BaseException { if (pSql == null) { pSql = ""; } pSql = pSql.replace(' ', ' '); // 参数长度不能为0 if (paramMapArray.length == 0) { throw new IllegalParamException(IllegalParamException.PARAM_CANNOT_NULL, ""); } PreparedStatement stmt = null; try { log.info("SQL: " + pSql); stmt = this.conn.prepareStatement(pSql); // set the parameter to the '?' mark for (int i = 0; i < paramMapArray.length; i++) { // 参数组中某组为空, 则过滤之 if (paramMapArray[i] == null) { continue; } this.fillParamToMark(stmt, paramMapArray[i]); stmt.addBatch(); } int[] rowCnt = stmt.executeBatch(); // 由于JDBC在执行批量INSERT/UPDATE操作时,返回的插入条数是未知的, 因此还需要计算真正发生影响的记录条数 for (int i = 0; i < rowCnt.length; i++) { // 如果返回成功标识 或 插入条数>0 if (rowCnt[i] == Statement.SUCCESS_NO_INFO || rowCnt[i] > 0) { rowCnt[i] = 1; } } return rowCnt; } catch (BatchUpdateException e) { log.error(e.getErrorCode(), e); throw new DataAccessException(this.getDBType(), e.getErrorCode(), e.getMessage()); } catch (SQLException e) { log.error(e.getErrorCode(), e); throw new DataAccessException(this.getDBType(), e.getErrorCode(), e.getMessage()); } catch (DataAccessException e) { log.error("", e); throw e; } catch (IllegalParamException e) { log.error("", e); throw e; } finally { this.freeResource(stmt); } } /** * 根据参数entity中的主键值, 装载相应实体, 即加载该实体的其它属性. * <p/> * Loads one data to the entity by the primary key that contained in the * entity. * * @param entity BaseEntity object which contains the values of primary key. * @return true, only when exists the record which matching the primary key; * otherwise fase; * @throws BaseException */ public boolean loadByPK(BaseEntity entity) throws BaseException { PreparedStatement stmt = null; EntityParser parser = null; ResultSet rs = null; try { parser = new EntityParser(getDBType(), this.conn, entity); // build the sql SqlParamIndexer sqlParamIndex = getSqlConstructor().buildSelectByPk(parser); log.info("SQL: " + sqlParamIndex.getSql()); // set the parameter to the '?' mark stmt = this.conn.prepareStatement(sqlParamIndex.getSql()); this.fillParamToMark(stmt, sqlParamIndex.getIndexedFieldVec(), parser); rs = stmt.executeQuery(); // parse the rs and transform it into PageList ResultAssembler assembler = this.getResultAssembler(rs, entity.getClass()); return assembler.loadEntity(entity); } catch (SQLException e) { log.error(e.getErrorCode(), e); throw new DataAccessException(this.getDBType(), e.getErrorCode(), e.getMessage()); } catch (DataAccessException e) { log.error("", e); throw e; } catch (IllegalEntityException e) { log.error("", e); throw e; } catch (FileAccessException e) { log.error("", e); throw e; } finally { if (parser != null) { parser.myFinalize(); } this.freeResource(stmt, rs); } } /** * 指定criteria作为查询条件,从entityClass所表示的某个表或无名视图中取一条记录. 如果检索到符合条件的记录数大于1,则抛出异常. * <p/> * Gets one data from the talbe(or view) specified by the 'entityClass'. * * @param entityClass Class object which represented the table, such as * 'Book.class'. * @param criteria the criteria of query. * @throws BaseException */ public BaseEntity getOne(Class<?> entityClass, Criteria criteria) throws BaseException { // 在方法getOne(Class, Criteria)中, Criteria对象不能为空! if (criteria == null || "".equals(criteria.toString().trim())) { throw new CriteriaException(CriteriaException.CRITERIA_CANNOT_EMPTY, ""); } PreparedStatement stmt = null; ResultSet rs = null; try { SqlConstructor sqlConstructor = this.getSqlConstructor(); // build the sql String sql = sqlConstructor.buildSelectSql(entityClass, criteria); // 分页查询, 取一条记录 String pagerSql = sqlConstructor.buildPageSql(sql, 1, 2); if (pagerSql == null) { String jndiKey = "jndi.name"; String jndiDBTypeKey = jndiKey.substring(0, jndiKey.indexOf('.')) + ".DBType"; String dbType = EasyConfig.getProperty(jndiDBTypeKey); // 如果该jndiKey不存在, 则取自实现的连接配置 if (dbType == null || "".equals(dbType.trim())) { dbType = EasyConfig.getProperty("DBType"); } if ("sqlserver".equalsIgnoreCase(dbType)) { throw new DataAccessException("EasyDB对Sqlserver不支持分页查询!"); } } log.info("SQL: " + pagerSql); stmt = this.conn.prepareStatement(pagerSql); rs = stmt.executeQuery(); // parse the rs and transform it into PageList ResultAssembler assembler = this.getResultAssembler(rs, entityClass); assembler.buildEntityList(); ArrayList<BaseEntity> rsList = assembler.getRsList(); // 没有符合条件的记录 if (rsList.size() == 0) { return null; } return (BaseEntity) rsList.get(0); } catch (SQLException e) { log.error(e.getErrorCode(), e); throw new DataAccessException(this.getDBType(), e.getErrorCode(), e.getMessage()); } catch (DataAccessException e) { log.error("", e); throw e; } catch (IllegalEntityException e) { log.error("", e); throw e; } catch (FileAccessException e) { log.error("", e); throw e; } finally { this.freeResource(stmt, rs); } } /** * 执行给定的查询语句,从参数entityClas所表示的表或视图(包括无名视图)中取一条或多条记录. <br> * <p/> * Gets some datas from the talbe(or view) specified by the 'entityClass'. * * @param eSql executable sql, such as: "SELECT A.NAME,A.TYPE FROM TABLEXX * WHERE A.NAME = 'JONE' " * @param entityClass the Class object of BaseEntity which loads the query results. * @throws BaseException */ @SuppressWarnings("rawtypes") public ArrayList get(String eSql, Class<?> entityClass) throws BaseException { if (eSql == null) { eSql = ""; } eSql = eSql.replace(' ', ' '); // 校验SQL是否安全 if (!validateQuerySql(eSql)) { throw new IllegalParamException(IllegalParamException.ONLY_FOR_SELECT, ""); } PreparedStatement stmt = null; ResultSet rs = null; try { log.info("SQL: " + eSql); stmt = this.conn.prepareStatement(eSql); rs = stmt.executeQuery(); // parse the rs and transform it into PageList ResultAssembler assembler = this.getResultAssembler(rs, entityClass); assembler.buildEntityList(); // 对于非分页查询, 将所有满足业务条件的记录总数设为实际查询出的记录条数 PageList rsList = (PageList) assembler.getRsList(); rsList.setTotalRecordCount(rsList.size()); return rsList; } catch (SQLException e) { log.error(e.getErrorCode(), e); throw new DataAccessException(this.getDBType(), e.getErrorCode(), e.getMessage()); } catch (DataAccessException e) { log.error("", e); throw e; } catch (IllegalEntityException e) { log.error("", e); throw e; } catch (FileAccessException e) { log.error("", e); throw e; } finally { this.freeResource(stmt, rs); } } /** * 根据给定的参数化SQL及相关参数值,从参数entityClas所表示的表或视图(包括无名视图)中取一条或多条记录. <br> * Gets some datas from the talbe(or view) specified by the 'entityClass'. * * @param pSql parameterized sql, such as: "SELECT A.NAME,A.TYPE FROM TABLEX * * WHERE A.NAME = ? " * @param paramMap the SqlParamMap object, which associates the specified value * with the specified '?' in the parameterized sql. * @param entityClass the Class object of BaseEntity which loads the query results. * @throws BaseException */ @SuppressWarnings("rawtypes") public ArrayList get(String pSql, SqlParamMap paramMap, Class<?> entityClass) throws BaseException { if (pSql == null) { pSql = ""; } pSql = pSql.replace(' ', ' '); // 校验SQL是否安全 if (!validateQuerySql(pSql)) { throw new IllegalParamException(IllegalParamException.ONLY_FOR_SELECT, ""); } PreparedStatement stmt = null; ResultSet rs = null; try { log.info("SQL: " + pSql); stmt = this.conn.prepareStatement(pSql); // set the parameter to the '?' mark this.fillParamToMark(stmt, paramMap); rs = stmt.executeQuery(); // parse the rs and transform it into PageList ResultAssembler assembler = this.getResultAssembler(rs, entityClass); assembler.buildEntityList(); // 对于非分页查询, 将所有满足业务条件的记录总数设为实际查询出的记录条数 PageList rsList = (PageList) assembler.getRsList(); rsList.setTotalRecordCount(rsList.size()); return rsList; } catch (SQLException e) { log.error(e.getErrorCode(), e); throw new DataAccessException(this.getDBType(), e.getErrorCode(), e.getMessage()); } catch (DataAccessException e) { log.error("", e); throw e; } catch (IllegalEntityException e) { log.error("", e); throw e; } catch (IllegalParamException e) { log.error("", e); throw e; } catch (FileAccessException e) { log.error("", e); throw e; } finally { this.freeResource(stmt, rs); } } /** * 本方法将给定的SQL包装成分页查询SQL, 再通过分页查询SQL取得某段范围(大于等于startPos, 且小于endPos)内的一条或多条记录. * <p/> * 注意:不允许在SQL的WHERE子句中出现某列与子查询返回结果相比较的语句, 例如下面两种类型的SQL是禁用的: <br> * 1.SELECT A.COL_1, A.COL_2 FROM A WHERE A.COL_1 = (SELECT B.COL FROM B ) <br> * 2.SELECT A.COL_1, A.COL_2 FROM A WHERE A.COL_1 IN (SELECT B.COL FROM B ). <br> * <p/> * 请将上面两种被禁用的SQL改成如下形式, 即用EXISTS语句代替子条件语句: 1.SELECT A.COL_1, A.COL_2 FROM A * WHERE EXISTS (SELECT 1 FROM B WHERE A.COL_1 =B.COL FROM B ) * <p/> * <br> * Decrates the given sql to pager sql, according the pager sql, gets * pesistent data which position is between startPos and endPos (greater * than or equals startPos, and less than endPos). * * @param eSql executable sql, such as: "SELECT A.NAME,A.TYPE FROM TABLEXX * WHERE A.NAME = 'JONE' " * @param entityClass the Class object of BaseEntity which loads the query results. * @param startPos starting position in searching records, it begins from 1 * @param endPos end position in searching records, it begins from 1 * @return PageList object, which contains pesistent data which position is * between startPos and endPos (greater than or equals startPos, and * less than endPos). * @throws BaseException */ public PageList get(String eSql, Class<?> entityClass, int startPos, int endPos) throws BaseException { if (eSql == null) { eSql = ""; } eSql = eSql.replace(' ', ' '); // 校验SQL是否安全 if (!validateQuerySql(eSql)) { throw new IllegalParamException(IllegalParamException.ONLY_FOR_SELECT, ""); } PreparedStatement stmt = null; ResultSet rs = null; try { // 取SQL构造器 SqlConstructor sqlConstructor = this.getSqlConstructor(); // 分页查询主SQL String pagerSql = sqlConstructor.buildPageSql(eSql, startPos, endPos); if (pagerSql == null) { String jndiKey = "jndi.name"; String jndiDBTypeKey = jndiKey.substring(0, jndiKey.indexOf('.')) + ".DBType"; String dbType = EasyConfig.getProperty(jndiDBTypeKey); // 如果该jndiKey不存在, 则取自实现的连接配置 if (dbType == null || "".equals(dbType.trim())) { dbType = EasyConfig.getProperty("DBType"); } if ("sqlserver".equalsIgnoreCase(dbType)) { throw new DataAccessException("EasyDB对Sqlserver不支持分页查询!"); } } log.info("SQL: " + pagerSql); // 查询位于startPos 至 endPos 之间的记录 stmt = this.conn.prepareStatement(pagerSql); rs = stmt.executeQuery(); // parse the rs and transform it into PageList ResultAssembler assembler = this.getResultAssembler(rs, entityClass); assembler.buildEntityList(); PageList pageList = (PageList) assembler.getRsList(); rs.close(); stmt.close(); // 取分页查询所能获取的总记录数的SQL String countSql = sqlConstructor.buildCountSql(eSql); log.info("SQL: " + countSql); // 查询、设置所有满足业务条件的记录总数 stmt = this.conn.prepareStatement(countSql); rs = stmt.executeQuery(); rs.next(); pageList.setTotalRecordCount(rs.getInt(1)); return pageList; } catch (SQLException e) { log.error(e.getErrorCode(), e); throw new DataAccessException(this.getDBType(), e.getErrorCode(), e.getMessage()); } catch (DataAccessException e) { log.error("", e); throw e; } catch (IllegalEntityException e) { log.error("", e); throw e; } catch (FileAccessException e) { log.error("", e); throw e; } finally { this.freeResource(stmt, rs); } } /** * 本方法将给定的SQL装饰成分页查询SQL, 再通过分页查询SQL及参数值取得某段范围(大于等于startPos, * 且小于endPos)内的一条或多条记录. * <p/> * 注意:不允许在SQL的WHERE子句中出现某列与子查询返回结果相比较的语句, 例如下面两种类型的SQL是禁用的: <br> * 1.SELECT A.COL_1, A.COL_2 FROM A WHERE A.COL_1 = (SELECT B.COL FROM B ) <br> * 2.SELECT A.COL_1, A.COL_2 FROM A WHERE A.COL_1 IN (SELECT B.COL FROM B ). <br> * <p/> * 请将上面两种被禁用的SQL改成如下形式, 即用EXISTS语句代替子条件语句: 1.SELECT A.COL_1, A.COL_2 FROM A * WHERE EXISTS (SELECT 1 FROM B WHERE A.COL_1 =B.COL FROM B )<br> * <p/> * <p/> * <br> * Decrates the given sql to pager sql, according the pager sql, gets * pesistent data which position is between startPos and endPos (greater * than or equals startPos, and less than endPos). * * @param pSql executable sql, such as: "SELECT A.NAME,A.TYPE FROM TABLEXX * WHERE A.NAME = ? " * @param entityClass the Class object of BaseEntity which loads the query results. * @param paramMap SqlParamMap object, which loads the prameter values for the * sql. * @param startPos starting position in searching records, it begins from 1 * @param endPos end position in searching records, it begins from 1 * @return PageList object, which contains pesistent data which position is * between startPos and endPos (greater than or equals startPos, and * less than endPos). * @throws BaseException */ public PageList get(String pSql, SqlParamMap paramMap, Class<?> entityClass, int startPos, int endPos) throws BaseException { if (pSql == null) { pSql = ""; } pSql = pSql.replace(' ', ' '); // 校验SQL是否安全 if (!validateQuerySql(pSql)) { throw new IllegalParamException(IllegalParamException.ONLY_FOR_SELECT, ""); } PreparedStatement stmt = null; ResultSet rs = null; try { // 取SQL构造器 SqlConstructor sqlConstructor = this.getSqlConstructor(); // 分页查询主SQL String pagerSql = sqlConstructor.buildPageSql(pSql, startPos, endPos); if (pagerSql == null) { String jndiKey = "jndi.name"; String jndiDBTypeKey = jndiKey.substring(0, jndiKey.indexOf('.')) + ".DBType"; String dbType = EasyConfig.getProperty(jndiDBTypeKey); // 如果该jndiKey不存在, 则取自实现的连接配置 if (dbType == null || "".equals(dbType.trim())) { dbType = EasyConfig.getProperty("DBType"); } if ("sqlserver".equalsIgnoreCase(dbType)) { throw new DataAccessException("EasyDB对Sqlserver不支持分页查询!"); } } log.info("SQL: " + pagerSql); // 查询位于startPos 至 endPos 之间的记录 stmt = this.conn.prepareStatement(pagerSql); // set the parameter to the '?' mark this.fillParamToMark(stmt, paramMap); rs = stmt.executeQuery(); // parse the rs and transform it into PageList ResultAssembler assembler = this.getResultAssembler(rs, entityClass); assembler.buildEntityList(); PageList pageList = (PageList) assembler.getRsList(); rs.close(); stmt.close(); // 计算所有满足业务条件的记录总数的SQL String countSql = sqlConstructor.buildCountSql(pSql); log.info("SQL: " + countSql); // 查询、设置所有满足业务条件的记录总数 stmt = this.conn.prepareStatement(countSql); this.fillParamToMark(stmt, paramMap); rs = stmt.executeQuery(); rs.next(); pageList.setTotalRecordCount(rs.getInt(1)); return pageList; } catch (SQLException e) { log.error(e.getErrorCode(), e); throw new DataAccessException(this.getDBType(), e.getErrorCode(), e.getMessage()); } catch (DataAccessException e) { log.error("", e); throw e; } catch (IllegalEntityException e) { log.error("", e); throw e; } catch (IllegalParamException e) { log.error("", e); throw e; } catch (FileAccessException e) { log.error("", e); throw e; } finally { this.freeResource(stmt, rs); } } // close the Statement in order to release resource protected void freeResource(Statement stmt) throws DataAccessException { try { if (stmt != null) { stmt.close(); } } catch (SQLException e) { log.error("", e); throw new DataAccessException(e.getMessage()); } } // close the Statement and ResultSet in order to release resource protected void freeResource(Statement stmt, ResultSet rs) throws DataAccessException { try { if (rs != null) { rs.close(); } if (stmt != null) { stmt.close(); } } catch (SQLException e) { log.error("", e); throw new DataAccessException(e.getMessage()); } } // 校验查询语句, 不得以UPDATE,DELETE,INSERT,TRUNCATE,DROP,ALTER等关键字开头 private boolean validateQuerySql(String sql) { final String UPDATE_KEYWORD = "UPDATE "; final String DELETE_KEYWORD = "DELETE "; final String INSERT_KEYWORD = "INSERT "; final String TRUNCATE_KEYWORD = "TRUNCATE "; final String DROP_KEYWORD = "DROP "; final String ALTER_KEYWORD = "ALTER "; String upperSql = sql.toUpperCase().trim(); if (upperSql.startsWith(UPDATE_KEYWORD) || upperSql.startsWith(DELETE_KEYWORD) || upperSql.startsWith(INSERT_KEYWORD) || upperSql.startsWith(TRUNCATE_KEYWORD) || upperSql.startsWith(DROP_KEYWORD) || upperSql.startsWith(ALTER_KEYWORD)) { return false; } return true; } /** * Retrieve the Sql Constructor. * * @return */ protected abstract SqlConstructor getSqlConstructor(); /** * Retrieve the ResultAssembler object which puts the query result into * PageList. */ protected abstract ResultAssembler getResultAssembler(ResultSet rs, Class<?> entityClass) throws DataAccessException, IllegalEntityException; // Process the writing of big data type, such as:BLOB/CLOB protected abstract void ProcessBigDataType(EntityParser parser) throws IllegalEntityException, DataAccessException, FileAccessException; // Process the writing of big data type, such as:BLOB/CLOB protected abstract void ProcessBigDataType(EntityParser dataParser, Criteria criteria) throws IllegalEntityException, DataAccessException, FileAccessException; // Set the parameter value to the '?' mark for parameterized sql. protected abstract void fillParamToMark(PreparedStatement stmt, Vector<String> indexedFieldVec, EntityParser entityParser) throws DataAccessException, FileAccessException, IllegalEntityException; // Set the parameter value to the '?' mark for parameterized sql. protected abstract void fillParamToMark(PreparedStatement stmt, SqlParamMap map) throws DataAccessException, FileAccessException, IllegalParamException; }