`

对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);
// 内部使用一个唯一的Connection
jdbcTemplate.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();
	}


	
}

分享到:
评论
4 楼 airlulu 2007-08-20  
JdbcTemplate之所以设计成只接收DataSource是跟Spring的事务同步机制有关。
如果在调用之前使用spring的编程或配置方式定义事务同步,就不会出现重复取连接的情况。
3 楼 fins 2007-07-26  
你可能用的是本地数据库吧??

在我们实际系统中,使用同一个 connection 确实要比在循环内部不停的去取得新connection快很多

当然这些与数据源的设置 网络情况 所使用的数据库等等有关.

在您的环境下也许确实提高不了速度.
2 楼 finly 2007-07-23  
用了一下...感觉速度几乎没任何提升,我跟你的例子一样,也有一个子查询..用与没用花的时间都是40秒左右...看来只能换种查询方式了.
1 楼 downpour 2007-06-15  
存在着一个问题,JDBCTemplate我们一般都是单例的,通过IoC注入到DAO中去,你现在继承JdbcTemplate去写,每次要用的时候就得自己去控制它的创建。你的这个思路好是好,不过总觉得还有一丝不足。

相关推荐

    Spring 学习 JdbcTemplate,模板模式,回调

    JdbcTemplate是Spring提供的一个用于简化数据库操作的API,它是Spring对JDBC(Java Database Connectivity)的轻量级封装。通过使用JdbcTemplate,开发者可以避免编写大量的样板代码,如打开和关闭连接、处理结果集...

    Spring中文帮助文档

    9.9.1. 对一个特定的 DataSource 使用了错误的事务管理器 9.10. 更多的资源 10. DAO支持 10.1. 简介 10.2. 一致的异常层次 10.3. 一致的DAO支持抽象类 11. 使用JDBC进行数据访问 11.1. 简介 11.1.1. 选择一...

    spring-jdbc源码

    Spring-JDBC是Spring框架的一个重要模块,它提供了一种简化数据库操作的抽象层,使得开发者可以更加方便地进行数据访问。在本篇文章中,我们将深入探讨Spring-JdbcTemplate、DataSourceTransactionManager以及相关的...

    Spring-Reference_zh_CN(Spring中文参考手册)

    9.9.1. 对一个特定的 DataSource 使用错误的事务管理器 9.10. 更多的资源 10. DAO支持 10.1. 简介 10.2. 一致的异常层次 10.3. 一致的DAO支持抽象类 11. 使用JDBC进行数据访问 11.1. 简介 11.1.1. Spring JDBC包结构...

    spring学习:JDBC数据访问

    3. **NamedParameterJdbcTemplate**:这是JdbcTemplate的一个扩展,允许使用命名参数代替占位符,使得SQL语句更易读和维护。 4. **Transaction Management**:Spring提供了声明式事务管理,通过在方法上添加@...

    Spring 2.0 开发参考手册

    9.9.1. 对一个特定的 DataSource 使用错误的事务管理器 9.10. 更多的资源 10. DAO支持 10.1. 简介 10.2. 一致的异常层次 10.3. 一致的DAO支持抽象类 11. 使用JDBC进行数据访问 11.1. 简介 11.1.1. Spring ...

    Spring API

    9.9.1. 对一个特定的 DataSource 使用了错误的事务管理器 9.10. 更多的资源 10. DAO支持 10.1. 简介 10.2. 一致的异常层次 10.3. 一致的DAO支持抽象类 11. 使用JDBC进行数据访问 11.1. 简介 11.1.1. 选择一种...

    spring chm文档

    9.9.1. 对一个特定的 DataSource 使用错误的事务管理器 9.10. 更多的资源 10. DAO支持 10.1. 简介 10.2. 一致的异常层次 10.3. 一致的DAO支持抽象类 11. 使用JDBC进行数据访问 11.1. 简介 11.1.1. Spring ...

    JMS1.1规范培训教程&&spring框架

    JMS 1.1 是其一个重要版本,它提供了应用程序间的可靠通信,使得消息的生产者和消费者可以在不同的时间进行交互,即使它们可能同时处于运行状态或暂时离线。以下是对 JMS 1.1 规范中的关键知识点的详细解释: 1. **...

    mysql-jdbc-Spring源代码分析

    `JdbcTemplate`是Spring框架提供的一个用于简化JDBC编程的工具类,它封装了许多常用的数据库操作,如查询、更新等,并且通过模板模式设计,提供了灵活的扩展机制。这使得开发者能够更专注于业务逻辑而非繁琐的JDBC...

    DB_Connection_Factory.rar_DB factory桥连接_factory

    DataSource可以配置在应用服务器中,这样多个应用程序可以共享同一个数据源,减少了资源消耗。同时,应用服务器会负责管理这些连接,包括连接池的创建、连接的分配和释放,确保了高效且线程安全的数据库访问。 在...

    spring整合数据库连接的几种方式

    - C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。 - C3P0的优势在于其内置的自动检测和修复损坏的连接功能,以及对多线程环境的良好支持。 - 配置C3P0的步骤与DBCP...

    02-01-11-基于Spring JDBC手写定制自己的ORM框架1

    在本课程"02-01-11-基于Spring JDBC手写定制自己的ORM框架1"中,我们将探讨如何利用Spring的JdbcTemplate设计理念,来构建一个自定义的ORM(对象关系映射)框架。ORM框架的主要目的是简化Java应用程序与数据库之间的...

    spring mvc 配置多数据源

    这里定义了一个名为`DynamicDataSource`的`Bean`,它能够根据传入的键值动态地选择数据源。 #### 5. jdbcTemplate配置 最后,还需要配置`jdbcTemplate`以供业务层调用。 ```xml <!-- jdbcTemplate配置 --> ...

    用jsoup框架进行音乐网站的数据爬取。以及用spring-boot+JDBC封装搭建音乐网站平台系统。.zip

    在构建一个音乐网站平台系统的过程中,技术和工具的选择至关重要。在这个项目中,主要涉及了两个核心部分:数据爬取和后端服务的搭建。下面将详细阐述这两个方面。 首先,我们来看数据爬取部分,这里使用了`Jsoup`...

    Java通过Druid连接mysql

    Java通过Druid连接MySQL是一种常见的数据库操作方式,Druid是一个强大的数据库连接池组件,由阿里巴巴开源,它提供了优秀的性能、全面的监控以及强大的扩展性。在这个场景中,我们将探讨如何在Java环境中使用Druid来...

    java后台springboot,利用spatialite,创建sqlite文件,写入离线矢量数据

    Spatialite是一个开源的扩展,它为SQLite数据库添加了对地理空间数据的支持,使其能够处理复杂的地理空间操作。 首先,你需要确保在系统`system32`目录下已经安装并配置了Spatialite的相关库。通常,这包括`...

    java常用框架学习笔记

    多对一关系是指多个实体对象与另一个实体对象之间存在关联关系。例如,一个订单可能属于一个客户,但一个客户可以拥有多个订单。在Hibernate中,可以通过`@ManyToOne`注解来定义这种关系。 **示例代码**: ```java...

Global site tag (gtag.js) - Google Analytics