对spring JdbcTemplate的一个扩展(使其支持单Connection)
对spring JdbcTemplate的一个扩展(使其支持单Connection).
不怕大家笑话,以前一直没怎么使用过spring jdbc template,
印象中只用过
public List queryForList(String sql, Object[] args)
public Map queryForMap(String sql, Object[] args)
和SqlFunction
在orm大行其道,spring诞生快一个实际的今天,再来探讨jdbc的一些封装实在不知道有没有意义.
不过还是想把刚刚弄出来的一点东西和大家分享.
看了一下 JdbcTemplate,
发现起核心是那几个 execute 方法.
而那几个execute方法的机构大概是这样(重点讨论Connection,所以其他地方从简)
execute方法开始 Connection con = DataSourceUtils.getConnection(getDataSource()); // 做一些数据库操作 DataSourceUtils.releaseConnection(con, getDataSource());execute方法结束
当你要批量执行一些操作时(不是 每个操作使用不同的sql,或者是其他不适合 addBatch的情形).
如下:
JdbcTemplate jdbcTemplate=new JdbcTemplate(ds);jdbcTemplate.query(sql_1, args_1,rch_1);jdbcTemplate.query(sql_2, args_2,rch_2);jdbcTemplate.query(sql_3, args_3,rch_3);jdbcTemplate.query(sql_4, args_4,rch_4);......
此时,在内部实际上执行了,n次 getConnection,releaseConnection.
而这些操作,在很多时候,是可以通过一个Connection来完成的.
我的扩展就是实现了这个功能.
JdbcTemplatePlus jdbcTemplate=new JdbcTemplatePlus(ds);// 内部使用一个唯一的ConnectionjdbcTemplate.setUseOneConnection(true);jdbcTemplate.query(sql_1, args_1,rch_1);jdbcTemplate.query(sql_2, args_2,rch_2);jdbcTemplate.query(sql_3, args_3,rch_3);jdbcTemplate.query(sql_4, args_4,rch_4);......// 最后调用该方法 释放那个内部唯一的Connection// 虽然我在finalize 里调用了,不过finalize毕竟不是总能在正确的时间被正确的调用jdbcTemplate.releaseConnection();
我们系统中,有大量的嵌套查询.使用该JdbcTemplatePlus的唯一Connection特性后,类似的操作速度提升明显.
(不过可能我们原先的做法不对,也许 spring内部已经实现这个机制了 这些我就不明白了,欢迎大家来指正)
我们原先的做法(伪代码):
public List queryNsubQueryUserList(Map param){// 外层查询final String bsql="select * from ......";final JdbcTemplate jdbcTemplate=createJdbcTemplate();// 子查询相关final String subSql="select ............ ";final JdbcTemplate subJdbcTemplate=createJdbcTemplate();List rslist=jdbcTemplate.query(bsql.toString(),sqlArg, new ResultSetHandler(){public void processRow(ResultSet rs) throws SQLException {final 一个VO recordObj=new 一个VO();// 子查询subJdbcTemplate.query(subSql, subQueryArgs, new ResultSetHandler(){public void processRow(ResultSet rs) throws SQLException {// 一些操作........}});// 一些操作........recordObj.setXXXXX(rs.getString("XXXXX"));.........// 将记录放入集合addRecord(recordObj);}});return rslist;}}
在使用 JdbcTemplatePlus 代替 JdbcTemplate,并设置.setUseOneConnection(true)后,
耗时变为原先的1/8左右.
扩展的 JdbcTemplatePlus 代码如下,欢迎大家拍砖,挑错.
对 execute方法的修改如下:
把所有的 this.ativeJdbcExtractor换成 getNativeJdbcExtractor(), 这个是必须的 呵呵.
把所有 Connection con = DataSourceUtils.getConnection(getDataSource()); 换成
Connection con = tryGetConnection();
把所有 DataSourceUtils.releaseConnection(con, getDataSource()); 换成
tryReleaseConnection(con);
package com.neusoft.tdframework.dao;import java.sql.CallableStatement;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.SQLException;import java.sql.Statement;import javax.sql.DataSource;import org.springframework.dao.DataAccessException;import org.springframework.jdbc.core.CallableStatementCallback;import org.springframework.jdbc.core.CallableStatementCreator;import org.springframework.jdbc.core.ConnectionCallback;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.jdbc.core.ParameterDisposer;import org.springframework.jdbc.core.PreparedStatementCallback;import org.springframework.jdbc.core.PreparedStatementCreator;import org.springframework.jdbc.core.SqlProvider;import org.springframework.jdbc.core.StatementCallback;import org.springframework.jdbc.datasource.DataSourceUtils;import org.springframework.jdbc.support.JdbcUtils;import org.springframework.util.Assert;public class JdbcTemplatePlus extends JdbcTemplate {private Connection connection=null;private boolean useOneConnection=false;public JdbcTemplatePlus() {super();}public JdbcTemplatePlus(DataSource dataSource) {super(dataSource);}public JdbcTemplatePlus(DataSource dataSource, boolean lazyInit) {super(dataSource,lazyInit);}private Connection tryGetConnection(){if (useOneConnection){if (connection==null){connection= DataSourceUtils.getConnection(getDataSource());}return connection;}return DataSourceUtils.getConnection(getDataSource());}private void tryReleaseConnection(Connection con){if (!useOneConnection){DataSourceUtils.releaseConnection(con, getDataSource());}}public Connection getConnection(){return connection;}public void setConnection(Connection connection){this.connection=connection;}public boolean isUseOneConnection() {return useOneConnection;}public void setUseOneConnection(boolean useOneConnection) {this.useOneConnection = useOneConnection;}public void releaseConnection(){DataSourceUtils.releaseConnection(connection, getDataSource());}// 不明白这个方法为什么spring不把他弄成 protected 或 public,// 导致我要重写execute方法时还必须要重写这个方法 :(public static String getSql(Object sqlProvider) {if (sqlProvider instanceof SqlProvider) {return ((SqlProvider) sqlProvider).getSql();}else {return null;}}/*对 execute方法的修改如下:把所有的 this.ativeJdbcExtractor换成 getNativeJdbcExtractor(), 这个是必须的 呵呵.把所有 Connection con = DataSourceUtils.getConnection(getDataSource()); 换成Connection con = tryGetConnection();把所有 DataSourceUtils.releaseConnection(con, getDataSource()); 换成tryReleaseConnection(con); */public Object execute(ConnectionCallback action) throws DataAccessException {Assert.notNull(action, "Callback object must not be null");Connection con = tryGetConnection();try {Connection conToUse = con;if (getNativeJdbcExtractor() != null) {conToUse = getNativeJdbcExtractor().getNativeConnection(con);} else {conToUse = createConnectionProxy(con);}return action.doInConnection(conToUse);} catch (SQLException ex) {tryReleaseConnection(con);con = null;throw getExceptionTranslator().translate("ConnectionCallback",getSql(action), ex);} finally {tryReleaseConnection(con);}}public Object execute(StatementCallback action) throws DataAccessException {Assert.notNull(action, "Callback object must not be null");Connection con = tryGetConnection();Statement stmt = null;try {Connection conToUse = con;if (getNativeJdbcExtractor() != null&& getNativeJdbcExtractor().isNativeConnectionNecessaryForNativeStatements()) {conToUse = getNativeJdbcExtractor().getNativeConnection(con);}stmt = conToUse.createStatement();applyStatementSettings(stmt);Statement stmtToUse = stmt;if (getNativeJdbcExtractor() != null) {stmtToUse = getNativeJdbcExtractor().getNativeStatement(stmt);}Object result = action.doInStatement(stmtToUse);handleWarnings(stmt.getWarnings());return result;} catch (SQLException ex) {JdbcUtils.closeStatement(stmt);stmt = null;tryReleaseConnection(con);con = null;throw getExceptionTranslator().translate("StatementCallback",getSql(action), ex);} finally {JdbcUtils.closeStatement(stmt);tryReleaseConnection(con);}}public Object execute(PreparedStatementCreator psc,PreparedStatementCallback action) throws DataAccessException {Assert.notNull(psc, "PreparedStatementCreator must not be null");Assert.notNull(action, "Callback object must not be null");if (logger.isDebugEnabled()) {String sql = getSql(psc);logger.debug("Executing prepared SQL statement"+ (sql != null ? " [" + sql + "]" : ""));}Connection con = tryGetConnection();PreparedStatement ps = null;try {Connection conToUse = con;if (getNativeJdbcExtractor() != null&& getNativeJdbcExtractor().isNativeConnectionNecessaryForNativePreparedStatements()) {conToUse = getNativeJdbcExtractor().getNativeConnection(con);}ps = psc.createPreparedStatement(conToUse);applyStatementSettings(ps);PreparedStatement psToUse = ps;if (getNativeJdbcExtractor() != null) {psToUse = getNativeJdbcExtractor().getNativePreparedStatement(ps);}Object result = action.doInPreparedStatement(psToUse);handleWarnings(ps.getWarnings());return result;} catch (SQLException ex) {if (psc instanceof ParameterDisposer) {((ParameterDisposer) psc).cleanupParameters();}String sql = getSql(psc);psc = null;JdbcUtils.closeStatement(ps);ps = null;tryReleaseConnection(con);con = null;throw getExceptionTranslator().translate("PreparedStatementCallback", sql, ex);} finally {if (psc instanceof ParameterDisposer) {((ParameterDisposer) psc).cleanupParameters();}JdbcUtils.closeStatement(ps);tryReleaseConnection(con);}}public Object execute(CallableStatementCreator csc,CallableStatementCallback action) throws DataAccessException {Assert.notNull(csc, "CallableStatementCreator must not be null");Assert.notNull(action, "Callback object must not be null");if (logger.isDebugEnabled()) {String sql = getSql(csc);logger.debug("Calling stored procedure"+ (sql != null ? " [" + sql + "]" : ""));}Connection con = tryGetConnection();CallableStatement cs = null;try {Connection conToUse = con;if (getNativeJdbcExtractor() != null) {conToUse = getNativeJdbcExtractor().getNativeConnection(con);}cs = csc.createCallableStatement(conToUse);applyStatementSettings(cs);CallableStatement csToUse = cs;if (getNativeJdbcExtractor() != null) {csToUse = getNativeJdbcExtractor().getNativeCallableStatement(cs);}Object result = action.doInCallableStatement(csToUse);handleWarnings(cs.getWarnings());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.if (csc instanceof ParameterDisposer) {((ParameterDisposer) csc).cleanupParameters();}String sql = getSql(csc);csc = null;JdbcUtils.closeStatement(cs);cs = null;tryReleaseConnection(con);con = null;throw getExceptionTranslator().translate("CallableStatementCallback", sql, ex);} finally {if (csc instanceof ParameterDisposer) {((ParameterDisposer) csc).cleanupParameters();}JdbcUtils.closeStatement(cs);tryReleaseConnection(con);}}protected void finalize() throws Throwable{super.finalize();releaseConnection();}}1 楼 downpour 2007-06-15 存在着一个问题,JDBCTemplate我们一般都是单例的,通过IoC注入到DAO中去,你现在继承JdbcTemplate去写,每次要用的时候就得自己去控制它的创建。你的这个思路好是好,不过总觉得还有一丝不足。 2 楼 finly 2007-07-23 用了一下...感觉速度几乎没任何提升,我跟你的例子一样,也有一个子查询..用与没用花的时间都是40秒左右...看来只能换种查询方式了. 3 楼 fins 2007-07-26 你可能用的是本地数据库吧??
在我们实际系统中,使用同一个 connection 确实要比在循环内部不停的去取得新connection快很多
当然这些与数据源的设置 网络情况 所使用的数据库等等有关.
在您的环境下也许确实提高不了速度. 4 楼 airlulu 2007-08-20 JdbcTemplate之所以设计成只接收DataSource是跟Spring的事务同步机制有关。
如果在调用之前使用spring的编程或配置方式定义事务同步,就不会出现重复取连接的情况。