为什么两个线程在从数据库读取时访问相同数据?

时间:2014-07-31 08:52:43

标签: multithreading spring batch-processing spring-batch

这里是我试过的代码,但多个线程正在访问数据库中的相同数据....

MY XML

 <?xml version="1.0" encoding="UTF-8"?>

<beans 
     xmlns="http://www.springframework.org/schema/beans"
    xmlns:batch="http://www.springframework.org/schema/batch"
    xmlns:jdbc="http://www.springframework.org/schema/jdbc"

    xsi:schemaLocation="
    http://www.springframework.org/schema/beans                  http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/batch                  http://www.springframework.org/schema/batch/spring-batch.xsd
    http://www.springframework.org/schema/jdbc                   http://www.springframework.org/schema/jdbc/spring-jdbc.xsd"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">


<bean id="JobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
        <property name="jobRepository" ref="jobRepository" />
</bean>


<batch:job id="firstjob">
       <batch:step id="masterStep">
            <batch:partition step="step1" partitioner="rangePartitioner">
                        <batch:handler grid-size="1" task-executor="taskExecutor" />
            </batch:partition>
       </batch:step>
</batch:job>



<batch:step id="step1"  xmlns="http://www.springframework.org/schema/batch">
        <batch:tasklet >
            <batch:chunk reader="itemReader" writer="ItemWriter" commit-interval="1" >
            </batch:chunk>
        </batch:tasklet>
</batch:step>


<bean id="rangePartitioner" class="com.spring.itemreader.Partioner" />

<bean id="taskExecutor" class="org.springframework.core.task.SimpleAsyncTaskExecutor" />


<bean id="jobRepository" class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="transactionManager" ref="transactionManager" />
        <property name="databaseType" value="mysql" />
</bean>

<bean id="transactionManager" class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />

<bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/test" />
        <property name="username" value="root" />
        <property name="password" value="" />
</bean>


<jdbc:initialize-database data-source="dataSource">
            <jdbc:script location="org/springframework/batch/core/schema-drop-mysql.sql" />
            <jdbc:script location="org/springframework/batch/core/schema-mysql.sql" />
</jdbc:initialize-database>




<bean id="itemReader"  class="org.springframework.batch.item.database.JdbcCursorItemReader" scope="step">
                <property name="dataSource" ref="dataSource" />             
                <property name="sql" value="select id  from reader where id  &gt;= ? and id &lt;= ? " />
                <property name="preparedStatementSetter" ref="readersetter"/>
                <property name="rowMapper" ref="rowmapprer"/>
</bean>         

<bean id="rowmapprer" class="com.spring.itemreader.rowmapprer">
      <property name="data" ref="data"/>
</bean>

<bean id="data" class="com.spring.itemreader.data" />

<bean id="ItemWriter" class="org.springframework.batch.item.database.JdbcBatchItemWriter">
        <property name="assertUpdates" value="false" />
        <property name="itemPreparedStatementSetter">
                <bean class="com.spring.itemwriter.setter" />
        </property>
        <property name="sql"  value="INSERT INTO writer VALUES(?)" />
        <property name="dataSource" ref="dataSource" />
</bean>


<bean id="readersetter" class="com.spring.itemreader.readersetter" scope="step">
            <property name="fid" value="#{stepExecutionContext[fromId]}"/>
            <property name="tid" value="#{stepExecutionContext[toId]}"/>
</bean>


</beans>

,我的分区类是:

 public class Partioner implements Partitioner{

    @Override
    public Map<String, ExecutionContext> partition(int gridSize) {

//map for storing:

    Map<String, ExecutionContext> result 
                       = new HashMap<String, ExecutionContext>();

        int range = 100;
        int fromId = 1;
        int toId = range;
        ExecutionContext value=null;

 using for loop i am giving range id and ExecutionContext

        for (int i = 1; i <= gridSize; i++) {
         value = new ExecutionContext();

            System.out.println("\nStarting : Thread" + i);
            System.out.println("fromId : " + fromId);
            System.out.println("toId : " + toId);

            value.putInt("fromId", fromId);
            value.putInt("toId", toId);

            // give each thread a name, thread 1,2,3
            value.putString("name", "Thread" + i);

            result.put("partition" + i, value);

            fromId = toId + 1;
            toId += range;


        }

        return result;
    }

和我的读者二传手 公共类readeretter实现PreparedStatementSetter {

private int fid;
private int tid;
public int getFid() {
    return fid;
}
public void setFid(int fid) {
    this.fid = fid;
}
public int getTid() {
    return tid;
}
public void setTid(int tid) {
    this.tid = tid;
}
@Override
public void setValues(PreparedStatement se) throws SQLException {
    // TODO Auto-generated method stub
    se.setInt(1,fid);
    se.setInt(2,tid);


}

但是对于网格尺寸1以上工作正常,当我增加尺寸时它不起作用,请帮助解决这个问题......

当我增加网格大小时,这就是面对的问题

开始:Thread1 开始:1 结束:110

开始:Thread2 开始:111 结束:220

开始:Thread3 开始:221 结束:330

开始:Thread4 开始:331 结束:440

开始:Thread5 开始:441 结束:550

错误 PreparedStatementCallback; SQL [INSERT INTO writer VALUES(?)];键'PRIMARY'重复输入'331';嵌套异常是java.sql.BatchUpdateException:键'PRIMARY'的重复条目'331'

就像每次都得到相同的错误一样,访问DB时其他线程范围也会改变....

Rowmapper和WriterSetter的Sysout 开始:Thread1 fromId:1 toId: 220

开始:Thread2 fromId:221 toId: 440

row mapper221

row mapper1

writer1

writer1

row mapper2

writer2

row mapper3

1 个答案:

答案 0 :(得分:0)

我认为问题出在您的分区逻辑中。请使用以下内容更改您的for循环。

for (int i = 1; i <= gridSize; i++) {

            toId = fromId + blocksize;
            ExecutionContext value = new ExecutionContext();


            if (fromId > totalRecords) {
                break;
            }

            if (toId > totalRecords) {
                toId = totalRecords;
                System.out.println("\nStarting : Thread" + i);
                System.out.println("fromId : " + fromId);
                System.out.println("toId : " + toId);

                value.putDouble("fromId", fromId);
                value.putDouble("toId", toId);
                // give each thread a name, thread 1,2,3
                value.putString("name", "Thread" + i);

                result.put("partition" + i, value);
                break;
            } else {
                System.out.println("\nStarting : Thread" + i);
                System.out.println("fromId : " + fromId);
                System.out.println("toId : " + toId);

                value.putDouble("fromId", fromId);
                value.putDouble("toId", toId);


                value.putString("name", "Thread" + i);

                result.put("partition" + i, value);

                fromId = toId + 1;
            }

        }