mon
mon

Reputation: 22326

Reason of the OpenJPA Error in Web - You cannot access the EntityTransaction when using managed transactions

Objective

To confirm the cause is because of trying to use the Container Managed Transaction in the Web Container, and check if my understanding is correct or not.

(In the example code below, a entity manager (not thread safe) is injected into a servlet instance variable which will cause multi thread issues but it is for testing sake)

Issue

Trying to port the developerWorks example into IBM WAS. Trying to use the Entity Manager em and start a transaction with em.getTransaction().begin() and got the error.

[27/09/15 0:28:33:717 AEST] 00000090 SystemErr     
R <openjpa-2.2.3-SNAPSHOT-r422266:1686911 nonfatal user error>
org.apache.openjpa.persistence.InvalidStateException: 
You cannot access the EntityTransaction when using managed transactions.

[27/09/15 0:28:33:719 AEST] 00000090 SystemErr     R    at org.apache.openjpa.persistence.EntityManagerImpl.getTransaction(EntityManagerImpl.java:552)
[27/09/15 0:28:33:719 AEST] 00000090 SystemErr     R    at org.apache.openjpa.persistence.EntityManagerImpl.getTransaction(EntityManagerImpl.java:102)
[27/09/15 0:28:33:720 AEST] 00000090 SystemErr     R    at com.ibm.ws.jpa.management.JPAExEmInvocation.getTransaction(JPAExEmInvocation.java:263)
[27/09/15 0:28:33:720 AEST] 00000090 SystemErr     R    at com.ibm.ws.jpa.management.JPAEntityManager.getTransaction(JPAEntityManager.java:421)
[27/09/15 0:28:33:721 AEST] 00000090 SystemErr     R    at sample.jpa.servlet.CreateAccount.doPost(CreateAccount.java:35)
[27/09/15 0:28:33:721 AEST] 00000090 SystemErr     R    at javax.servlet.http.HttpServlet.service(HttpServlet.java:595)
[27/09/15 0:28:33:722 AEST] 00000090 SystemErr     R    at javax.servlet.http.HttpServlet.service(HttpServlet.java:668)
[27/09/15 0:28:33:722 AEST] 00000090 SystemErr     R    at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1232)
[27/09/15 0:28:33:722 AEST] 00000090 SystemErr     R    at com.ibm.ws.webcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:781)
[27/09/15 0:28:33:724 AEST] 00000090 SystemErr     R    at com.ibm.ws.webcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:480)
[27/09/15 0:28:33:725 AEST] 00000090 SystemErr     R    at com.ibm.ws.webcontainer.servlet.ServletWrapperImpl.handleRequest(ServletWrapperImpl.java:178)
[27/09/15 0:28:33:725 AEST] 00000090 SystemErr     R    at com.ibm.ws.webcontainer.filter.WebAppFilterManager.invokeFilters(WebAppFilterManager.java:1114)
[27/09/15 0:28:33:726 AEST] 00000090 SystemErr     R    at com.ibm.ws.webcontainer.servlet.CacheServletWrapper.handleRequest(CacheServletWrapper.java:87)
[27/09/15 0:28:33:726 AEST] 00000090 SystemErr     R    at com.ibm.ws.webcontainer.WebContainer.handleRequest(WebContainer.java:940)
[27/09/15 0:28:33:727 AEST] 00000090 SystemErr     R    at com.ibm.ws.webcontainer.WSWebContainer.handleRequest(WSWebContainer.java:1817)
[27/09/15 0:28:33:727 AEST] 00000090 SystemErr     R    at com.ibm.ws.webcontainer.channel.WCChannelLink.ready(WCChannelLink.java:200)
[27/09/15 0:28:33:728 AEST] 00000090 SystemErr     R    at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.handleDiscrimination(HttpInboundLink.java:463)
[27/09/15 0:28:33:728 AEST] 00000090 SystemErr     R    at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.handleNewRequest(HttpInboundLink.java:530)
[27/09/15 0:28:33:729 AEST] 00000090 SystemErr     R    at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.processRequest(HttpInboundLink.java:316)
[27/09/15 0:28:33:729 AEST] 00000090 SystemErr     R    at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.ready(HttpInboundLink.java:287)
[27/09/15 0:28:33:729 AEST] 00000090 SystemErr     R    at com.ibm.ws.tcp.channel.impl.NewConnectionInitialReadCallback.sendToDiscriminators(NewConnectionInitialReadCallback.java:214)
[27/09/15 0:28:33:730 AEST] 00000090 SystemErr     R    at com.ibm.ws.tcp.channel.impl.NewConnectionInitialReadCallback.complete(NewConnectionInitialReadCallback.java:113)
[27/09/15 0:28:33:730 AEST] 00000090 SystemErr     R    at com.ibm.ws.tcp.channel.impl.AioReadCompletionListener.futureCompleted(AioReadCompletionListener.java:175)
[27/09/15 0:28:33:731 AEST] 00000090 SystemErr     R    at com.ibm.io.async.AbstractAsyncFuture.invokeCallback(AbstractAsyncFuture.java:217)
[27/09/15 0:28:33:731 AEST] 00000090 SystemErr     R    at com.ibm.io.async.AsyncChannelFuture.fireCompletionActions(AsyncChannelFuture.java:161)
[27/09/15 0:28:33:732 AEST] 00000090 SystemErr     R    at com.ibm.io.async.AsyncFuture.completed(AsyncFuture.java:138)
[27/09/15 0:28:33:732 AEST] 00000090 SystemErr     R    at com.ibm.io.async.ResultHandler.complete(ResultHandler.java:204)
[27/09/15 0:28:33:732 AEST] 00000090 SystemErr     R    at com.ibm.io.async.ResultHandler.runEventProcessingLoop(ResultHandler.java:775)
[27/09/15 0:28:33:733 AEST] 00000090 SystemErr     R    at com.ibm.io.async.ResultHandler$2.run(ResultHandler.java:905)
[27/09/15 0:28:33:733 AEST] 00000090 SystemErr     R    at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:1881)

persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0"
    xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
    <persistence-unit name="DB2AccountUnit" transaction-type="JTA">
        <jta-data-source>jdbc/DB2</jta-data-source>
        <class>sample.jpa.Db2account</class>
        <properties>
            <property name="openjpa.Log" value="File=C:/opt/openjpa/org.apache.openjpa.log, DefaultLevel=INFO, Runtime=TRACE, Tool=INFO, SQL=TRACE, JDBC=TRACE" />
            <property name="openjpa.ConnectionUserName" value="User" />
            <property name="openjpa.ConnectionPassword" value="Password" />
        </properties>
    </persistence-unit>
</persistence>

Code

package sample.jpa.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.math.BigDecimal;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;

import javax.persistence.PersistenceContext;
import javax.persistence.EntityManager;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;
import javax.transaction.SystemException;
import javax.naming.InitialContext;

import sample.jpa.AccountException;
import sample.jpa.Db2account;

@WebServlet("/CreateAccount")
public class CreateAccount extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {
    private static final long serialVersionUID = 1L;

    @PersistenceContext(unitName="DB2AccountUnit")
    EntityManager em;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }

    synchronized protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        InitialContext context = null;
        UserTransaction ut = null;

        try {
            em.getTransaction().begin(); // <---- Error

            long accno = Long.parseLong(request.getParameter("ACCNO"));
            Db2account account1 = em.find(Db2account.class, accno);

            if (account1 != null) {
                throw new AccountException("Error : sample.jpa.servlet.CreateAccount : Account Number already exists:" + accno);
            } else {
                SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy");
                java.sql.Date dbDate = new java.sql.Date(format.parse(request.getParameter("DOP"), new ParsePosition(0)).getTime());

                Db2account account = new Db2account();
                account.setOwner(request.getParameter("OWNERNAME"));
                account.setAccno(accno);
                account.setAccountType(request.getParameter("ACCOUNTTYPE"));
                account.setBalance(new BigDecimal(request.getParameter("BALANCE")));
                account.setDateOpen(dbDate);

                em.persist(account);
                em.getTransaction().commit();

                response.sendRedirect("Create_Account.jsp");
            }
        } catch (Exception e) {
            e.printStackTrace();
            em.getTransaction().rollback();
            try{
                ut.rollback();
            } catch (SystemException se){
                se.printStackTrace();
                printMessage(se, response.getWriter());
            }
        }
    }
}

Question

(Update: Found the understanding was completely wrong. Put self-answer)

Please advise my understanding below is correct or not.

My understanding is that the error is because the EntityTransaction provided from EntityManager.getTransaction() is to be used as the Container Managed Transaction (CMT) in the EJB Container but not in the Web Container.

The Web Container (I believe in general) does not support CMT, hence the error is caused. The message "You cannot access the EntityTransaction when using managed transactions" means 'You cannot use CMT/EntityTransaction where you need to use Bean Managed Transacdtion and javax.transaction.UserTransaction'.

Environment

IBM WAS 8.5.5.7 for Developers (Apache openjpa-2.2.3-SNAPSHOT-r422266)
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
Windows 7 64 bit on DELL Latitude core i7 2.8GHz 8G memory & SSD HDD

References


Fixed Code

package sample.jpa.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.math.BigDecimal;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;

import javax.persistence.PersistenceContext;
import javax.persistence.EntityManager;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;
import javax.transaction.SystemException;
import javax.naming.InitialContext;



import sample.jpa.AccountException;
import sample.jpa.Db2account;

@WebServlet("/CreateAccount")
public class CreateAccount extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {
    private static final long serialVersionUID = 1L;

    @PersistenceContext(unitName="DB2AccountUnit")
    EntityManager em;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }

    synchronized protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        InitialContext context = null;
        UserTransaction ut = null;

        try {
            context = new InitialContext();
            ut = (UserTransaction)context.lookup("java:comp/UserTransaction");

            if(ut == null){
                System.out.println("UserTransactin is NULL");
            }

//          em.getTransaction().begin();
            ut.begin();

            long accno = Long.parseLong(request.getParameter("ACCNO"));
            Db2account account1 = em.find(Db2account.class, accno);

            if (account1 != null) {
                throw new AccountException("Error : sample.jpa.servlet.CreateAccount : Account Number already exists:" + accno);
            } else {
                SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy");
                java.sql.Date dbDate = new java.sql.Date(format.parse(request.getParameter("DOP"), new ParsePosition(0)).getTime());

                Db2account account = new Db2account();
                account.setOwner(request.getParameter("OWNERNAME"));
                account.setAccno(accno);
                account.setAccountType(request.getParameter("ACCOUNTTYPE"));
                account.setBalance(new BigDecimal(request.getParameter("BALANCE")));
                account.setDateOpen(dbDate);

                em.persist(account);
//              em.getTransaction().commit();
                ut.commit();

                response.sendRedirect("Create_Account.jsp");
            }
        } catch (Exception e) {
            e.printStackTrace();
//          em.getTransaction().rollback();
            try{
                ut.rollback();
            } catch (SystemException se){
                se.printStackTrace();
            }
        }
    }
}

Upvotes: 0

Views: 3084

Answers (2)

mon
mon

Reputation: 22326

Did some more research and found the reason.

EntityTransaction is intended for Resource Local Use in a situation where there is no JTA is available such as standalone Java SE environment. The Entity Manager is container managed and under the control of the JTA transaction manager of the container.

So the error message should be meaning "You cannot use a transaction managed by a local resource manager within the transactions managed by the JTA transaction manager of the JEE container", I believe.

Fix

Created another persistent-unit DB2AccountUnitLocal with RESOURCE_LOCAL transaction_type.

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0"
    xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
    <persistence-unit name="DB2AccountUnit" transaction-type="JTA">
        <jta-data-source>jdbc/DB2XA</jta-data-source>
        <class>sample.jpa.Db2account</class>
        <properties>
            <property name="openjpa.Log" value="File=C:/opt/openjpa/org.apache.openjpa.log, DefaultLevel=INFO, Runtime=TRACE, Tool=INFO, SQL=TRACE, JDBC=TRACE" />
            <property name="openjpa.ConnectionUserName" value="User" />
            <property name="openjpa.ConnectionPassword" value="Password" />
        </properties>
    </persistence-unit>

    <persistence-unit name="DB2AccountUnitLocal" transaction-type="RESOURCE_LOCAL">
        <class>sample.jpa.Db2account</class>
        <properties>
            <property name="openjpa.ConnectionUserName" value="User" />
            <property name="openjpa.ConnectionPassword" value="Password" />
            <property name="openjpa.ConnectionURL" value="jdbc:oracle:thin:@localhost:1521:NR" />
            <property name="openjpa.ConnectionDriverName" value="oracle.jdbc.driver.OracleDriver" />
        </properties>
    </persistence-unit>
</persistence>

Changed the code to use the Application Managed Entity Manager and EntityTransaction from it.

package sample.jpa.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.math.BigDecimal;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceUnit;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import sample.jpa.Db2account;
import sample.jpa.AccountException;

@WebServlet("/CreateAccount")
public class CreateAccount extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {
    tatic final long serialVersionUID = 1L;

    //@PersistenceUnit(unitName="DB2AccountUnitLocal")
    private EntityManagerFactory emf;
    private EntityManager em = null;

   public void init(ServletConfig config){
       System.out.println("CreateAccount Servlet created..!");
       emf = Persistence.createEntityManagerFactory("DB2AccountUnitLocal");
       em = emf.createEntityManager(); 
   }       
   public void destroy(){
       System.out.println("CreateAccount Servlet destroyed..!");
       em.close();
       emf.close();
   }
    public CreateAccount() {
        super();
    }       

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }   

    synchronized protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        try{
            long accno = Long.parseLong(request.getParameter("ACCNO"));
            String ownerName = request.getParameter("OWNERNAME");
            String strDate = request.getParameter("DOP");

            long lgDate=new SimpleDateFormat("MM/dd/yyyy").parse(strDate,new ParsePosition(0)).getTime();
            java.sql.Date dbDate=new java.sql.Date(lgDate);

            String accType = request.getParameter("ACCOUNTTYPE");
            BigDecimal balance = new BigDecimal(request.getParameter("BALANCE"));

            em.getTransaction().begin();

            Db2account account1 = em.find(Db2account.class, accno);

            if(account1 != null){
                throw new AccountException("Error : sample.jpa.servlet.CreateAccount : Account Number already exists:"+accno);
            }
            Db2account account = new Db2account();

            account.setAccno(accno);
            account.setAccountType(accType);
            account.setBalance(balance);
            account.setDateOpen(dbDate);
            account.setOwner(ownerName);

            em.persist(account);
            em.getTransaction().commit();
        }
        catch (AccountException ae){
            ae.printStackTrace();
            em.getTransaction().rollback();
        }
        catch (Exception e){
            e.printStackTrace();
            em.getTransaction().rollback();
        }
    }       
}

Upvotes: 1

AsSiDe
AsSiDe

Reputation: 1846

you have to tell transaction-type in persistence.xml

<persistence-unit name="Tutorial" transaction-type="RESOURCE_LOCAL">

and change this

 <jta-data-source>jdbc/DB2</jta-data-source>

to

<non-jta-data-source>jdbc/DB2</non-jta-data-source>

This Question may help you:

JTA and Local Transaction

enter image description here

Upvotes: 0

Related Questions