技术CTO-关注编程入门知识,提供编程入门教程

您的位置: 首页 > 网络编程 > java开发 > 正文

关于jdbcTemplate的dataSource设置为ComboPooledDataSource,批量处理并没有使执行时间减少.(为了尽可能详细,问题有点长)

来源: 技术CTO 阅读:

我在applicationContext.xml中的配置如下:


<!-- c3p0 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
  destroy-method="close">
  <property name="user"  value="${db_user}" />
<property name="password" value="${db_password}" />
<property name="jdbcUrl" value="${db_url}" />
<property name="driverClass" value="${db_driver}" />

<!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 -->
<property name="AcquireIncrement"  value="3"/>     
 <!--定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 -->
    <property name="AcquireRetryAttempts"  value="30"/> 
      <!--两次连接中间隔时间,单位毫秒。Default: 1000 -->
    <property name="AcquireRetryDelay" value="1000"/>   
    <!--连接关闭时默认将所有未提交的操作回滚。Default: false -->
    <property name="AutoCommitOnClose" value="false"/>
    <!--获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常。但是数据源仍有效保留,并在下次调用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试获取连接失败后该数据源将申明已断开并永久关闭。Default: false-->
    <property name="BreakAfterAcquireFailure" value="false"/>
    <!--当连接池用完时客户端调用getConnection()后等待获取新连接的时间,超时后将抛出SQLException,如设为0则无限期等待。单位毫秒。Default: 0 -->
    <property name="CheckoutTimeout" value="200000"/>                     
    <property name="Description" value="A pooled c3p0 DataSource"/>
    <property name="InitialPoolSize" value="2"/>
    <property name="MinPoolSize" value="1"/>
    
    <property name="MaxPoolSize" value="10"/>
    <!--最大空闲时间,1800秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->
    <property name="MaxIdleTime" value="1800"/>
    <!--JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default: 0-->
    <property name="MaxStatements" value="0"/>
    <!--定义了连接池内单个连接所拥有的最大缓存statements数。Default: 0 -->
    <property name="MaxStatementsPerConnection" value="100"/>
    <!--c3p0是异步操作的,缓慢的JDBC操作通过帮助进程完成。扩展这些操作可以有效的提升性能通过多线程实现多个操作同时被执行。Default: 3-->
    <property name="NumHelperThreads" value="2"/>
    
    
    <!--通过实现ConnectionTester或QueryConnectionTester的类来测试连接。类名需制定全路径。Default: com.mchange.v2.c3p0.impl.DefaultConnectionTester-->
    <property name="ConnectionTesterClassName" value="com.mchange.v2.c3p0.impl.DefaultConnectionTester"/>
    <!--每段时间检查所有连接池中的空闲连接。时间为秒,Default: 0 -->
     <property name="IdleConnectionTestPeriod" value="3600"/>
    <!--c3p0将建一张名为Test的空表,并使用其自带的查询语句进行测试。如果定义了这个参数那么属性preferredTestQuery将被忽略。你不能在这张Test表上进行任何操作,它将只供c3p0测试使用。Default: null-->
<property name="automaticTestTable" value="C3P0_TEST"/>
    <!--如果设为true那么在取得连接的同时将校验连接的有效性。Default: false -->
    <property name="TestConnectionOnCheckin" value="true"/>     
    <!--因性能消耗大请只在需要的时候使用它。如果设为true那么在每个connection提交的时候都将校验其有效性。建议使用idleConnectionTestPeriod或automaticTestTable等方法来提升连接测试的性能。Default: false -->
    <property name="TestConnectionOnCheckout" value="false"/>
    <!--Strongly disrecommended. Setting this to true may lead to subtle and bizarre bugs.(文档原文)作者强烈建议不使用的一个属性-->
    <property name="ForceIgnoreUnresolvedTransactions" value="false"/>
    <!--早期的c3p0版本对JDBC接口采用动态反射代理。在早期版本用途广泛的情况下这个参数 允许用户恢复到动态反射代理以解决不稳定的故障。最新的非反射代理更快并且已经开始 广泛的被使用,所以这个参数未必有用。现在原先的动态反射与新的非反射代理同时受到支持,但今后可能的版本可能不支持动态反射代理。Default: false--> 
    <property name="UsesTraditionalReflectiveProxies" value="false"/>
    
 </bean>

<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>


在DAO层这样处理:

protected int[] batchExcute(final String sql, final List<Object[]> args) {
    
int[] temp=jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {

@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
Object[] os = args.get(i);
for (int n = 0; n < os.length; n++) {
ps.setObject(n + 1, os[n]);
}
}

@Override
public int getBatchSize() {
return args.size();
}
});

    return temp;
}


今天在编写一段需要对数据批量新增的代码时,我发现使用上面的方法和我一条一条新增所消耗时间并没有多大的差别。

后来查阅了资料后,发现需要设置Connection的autoCommit属性为false,也就是如下:

jdbcTemplate.getDataSource().getConnection().setAutoCommit(false);

//批处理code
//.....

jdbcTemplate.getDataSource().getConnection().commit();//手动提交
jdbcTemplate.getDataSource().getConnection().setAutoCommit(true);//还原


但是,出现了如下异常:


java.sql.SQLException: Can't call commit when autocommit=true
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:930)
at com.mysql.jdbc.ConnectionImpl.commit(ConnectionImpl.java:1535)
at com.mchange.v2.c3p0.impl.NewProxyConnection.commit(NewProxyConnection.java:1580)
at com.xmu.orm.BasicDao.batchExcute(BasicDao.java:222)
at com.xmu.dao.NewsDao.batchExecute(NewsDao.java:36)
at com.xmu.service.NewsService.batchExecute(NewsService.java:28)
at com.xmu.main.Test.preprocess(Test.java:102)
at com.xmu.main.Test.main(Test.java:122)


从异常可以看出,设置无效!!这是为什么?--

另外,我还自己编写了一个DBUtil类,证明了在设置Connection的autoCommit属性为false时,时间会相差几十倍,相关部分的代码如下:


public DBUitl() throws Exception {

Class.forName(DRIVER);
conn = (Connection) DriverManager.getConnection(URL, USER, PASSWORD);
conn.setAutoCommit(false);
}

        public int[] addBatch(List<News> list) throws Exception{
final String sql = "insert into news(news_id,news_time,news_time_str,title,content,type,theme,keywords) value(?,?,?,?,?,?,?,?) ";
PreparedStatement statement = (PreparedStatement) conn.prepareStatement(sql);

for(News news:list){
List<Object> params = new ArrayList<Object>();

params.add(news.getNewsId());
params.add(news.getNewsTime());
params.add(news.getNewsTimeStr());
params.add(news.getTitle());

params.add(news.getContent());
params.add(news.getType());
params.add(news.getTheme());
params.add(news.getKeywords());

for (int i = 1; i <= params.size(); i++) {
statement.setObject(i, params.get(i - 1));
}

statement.addBatch();
}
int[] result=statement.executeBatch();
conn.commit();
closeConnection();
return result;
}


以下是各方案的执行时间:

处理模式:jdbcTemplate and dataSource 为ComboPooledDataSource 批量处理
所处理的记录数:1000:
time:55530

处理模式:jdbcTemplate and dataSource 为ComboPooledDataSource 一条一条新增处理
所处理的记录数:1000:
time:4950

处理模式:自己写的DBUtil,同时conn.setAutoCommit(false);批量新增
所处理的记录数:1000:
time:381

处理模式:自己写的DBUtil,同时conn.setAutoCommit(true);批量新增
所处理的记录数:1000:
time:68940

从上面的对比可以看出,设置Connection的autoCommit属性很关键!

回到问题的本质:
1)为什么ComboPooledDataSource的autoCommit的设置无效呢?是不是配置文件出现了什么问题?
2)还是说我设置的位置不对,应该在哪里设置呢?

纠结差不多一天了,求大家指点下!万分感激
问题描述的很详细,可惜我jdbc用的较少
你用的什么数据库,是不是不支持批处理操作
引用 1 楼 sc6231565 的回复:
问题描述的很详细,可惜我jdbc用的较少
 
蒽蒽,没事的!能不能邀请一些大牛来指点下?
引用 2 楼 whos2002110 的回复:
你用的什么数据库,是不是不支持批处理操作

mysql,支持批处理
c3p0不支持这个设置,就是说配置文件没用,只能通过代码,你可以看下配置文件,但dbcp支持,你可以看下,http://sjsky.iteye.com/blog/1105674,mysql默认是autocommit=true,这个是很影响性能的
一句话:通过配置文件不能设置autoCommit为false

^_^ 如果您热爱技术、热爱编程,想与更多的朋友一起交流学习,欢迎加入本站官方QQ群:345733473 ^_^