在多线程环境中使用Spring jdbcTemplate批处理插入时,数据库中缺少条目

时间:2017-05-01 13:50:05

标签: java spring multithreading jdbctemplate

我正在尝试使用对batchUpdate()方法的简单spring jdbcTemplate调用将多个记录添加到Oracle 12c数据库中。进行batchUpdate()调用的代码在我的DAO类中。但是,调用服务方法的代码是多线程的。

这就是我的DAO类看起来的样子(还有更像这样):

public void batchInsertSessionSignature(final List<SessionSignature> sessionSignatures, DataSource dataSource) {

    Assert.notNull(sessionSignatures, "Checking to see if the SessionSignature objects passed in are null");

    if (dataSource != null) {
        jdbcTemplate.setDataSource(dataSource);
    }

    if (!sessionSignatures.isEmpty()) {

        log.debug("Inserting batch entries into lev_session_signature_temp of size: " + sessionSignatures.size());

        int[] res = jdbcTemplate.batchUpdate(insertSessionSig, new BatchPreparedStatementSetter() {

            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

            @Override
            public void setValues(PreparedStatement pstmt, int i) throws SQLException {

                SessionSignature ss = sessionSignatures.get(i);
                String created_date = sdf.format(ss.getCreated_date());

                pstmt.setLong(1, ss.getId());
                pstmt.setString(2, ss.getHost());
                pstmt.setString(3, ss.getSession_id());
                pstmt.setString(4, ss.getIp_address());         
                pstmt.setString(5, ss.getAccountName());
                try {
                    pstmt.setInt(6, sysInfoBean.getSysinfoID(ss.getSysinfo()));
                    pstmt.setDate(7, new Date(sdf.parse(created_date).getTime()));
                } catch (ParseException e) {
                    e.printStackTrace();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                pstmt.setString(8, ss.getCountry());
                pstmt.setInt(9, ss.getAccountID());
                pstmt.setString(10, ss.getBusinessUnit());
            }

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

        log.debug(res.length + " entries inserted into lev_session_signature_temp");

    }
}

这是调用DAO类的服务层方法(该方法是线程安全的)

@Transactional(propagation= Propagation.REQUIRES_NEW, rollbackFor=Exception.class)
public synchronized void writeTargetDaoEntries(TargetDao targetDao, DataSource dataSource, ArrayList<SessionSignature> tgtSessionSignatures, 
        ArrayList<Session> tgtSession, ArrayList<SearchGroup> tgtSearchGroup, ArrayList<Search> tgtSearches, 
        ArrayList<LuceneEvent> tgtEvents, ArrayList<EventAnalysisDaoUtil> eventAnalysisList) throws Exception {

    targetDao.batchInsertSessionSignatures(tgtSessionSignatures, dataSource);
    targetDao.batchInsertUserSessions(tgtSession, dataSource);
    targetDao.batchInsertSearchGroups(tgtSearchGroup, dataSource);
    targetDao.batchInsertSearches(tgtSearches, dataSource);
    targetDao.batchInsertEvents(tgtEvents, dataSource);
    targetDao.batchInsertEventAnalysis(eventAnalysisList, dataSource);

}

我的弹簧配置如下所示

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx 
http://www.springframework.org/schema/tx/spring-tx.xsd"> 

<context:annotation-config/>
<context:property-placeholder location="app.properties"/>

<bean id="dataSourceQa" class="oracle.ucp.jdbc.PoolDataSourceFactory"
    factory-method="getPoolDataSource" lazy-init="true">
    <property name="connectionFactoryClassName" 
value="oracle.jdbc.pool.OracleDataSource" />
    <property name="user" value="${qaTargetId}" />
    <property name="password" value="${qaTargetPswd}" />
    <property name="URL" value="${qaTargetURL}" />
    <property name="minPoolSize" value="2" />
    <property name="maxPoolSize" value="10" />
</bean>

<bean id="dataSourceProd" class="oracle.ucp.jdbc.PoolDataSourceFactory"
    factory-method="getPoolDataSource" lazy-init="true">
    <property name="connectionFactoryClassName" 
value="oracle.jdbc.pool.OracleDataSource" />
    <property name="user" value="${prodSourceId}" />
    <property name="password" value="${prodSourcePswd}" />
    <property name="URL" value="${prodSourceURL}" />
    <property name="minPoolSize" value="2" />
    <property name="maxPoolSize" value="10" />
</bean>

<!-- JDBC Template Configuration -->        
<bean id = "jdbcTemplate" class = "org.springframework.jdbc.core.JdbcTemplate">
    <property name = "dataSource" ref = "dataSourceQa"></property>
</bean>

<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSourceQa" />
</bean>

<bean id="taskExecutor"
    class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
    <property name="corePoolSize" value="5" />
    <property name="maxPoolSize" value="10" />
    <property name="WaitForTasksToCompleteOnShutdown" value="true" />
</bean>

<!-- DAO Bean Configuration -->
<bean id = "eventDaoBean" class = "com.uptodate.lucene.tool.analysis.transfer.oracleDao.EventDao" />
<bean id = "searchDaoBean" class = "com.uptodate.lucene.tool.analysis.transfer.oracleDao.SearchDao" />
<bean id = "searchGroupDaoBean" class = "com.uptodate.lucene.tool.analysis.transfer.oracleDao.SearchGroupDao" />
<bean id = "userSessionDaoBean" class = "com.uptodate.lucene.tool.analysis.transfer.oracleDao.UserSessionDao" />
<bean id = "sessionSignatureDaoBean" class = "com.uptodate.lucene.tool.analysis.transfer.oracleDao.SessionSignatureDao" />
<bean id = "eventAnalysisDaoBean" class = "com.uptodate.lucene.tool.analysis.transfer.oracleDao.EventAnalysisDao" />
<bean id = "dailySummaryDaoBean" class = "com.uptodate.lucene.tool.analysis.transfer.oracleDao.DailySummaryDao" />
<bean id = "keyGeneratorBean" class = "com.uptodate.lucene.tool.analysis.transfer.oracleDao.KeyGenerator" />
<bean id = "machineLearningDaoBean" class = "com.uptodate.lucene.tool.analysis.transfer.oracleDao.MachineLearningDao" />
<bean id = "sourceDaoBean" abstract = "true" class = "com.uptodate.lucene.tool.analysis.transfer.oracleDao.SourceDao" />
<bean id = "desktopSourceDaoBean" class = "com.uptodate.lucene.tool.analysis.transfer.oracleDao.DesktopSourceDao" />        
<bean id = "mobileSourceDaoBean" class = "com.uptodate.lucene.tool.analysis.transfer.oracleDao.MobileSourceDao" />
<bean id = "webSourceDaoBean" class = "com.uptodate.lucene.tool.analysis.transfer.oracleDao.WebSourceDao" />
<bean id = "tempTableDaoBean" class = "com.uptodate.lucene.tool.analysis.transfer.oracleDao.TempTableDao" />

<bean id = "targetDaoBean" class = "com.uptodate.lucene.tool.analysis.transfer.oracleDao.TargetDao">
    <constructor-arg ref = "eventDaoBean" />
    <constructor-arg ref = "searchDaoBean" />
    <constructor-arg ref = "searchGroupDaoBean" />
    <constructor-arg ref = "userSessionDaoBean" />
    <constructor-arg ref = "sessionSignatureDaoBean" />
    <constructor-arg ref = "eventAnalysisDaoBean" />
</bean>

<!-- Parser Bean Configuration -->
<bean id = "userSessionParserBean" class = "com.uptodate.lucene.tool.analysis.transfer.parser.UserSessionParser" scope = "prototype" />
<bean id = "searchParserBean" class = "com.uptodate.lucene.tool.analysis.transfer.parser.SearchParser" scope = "prototype" />
<bean id = "searchGroupParserBean" class = "com.uptodate.lucene.tool.analysis.transfer.parser.SearchGroupParser" scope = "prototype" />
<bean id = "eventParserBean" abstract = "true" class = "com.uptodate.lucene.tool.analysis.transfer.parser.EventParser" scope = "prototype" />
<bean id = "mobileEventParserBean" class = "com.uptodate.lucene.tool.analysis.transfer.parser.MobileEventParser" scope = "prototype" />
<bean id = "webEventParserBean" class = "com.uptodate.lucene.tool.analysis.transfer.parser.WebEventParser" scope = "prototype" />

<bean id = "logParserBean" class = "com.uptodate.lucene.tool.analysis.transfer.LogParser" scope = "prototype">
    <constructor-arg ref = "keyGeneratorBean" />
    <constructor-arg ref = "searchParserBean" />
    <constructor-arg ref = "searchGroupParserBean" />
    <constructor-arg ref = "userSessionParserBean" />
</bean>

<!-- Transfer Bean Configuration -->
<bean id = "dailySummaryGeneratorBean" class = "com.uptodate.lucene.tool.analysis.transfer.DailySummaryGenerator" />

<!-- Util Bean Configuration -->
<bean id = "sysInfoBean" class = "com.uptodate.lucene.tool.analysis.vo.SysInfo">
    <property name = "dataSource" ref = "dataSourceProd" />
</bean>

我尝试添加对事务的支持,希望这有助于确保将条目提交到数据库,但这没有帮助。我可以在代码中看到发送到数据库的所有条目都写入了它但是当我检查数据库中的记录数时,不会反映相同的数字。我不确定这是否是因为代码的多线程特性导致一批entires以某种方式被覆盖?使用1线程似乎工作正常使我相信它是一个多线程问题,但我不知道问题出在哪里。对此问题的任何帮助表示赞赏。

0 个答案:

没有答案