Spring @Transactional commit failures ; Deby + Eclipselink
The following is the spring config
Date Source
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
<property name="driverClassName" value="${rwos.dataSource.driverClassName}" />
<property name="url" value="${rwos.dataSource.url}" />
<property name="username" value="${rwos.dataSource.user}" />
<property name="password" value="${rwos.dataSource.password}" />
</bean>
Entity manager config
<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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<bean id="persistenceUnitManager" class="org.springframework.data.jpa.support.MergingPersistenceUnitManager">
<property name="defaultDataSource" ref="dataSource"/>
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitManager" ref="persistenceUnitManager"/>
<property name="persistenceUnitName" value="com.retailwave.rwos_rwos-data-pojo_jar_4.0.0PU"/>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" lazy-init="true">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
</beans>
The following is the code snippet used to persist entity
@Singleton
@Component
public class RWTransactionDao {
@PersistenceContext(type = PersistenceContextType.EXTENDED)
private EntityManager em;
@Override
protected EntityManager getEntityManager() {
return em;
}
@Transactional
public void createOrderTxns(RWRetailTransaction peTxn, RWRetailTransaction fcTxn) {
create(peTxn);
create(fcTxn);
log.debug("Committed Transaction : {} ", peTxn.displayString());
log.debug("Committed Transaction : {} ", fcTxn.displayString());
}
@Transactional
public void create(T entity) {
getEntityManager().persist(entity);
}
}
Class :
@Component
@Path("/")
public class RWTransactionREST {
@Inject
private RWTransactionDao rWTransactionDao;
@POST
@Produces(MediaType.APPLICATION_JSON)
@Path("txns/sales")
public RWResponse createPurchaseTransaction(RWRetailTransaction peTxn, RWRetailTransaction fcTxn) {
rWTransactionDao.createOrderTxns(peTxn, fcTxn);
builder.status(RWStatus.OK);
RWResponse response = builder.build();
log.info("Returning.. {}", response);
return response;
}
}
Log message:
2017-06-14 10:49:51,453 DEBUG [qtp592179046-13] - Committed Transaction : RWRetailTransaction[609, CU_ORD, RTCO-609-17-11193, 2017-06-14 10:49:51.431]
2017-06-14 10:49:51,453 DEBUG [qtp592179046-13] - Committed Transaction : RWRetailTransaction[509, CU_ORD, RTCO-509-17-11193, 2017-06-14 10:49:51.444]
2017-06-14 10:49:51,463 INFO [qtp592179046-13] RWTransactionREST - Returning.. Response[1000:Order has been created successfully, Transaction Id : RTCO-609-17-11193]
After some time the following error occurred in the same RWTransactionDao in some other REST imple methods
2017-06-14 10:51:24,199 ERROR [qtp592179046-16] com.retailwave.rwos.compartment.rest.exception.RWCompartmentRestExceptionMapper - Exception caught at Mapper : Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.4.v20160829-44060b6): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLTransactionRollbackException: A lock could not be obtained within the time requested
Due to this error previous commit got rolled back but it not supposed to be.
Not sure what causes for this rollback.
Derby locks single rows for INSERT statements, holding each row until the transaction is committed. (If there is an index associated with the table, the previous key is also locked.)
So for your problem, my understanding is your tried to INSERT two records to Derby in one transaction, so when your execute create(peTxn);
the derby locks single rows and holding each row until the transaction is committed [ lockA ], then you try to execute create(fcTxn)
, it will try to obtain another single-row lock to INSERT a new record, but actually the transaction is not committed so the locks still holed [ lockA ].
So the solution is
call getEntityManager().refresh
the transaction to commit the transaction when the first step finished. this will focus the SQL INSERT to database.
and suggest to use @Transactional
in the service layer.
The problem was due to PersistenceContextType.EXTENDED
. I did two changes and problem got resolved.
PersistenceContextType.EXTENDED
changed to PersistenceContextType.TRANSACTION
@Singleton
changed to @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Component
public class RWTransactionDao {
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
private EntityManager em;
@Override
protected EntityManager getEntityManager() {
return em;
}
@Transactional
public void createOrderTxns(RWRetailTransaction peTxn, RWRetailTransaction fcTxn) {
create(peTxn);
create(fcTxn);
log.debug("Committed Transaction : {} ", peTxn.displayString());
log.debug("Committed Transaction : {} ", fcTxn.displayString());
}
@Transactional
public void create(T entity) {
getEntityManager().persist(entity);
}
上一篇: 我如何在Perl 6中重新分配一个对象?