当hibernate从4.x升级到5.x和多线程的Spring4.x时,concurrentmodificationexception

trnvg8h3  于 2021-07-13  发布在  Java
关注(0)|答案(1)|浏览(440)

当我们从Hibernate4迁移到5时,我遇到了一个问题。我已经挣扎了一个星期,阅读了大量的博客和网页,但无法解决。以下是关于issue:-
1) 休眠版本:从:4.3.11.final到:5.4.28.final
2) spring orm版本:4.3.29.release
3) spring批处理基础设施/核心:3.0.10.release
4) 休眠xml配置:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <bean id="sessionFactory"
  3. class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
  4. <property name="dataSource">
  5. <ref bean="dataSource"/> <!-- set in another xml using org.apache.commons.dbcp.BasicDataSource -->
  6. </property>
  7. <property name="hibernateProperties">
  8. <props>
  9. <prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
  10. <prop key="hibernate.show_sql">true</prop>
  11. <prop key="hibernate.id.new_generator_mappings">true</prop>
  12. <!-- -->
  13. <prop key="hibernate.jdbc.batch_size">1000</prop>
  14. <prop key="hibernate.jdbc.fetch_size">1000</prop>
  15. <prop key="hibernate.order_inserts">true</prop>
  16. <prop key="hibernate.order_updates">true</prop>
  17. </props>
  18. </property>
  19. <property name="annotatedClasses">
  20. <list>
  21. <value>com.company.ParentTable</value>
  22. <value>com.company.ChildTable</value>
  23. </list>
  24. </property>
  25. </bean>
  26. <bean id="transactionTemplate"
  27. class="org.springframework.transaction.support.TransactionTemplate"
  28. p:isolationLevelName="ISOLATION_READ_COMMITTED"
  29. p:propagationBehaviorName="PROPAGATION_REQUIRES_NEW"
  30. p:transactionManager-ref="transactionManager"
  31. />
  32. <tx:annotation-driven transaction-manager="transactionManager"/>
  33. <!-- Spring transaction management -->
  34. <bean id="transactionManager"
  35. class="org.springframework.orm.hibernate5.HibernateTransactionManager"
  36. p:sessionFactory-ref="sessionFactory"
  37. />

5) Spring批配置

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <batch:job id="loaderJob" job-repository="jobRepository">
  3. <batch:step id="initLoader" next="pipeline">
  4. <batch:tasklet transaction-manager="transactionManager">
  5. <bean class="com.company.InitialLoaderTask" scope="step">
  6. <constructor-arg name="dao" ref="dao"/>
  7. <constructor-arg name="filter" value="#{jobParameters['filter']}"/>
  8. </bean>
  9. </batch:tasklet>
  10. </batch:step>
  11. <batch:step id="pipeline">
  12. <batch:tasklet transaction-manager="transactionManager" task-executor="loader_multi_thread_taskExecutor" throttle-limit="50">
  13. <batch:chunk
  14. reader="reader"
  15. writer="processorAndWriter"
  16. commit-interval="1000">
  17. </batch:tasklet>
  18. </batch:step>
  19. </batch:job>
  20. <bean id="reader" class="com.company.SynchronizedItemStreamReader" scope="step" > <!-- Custom Synchrnized Item Reader -->
  21. <constructor-arg name="delegate" ref="#{ jobExecutionContext['region'] ? 'hibernateReader':'hibernateReader2' }"/> <!-- hibernateReader2 is same as hibernateReader but with more params -->
  22. </bean>
  23. <bean id="hibernateReader"
  24. class="org.springframework.batch.item.database.HibernateCursorItemReader"
  25. scope="step" >
  26. <property name="sessionFactory" ref="sessionFactory"/>
  27. <property name="queryName" value="ParentTable.query1"/>
  28. <property name="useStatelessSession" value="false"/>
  29. <property name="saveState" value="false"/>
  30. <property name="fetchSize" value="10000"/>
  31. <property name="parameterValues">
  32. <map>
  33. <entry key="param1" value="#{jobExecutionContext['param1']}"/>
  34. <entry key="param2" value="#{jobExecutionContext['param2']}"/>
  35. </map>
  36. </property>
  37. </bean>
  38. <bean id="processorAndWriter"
  39. class="com.company.LoaderAndWriter"
  40. >
  41. <constructor-arg name="generator" ref="processor"/>
  42. <constructor-arg name="writer" ref="hibernateWriter"/>
  43. </bean>
  44. <bean id="processor" class="org.springframework.batch.item.support.CompositeItemProcessor" >
  45. <property name="delegates">
  46. <list>
  47. <bean class="com.company.Generator" scope="step">
  48. <constructor-arg name="param1" value="#{jobExecutionContext['param1']}"/>
  49. </bean>
  50. <bean class="com.company.Processor" scope="step">
  51. <constructor-arg name="param3" value="#{jobExecutionContext['param3']}"/>
  52. </bean>
  53. </list>
  54. </property>
  55. </bean>
  56. <bean id="hibernateWriter" class="org.springframework.batch.item.database.HibernateItemWriter">
  57. <property name="sessionFactory" ref="sessionFactory"/>
  58. </bean>
  59. <bean id="loader_multi_thread_taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
  60. <property name="corePoolSize" value="25"/>
  61. <property name="maxPoolSize" value="50"/>
  62. </bean>

6) 实体对象

  1. @Entity
  2. @Table(name = "PARENT_TABLE")
  3. @NamedNativeQueries({
  4. @NamedNativeQuery(
  5. name = "ParentTable.query1",
  6. query = "SELECT * FROM PARENT_TABLE where param = :param1",
  7. resultClass = ParentTable.class
  8. )
  9. })
  10. @BatchSize(size = 1000)
  11. public class ParentTable extends PersistentEntity<Long> {
  12. private static final long serialVersionUID = 1L;
  13. @Id
  14. @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "PARENT_TABLE_SEQ")
  15. @SequenceGenerator(name = "PARENT_TABLE_SEQ", sequenceName = "PARENT_TABLE_SEQ", allocationSize=5)
  16. private Long id;
  17. @Column
  18. private Long field1;
  19. @OneToMany(cascade = CascadeType.ALL, mappedBy = "fieldId", fetch = FetchType.EAGER)
  20. @BatchSize(size = 1000)
  21. private List<ChildTable> childTable;
  22. }
  23. @Entity
  24. @Table(name = "CHILD_TABLE")
  25. @BatchSize(size = 1000)
  26. public class ChildTable extends PersistentEntity<Long> {
  27. private static final long serialVersionUID = 1L;
  28. @Id
  29. private Long id;
  30. @ManyToOne
  31. @JoinColumn(name = "field1")
  32. private ParentTable parentData;
  33. @Column
  34. private String field2;
  35. }

7) 从parentdata列表访问子表数据时引发错误的生成器类

  1. public class Generator implements ItemProcessor<List<? extends ParentTable>, RequestPojoBean> {
  2. private final int param1;
  3. public Generator(int param1) {
  4. this.param1 = param1;
  5. }
  6. @Override
  7. public RequestPojoBean process(List<? extends ParentTable> parentDataList) throws Exception {
  8. RequestPojoBean result = new RequestPojoBean();
  9. result.setParentDataList(convertToRequestList(parentDataList));
  10. result.setparam1(param1);
  11. return result;
  12. }
  13. private List<ParentRequestPojoBean> convertToRequestList(List<? extends ParentTable> parentDataList) {
  14. List<ParentRequestPojoBean> parentRequestBean = new ArrayList<>();
  15. for (ParentTable parentData : parentDataList) {
  16. LOGGER.info("Parent detail " + parentData.getField1());
  17. ParentRequestPojoBean bean1 = new ParentRequestPojoBean();
  18. bean1.setSomeData(callToSomeServiceViaHibernate(parentData.somedata, param1));
  19. // attach References if any
  20. List<RequestPojoBeanChildData> childList = new ArrayList<>();
  21. for (ChildTable child : parentData.getChildTable()) { *****<--- Exception is thrown at this line*****
  22. RequestPojoBeanChildData childPojoData = new RequestPojoBeanChildData();
  23. childPojoData.setField2(child.getField2());
  24. childList.add(childPojoData);
  25. }
  26. bean1.setChildData(childList);
  27. parentRequestBean.add(bean1);
  28. }
  29. return parentRequestBean;
  30. }
  31. }

8) 异常堆栈跟踪:

  1. 2021-04-12 13:33:42,548 ERROR [main] [org.springframework.batch.core.step.AbstractStep] - <Encountered an error executing step pipeline in job loaderJob>
  2. java.util.ConcurrentModificationException
  3. at java.util.LinkedHashMap$LinkedHashIterator.nextNode(LinkedHashMap.java:719)
  4. at java.util.LinkedHashMap$LinkedEntryIterator.next(LinkedHashMap.java:752)
  5. at java.util.LinkedHashMap$LinkedEntryIterator.next(LinkedHashMap.java:750)
  6. at org.hibernate.engine.spi.BatchFetchQueue.getCollectionBatch(BatchFetchQueue.java:311)
  7. at org.hibernate.loader.collection.plan.LegacyBatchingCollectionInitializerBuilder$LegacyBatchingCollectionInitializer.initialize(LegacyBatchingCollectionInitializerBuilder.java:79)
  8. at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:710)
  9. at org.hibernate.event.internal.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:76)
  10. at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:93)
  11. at org.hibernate.internal.SessionImpl.initializeCollection(SessionImpl.java:2163)
  12. at org.hibernate.collection.internal.AbstractPersistentCollection$4.doWork(AbstractPersistentCollection.java:589)
  13. at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:264)
  14. at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:585)
  15. at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:149)
  16. at org.hibernate.collection.internal.PersistentBag.iterator(PersistentBag.java:387)

作为迁移的一部分,我更新了
休眠版本
hibernate5.localsessionfactorybean
休眠5.hibernatetransactionmanager
如果我为hibernate4恢复上面提到的更改,代码在多线程池中运行良好,但会抛出带有上述更新的concurrentmodificationexception。
我肯定我错过了一些非常愚蠢的东西,或者一些需要作为Hibernate5迁移的一部分添加的设置。
任何建议都将不胜感激。
提前谢谢。

wwtsj6pe

wwtsj6pe1#

hibernate会话和从该会话加载的对象一起不是线程安全的。因此,不能从会话加载对象,然后在不同的线程中使用这些对象。
在其他问题中,这些对象可能会触发延迟集合/代理的初始化,这最终会落在底层jdbc连接上,并且该连接也不是线程安全的。
您需要给每个线程分配它自己的会话,并且不能在线程之间共享从会话加载的对象

相关问题