当我们从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配置:
<?xml version="1.0" encoding="UTF-8"?>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource"/> <!-- set in another xml using org.apache.commons.dbcp.BasicDataSource -->
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.id.new_generator_mappings">true</prop>
<!-- -->
<prop key="hibernate.jdbc.batch_size">1000</prop>
<prop key="hibernate.jdbc.fetch_size">1000</prop>
<prop key="hibernate.order_inserts">true</prop>
<prop key="hibernate.order_updates">true</prop>
</props>
</property>
<property name="annotatedClasses">
<list>
<value>com.company.ParentTable</value>
<value>com.company.ChildTable</value>
</list>
</property>
</bean>
<bean id="transactionTemplate"
class="org.springframework.transaction.support.TransactionTemplate"
p:isolationLevelName="ISOLATION_READ_COMMITTED"
p:propagationBehaviorName="PROPAGATION_REQUIRES_NEW"
p:transactionManager-ref="transactionManager"
/>
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- Spring transaction management -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate5.HibernateTransactionManager"
p:sessionFactory-ref="sessionFactory"
/>
5) Spring批配置
<?xml version="1.0" encoding="UTF-8"?>
<batch:job id="loaderJob" job-repository="jobRepository">
<batch:step id="initLoader" next="pipeline">
<batch:tasklet transaction-manager="transactionManager">
<bean class="com.company.InitialLoaderTask" scope="step">
<constructor-arg name="dao" ref="dao"/>
<constructor-arg name="filter" value="#{jobParameters['filter']}"/>
</bean>
</batch:tasklet>
</batch:step>
<batch:step id="pipeline">
<batch:tasklet transaction-manager="transactionManager" task-executor="loader_multi_thread_taskExecutor" throttle-limit="50">
<batch:chunk
reader="reader"
writer="processorAndWriter"
commit-interval="1000">
</batch:tasklet>
</batch:step>
</batch:job>
<bean id="reader" class="com.company.SynchronizedItemStreamReader" scope="step" > <!-- Custom Synchrnized Item Reader -->
<constructor-arg name="delegate" ref="#{ jobExecutionContext['region'] ? 'hibernateReader':'hibernateReader2' }"/> <!-- hibernateReader2 is same as hibernateReader but with more params -->
</bean>
<bean id="hibernateReader"
class="org.springframework.batch.item.database.HibernateCursorItemReader"
scope="step" >
<property name="sessionFactory" ref="sessionFactory"/>
<property name="queryName" value="ParentTable.query1"/>
<property name="useStatelessSession" value="false"/>
<property name="saveState" value="false"/>
<property name="fetchSize" value="10000"/>
<property name="parameterValues">
<map>
<entry key="param1" value="#{jobExecutionContext['param1']}"/>
<entry key="param2" value="#{jobExecutionContext['param2']}"/>
</map>
</property>
</bean>
<bean id="processorAndWriter"
class="com.company.LoaderAndWriter"
>
<constructor-arg name="generator" ref="processor"/>
<constructor-arg name="writer" ref="hibernateWriter"/>
</bean>
<bean id="processor" class="org.springframework.batch.item.support.CompositeItemProcessor" >
<property name="delegates">
<list>
<bean class="com.company.Generator" scope="step">
<constructor-arg name="param1" value="#{jobExecutionContext['param1']}"/>
</bean>
<bean class="com.company.Processor" scope="step">
<constructor-arg name="param3" value="#{jobExecutionContext['param3']}"/>
</bean>
</list>
</property>
</bean>
<bean id="hibernateWriter" class="org.springframework.batch.item.database.HibernateItemWriter">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="loader_multi_thread_taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="25"/>
<property name="maxPoolSize" value="50"/>
</bean>
6) 实体对象
@Entity
@Table(name = "PARENT_TABLE")
@NamedNativeQueries({
@NamedNativeQuery(
name = "ParentTable.query1",
query = "SELECT * FROM PARENT_TABLE where param = :param1",
resultClass = ParentTable.class
)
})
@BatchSize(size = 1000)
public class ParentTable extends PersistentEntity<Long> {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "PARENT_TABLE_SEQ")
@SequenceGenerator(name = "PARENT_TABLE_SEQ", sequenceName = "PARENT_TABLE_SEQ", allocationSize=5)
private Long id;
@Column
private Long field1;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "fieldId", fetch = FetchType.EAGER)
@BatchSize(size = 1000)
private List<ChildTable> childTable;
}
@Entity
@Table(name = "CHILD_TABLE")
@BatchSize(size = 1000)
public class ChildTable extends PersistentEntity<Long> {
private static final long serialVersionUID = 1L;
@Id
private Long id;
@ManyToOne
@JoinColumn(name = "field1")
private ParentTable parentData;
@Column
private String field2;
}
7) 从parentdata列表访问子表数据时引发错误的生成器类
public class Generator implements ItemProcessor<List<? extends ParentTable>, RequestPojoBean> {
private final int param1;
public Generator(int param1) {
this.param1 = param1;
}
@Override
public RequestPojoBean process(List<? extends ParentTable> parentDataList) throws Exception {
RequestPojoBean result = new RequestPojoBean();
result.setParentDataList(convertToRequestList(parentDataList));
result.setparam1(param1);
return result;
}
private List<ParentRequestPojoBean> convertToRequestList(List<? extends ParentTable> parentDataList) {
List<ParentRequestPojoBean> parentRequestBean = new ArrayList<>();
for (ParentTable parentData : parentDataList) {
LOGGER.info("Parent detail " + parentData.getField1());
ParentRequestPojoBean bean1 = new ParentRequestPojoBean();
bean1.setSomeData(callToSomeServiceViaHibernate(parentData.somedata, param1));
// attach References if any
List<RequestPojoBeanChildData> childList = new ArrayList<>();
for (ChildTable child : parentData.getChildTable()) { *****<--- Exception is thrown at this line*****
RequestPojoBeanChildData childPojoData = new RequestPojoBeanChildData();
childPojoData.setField2(child.getField2());
childList.add(childPojoData);
}
bean1.setChildData(childList);
parentRequestBean.add(bean1);
}
return parentRequestBean;
}
}
8) 异常堆栈跟踪:
2021-04-12 13:33:42,548 ERROR [main] [org.springframework.batch.core.step.AbstractStep] - <Encountered an error executing step pipeline in job loaderJob>
java.util.ConcurrentModificationException
at java.util.LinkedHashMap$LinkedHashIterator.nextNode(LinkedHashMap.java:719)
at java.util.LinkedHashMap$LinkedEntryIterator.next(LinkedHashMap.java:752)
at java.util.LinkedHashMap$LinkedEntryIterator.next(LinkedHashMap.java:750)
at org.hibernate.engine.spi.BatchFetchQueue.getCollectionBatch(BatchFetchQueue.java:311)
at org.hibernate.loader.collection.plan.LegacyBatchingCollectionInitializerBuilder$LegacyBatchingCollectionInitializer.initialize(LegacyBatchingCollectionInitializerBuilder.java:79)
at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:710)
at org.hibernate.event.internal.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:76)
at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:93)
at org.hibernate.internal.SessionImpl.initializeCollection(SessionImpl.java:2163)
at org.hibernate.collection.internal.AbstractPersistentCollection$4.doWork(AbstractPersistentCollection.java:589)
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:264)
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:585)
at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:149)
at org.hibernate.collection.internal.PersistentBag.iterator(PersistentBag.java:387)
作为迁移的一部分,我更新了
休眠版本
hibernate5.localsessionfactorybean
休眠5.hibernatetransactionmanager
如果我为hibernate4恢复上面提到的更改,代码在多线程池中运行良好,但会抛出带有上述更新的concurrentmodificationexception。
我肯定我错过了一些非常愚蠢的东西,或者一些需要作为Hibernate5迁移的一部分添加的设置。
任何建议都将不胜感激。
提前谢谢。
1条答案
按热度按时间wwtsj6pe1#
hibernate会话和从该会话加载的对象一起不是线程安全的。因此,不能从会话加载对象,然后在不同的线程中使用这些对象。
在其他问题中,这些对象可能会触发延迟集合/代理的初始化,这最终会落在底层jdbc连接上,并且该连接也不是线程安全的。
您需要给每个线程分配它自己的会话,并且不能在线程之间共享从会话加载的对象