Search code examples
javahibernatejpaejbjta

Save @Entity using @EntityManager not working


Trying to persist an entity is not working but retrieving a list is.

Below is my Bean: (a)

@Stateless
public class UsersClass {
    @PersistenceContext(unitName = "unit")
    private EntityManager em;

    public UsersClass () {}

    public void create(Users entity) {
        em.persist(entity);
    }
}

persistence.xml as below:

<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence   http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">

<persistence-unit name="unit" transaction-type="JTA">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>

<jta-data-source>jdbc/ds</jta-data-source> 
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
</properties>
</persistence-unit>
</persistence>

Beans have to be @Stateless. Above, setup executes fine - no exception or anything but nothing gets saved in DB also.

Tried whats mentioned here. Not sure if I did it correctly or not but was getting exception.

I also tried whats mentioned here and modified as below:(b)

@Stateless
public class UsersClass {
    @PersistenceContext(unitName = "unit")
    private EntityManager em;

    @Resource
    private SessionContext sessionContext;

    public UsersClass () {}

    public void create(Users entity) {
        UserTransaction userTxn = sessionContext.getUserTransaction();
        try {
             userTxn.begin();
             getEntityManager().persist(entity);
             userTxn.commit();
        } catch(Throwable e){
            e.printStackTrace();
            try {
              userTxn.rollback();
            } catch (IllegalStateException | SecurityException | SystemException e1) {
                e1.printStackTrace();
            } 
        }
    }
}

but got the below stack trace -

Caused by: java.lang.IllegalStateException: Only session beans with bean-managed transactions can obtain UserTransaction
at com.sun.ejb.containers.EJBContainerTransactionManager.getUserTransaction(EJBContainerTransactionManager.java:566)
at com.sun.ejb.containers.BaseContainer.getUserTransaction(BaseContainer.java:995)
at com.sun.ejb.containers.AbstractSessionContextImpl.getUserTransaction(AbstractSessionContextImpl.java:120)

So as per my readings, I thought adding @TransactionManagement(TransactionManagementType.BEAN) to my class should help. Indeed, the exception is gone but nothing get persisted in the db.

Have also tried - entitymanager.getTransaction().begin() and commit() but there I get the below stack-trace

Caused by: java.lang.IllegalStateException: A JTA EntityManager cannot use getTransaction()
at org.hibernate.internal.AbstractSharedSessionContract.getTransaction(AbstractSharedSessionContract.java:360)
at org.hibernate.internal.AbstractSessionImpl.getTransaction(AbstractSessionImpl.java:23)
at com.sun.enterprise.container.common.impl.EntityManagerWrapper.getTransaction(EntityManagerWrapper.java:806)

It would really help me if someone can point out the missing piece of code.

Update 1 -

I have tried the below set of changes also with (b)

 userTxn.begin();
 getEntityManager().joinTransaction();
 getEntityManager().persist(entity);
 userTxn.commit();

Am getting below stack-trace -

org.hibernate.resource.transaction.backend.jta.internal.JtaPlatformInaccessibleException: Unable to access TransactionManager or UserTransaction to make physical transaction delegate
at org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorImpl.makePhysicalTransactionDelegate(JtaTransactionCoordinatorImpl.java:229)
at org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorImpl.getTransactionDriverControl(JtaTransactionCoordinatorImpl.java:203)
at org.hibernate.engine.transaction.internal.TransactionImpl.<init>(TransactionImpl.java:37)
at org.hibernate.internal.AbstractSharedSessionContract.accessTransaction(AbstractSharedSessionContract.java:372)
at org.hibernate.internal.AbstractSharedSessionContract.markForRollbackOnly(AbstractSharedSessionContract.java:342)
at org.hibernate.internal.ExceptionConverterImpl.handlePersistenceException(ExceptionConverterImpl.java:271)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:148)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:155)
at org.hibernate.internal.SessionImpl.joinTransaction(SessionImpl.java:3736)
at org.hibernate.internal.SessionImpl.joinTransaction(SessionImpl.java:3718)
at com.sun.enterprise.container.common.impl.EntityManagerWrapper.joinTransaction(EntityManagerWrapper.java:990)

Solution

    1. Container Managed Transaction

    This seems to be OK, your create method in your Users stateless bean starts the transaction automatically. You entityManager takes part in the transaction and the changes should be persisted to the Database. What was the exception in this case?

    1. Container Managed Transaction and try to start a JTA transaction

    Your create method starts a transaction automatically, you tried to start another JTA transaction in the code and you got a 'IllegalStateException'. If you want to take control of the transaction in the code, change the configuration to Bean Managed Transaction.

    1. Bean Managed Transaction

    You are controlling the Transaction in the code. The code doesn't throw any exception but the changes are not being persisted. This is because the EntityManager is not joining the transaction. If you have an EntityManager created before the start of the JTA transaction, you need to call the method joinTransaction() to make the EntityManager to join the transaction:

    userTxn.begin();
    EntityManager em = getEntityManager();
    em.joinTransaction();
    em.persist(entity);
    userTxn.commit();