2007-06-13
对spring JdbcTemplate的一个扩展(使其支持单Connection)
关键字: sping
对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,所以其他地方从简)
当你要批量执行一些操作时(不是 每个操作使用不同的sql,或者是其他不适合 addBatch的情形).
如下:
此时,在内部实际上执行了,n次 getConnection,releaseConnection.
而这些操作,在很多时候,是可以通过一个Connection来完成的.
我的扩展就是实现了这个功能.
我们系统中,有大量的嵌套查询.使用该JdbcTemplatePlus的唯一Connection特性后,类似的操作速度提升明显.
(不过可能我们原先的做法不对,也许 spring内部已经实现这个机制了 这些我就不明白了,欢迎大家来指正)
我们原先的做法(伪代码):
在使用 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);
不怕大家笑话,以前一直没怎么使用过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();
}
}
评论
airlulu
2007-08-20
JdbcTemplate之所以设计成只接收DataSource是跟Spring的事务同步机制有关。
如果在调用之前使用spring的编程或配置方式定义事务同步,就不会出现重复取连接的情况。
如果在调用之前使用spring的编程或配置方式定义事务同步,就不会出现重复取连接的情况。
fins
2007-07-26
你可能用的是本地数据库吧??
在我们实际系统中,使用同一个 connection 确实要比在循环内部不停的去取得新connection快很多
当然这些与数据源的设置 网络情况 所使用的数据库等等有关.
在您的环境下也许确实提高不了速度.
在我们实际系统中,使用同一个 connection 确实要比在循环内部不停的去取得新connection快很多
当然这些与数据源的设置 网络情况 所使用的数据库等等有关.
在您的环境下也许确实提高不了速度.
finly
2007-07-23
用了一下...感觉速度几乎没任何提升,我跟你的例子一样,也有一个子查询..用与没用花的时间都是40秒左右...看来只能换种查询方式了.
downpour
2007-06-15
存在着一个问题,JDBCTemplate我们一般都是单例的,通过IoC注入到DAO中去,你现在继承JdbcTemplate去写,每次要用的时候就得自己去控制它的创建。你的这个思路好是好,不过总觉得还有一丝不足。
- 浏览: 706235 次
- 性别:

- 来自: 小胖儿的大城

- 详细资料
搜索本博客
我的相册
David Recordon
共 63 张
共 63 张
链接
最新评论
-
EXT 2 绚丽表格 背后的 ...
楼上的真是锐道的好员工啊 dorado整体表现确实不错 但是没有哪个单项可以用 ...
-- by fins -
EXT 2 绚丽表格 背后的 ...
http://www.bstek.com/dorado5/performance ...
-- by hotbarsmu -
[GT-Grid]列表组件 GT-Gr ...
如果一切正常 下周应该会出一个前后台结合的例子 例子已经在编写中了 不过为了 ...
-- by fins -
[GT-Grid]列表组件 GT-Gr ...
fins什么时候会有和服务端结合的版本呢?您可以给个简单的案例吗?谢谢
-- by hgq0011 -
[GT-Grid]列表组件 GT-Gr ...
这个是和ecside完全不同的产品 自然看起来也会面目全非了 呵呵
-- by fins






评论排行榜