7

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.

vels4j
  • 11,208
  • 5
  • 38
  • 63
  • Do you have enough open connections to database? Connection-pool? – tak3shi Jun 14 '17 at 08:53
  • @tak3shi used `dbcp2.BasicDataSource` with default connection pool size – vels4j Jun 14 '17 at 09:53
  • Row locks observed on the database table? – harshavmb Jun 15 '17 at 08:16
  • @harshavmb Table lock – vels4j Jun 15 '17 at 09:13
  • 1
    Table lock, Ideally we shouldn't see table locks held by application as they shouldn't run DDL operations. Any DDL operations running like backup, index creation on the database and held table locks? – harshavmb Jun 15 '17 at 09:27
  • 1
    You haven't shown what you are doing at all within this transaction. Turn on logging in EclipseLink to see the SQL it is issuing before the lock timeout, and check your methods, as we can't help without you showing what you are doing in createNewRWRetailTransaction and any other calls that use JPA. – Chris Jun 15 '17 at 13:42
  • @Chris sorry, I missed something while posting mvce, just updated – vels4j Jun 16 '17 at 15:26
  • We need eclipselink logging set to fine or higher to see the sql issued. The only logging regarding transactions is your own, so it's hard to say how the transaction is demarcated and when it is committed. A stack trace on the error might help too – Chris Jun 19 '17 at 12:38
  • @Chris RWTransactionDao is a singleton and `EntityManager` is sharing commonly for all the request. may that causes issue ? – vels4j Jun 19 '17 at 20:49
  • 1
    Spring should be reusing the same EntityManager and database connection, so it shouldn't be locking on itself - but I'm not familiar with Spring, and you haven't shown any logs so I can't say. Even the error stack trace might tell you where the error is coming from, and point to why it is rolling back after your method thinks it has returned successfully. By the way, what is the RWCompartmentRestExceptionMapper class catching this exception? – Chris Jun 20 '17 at 13:52
  • as @Chris mentions, stack trace can tell a lot. please print the stack trace and paste it here. – prem kumar Jun 21 '17 at 07:22
  • @Chris I've resolved the issue as I posted the answer. Ton thanks for your comments – vels4j Jun 24 '17 at 07:52

2 Answers2

3

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.

Liping Huang
  • 4,378
  • 4
  • 29
  • 46
  • Both peTxn and FcTxn must be committed together. we dont use refresh during persist – vels4j Jun 19 '17 at 05:28
  • @vels4j You can have a try, if the solution really fix your problem, then next, you can add extra codes to keep the *consistency* – Liping Huang Jun 19 '17 at 05:37
  • The 'transaction' here seems to be an entity, and the code is trying to insert two entities in the same DB transaction. This should be fine if it is hooked up correctly – Chris Jun 19 '17 at 12:40
1

The problem was due to PersistenceContextType.EXTENDED. I did two changes and problem got resolved.

  1. PersistenceContextType.EXTENDED changed to PersistenceContextType.TRANSACTION
  2. @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);
        }
    
vels4j
  • 11,208
  • 5
  • 38
  • 63