Hibernate JPA with JTA and Glassfish Application Server doesn't seem to commit

12,265

Solution 1

In an application server, you can either have a container managed EntityManager (aka, a persistence context) or an application managed EntityManager. In both cases, you would have to associate the persistence context with a Transaction that is either a JTA Transaction, or plain JDBC transaction.

With respect to your problem at hand, the following points ought to be considered:

  • Your persistence.xml file indicates that you intend to use a JTA datasource and hence, JTA transactions to perform transactional work.
  • Also, your JpaUtil class is responsible for creating application-managed EntityManager instances.

In light of the above two statements and the behavior demonstrated by your application, it would appear that your EntityManager instance is not associated with a JTA transaction. Hence, any changes made in the persistence context, will simply not be flushed to the database. This is simply due to the fact that the JPA provider will rely on the JTA transaction and enlisted resources to perform the transactional work; if none are found, then no work will be done (unlike the case with Resource-Local transactions where the connection pool and resource enlistment is performed by the JPA provider itself).

The point therefore, is that the EntityManager or the persistence context must be associated with an active transaction, before you perform any changes to entities, so that all changes made to the entities in the persistence context can be flushed to the database. To resolve your problem, you must:

  • start a new JTA transaction. You could opt for either container managed transactions or application managed transactions.

    • Container managed transactions as the name states, are managed entirely by the container. You cannot invoke APIs to start and terminate such transactions. Instead, you must perform all transactional work within EJBs. The EJBs in question should be configured as ones requiring container managed transactions. Demonstrating the use of EJBs to perform transactional work in your scenario would be out of bounds of this answer. I would suggest reading up on EJBs if you haven't learn how to write one yet.
    • Application or Bean managed transactions are managed by the application itself and not by the container. While this might appear suitable in your case, do note that you are now responsible for transaction management; often this strategy results in mistakes, and quite often, it is not well-understood by developers with the result that it is often considered a bad practice in most projects. If you wish to use Bean managed transactions, then you will need to start a Transaction using the UserTransaction API class as shown below:

      public class WriteTest extends HttpServlet
      {
          @Resource
          UserTransaction tx; // a UserTransaction reference is injected like a any other resource. It can also be looked up from JNDI.
      
          public void doGet(HttpServletRequest request, HttpServletResponse response)
          {
              ...
              tx.begin(); // Start a new JTA BMT
              EntityManager em = JpaUtil.getEntityManagerFactory().createEntityManager();
              ...
              User user = new User("Hans", "Maulwurf", "[email protected]");
              Adress adress = new Adress("Deppenstraße 3","Deppingen");
              //user.setAddress(adress);
      
              em.persist(user);
              em.close();
              ...
              tx.commit(); // Commit the JTA BMT
          }
      
      }
      

      The above code isn't exactly production-ready. For example, it does not perform any exception handling, and nor does it explicitly rollback the changes in the event of an application failure.

  • join the EntityManager instance with the JTA transaction, if the EntityManager is not already associated with the JTA transaction. This is not necessary if you start the JTA transaction first (which is done in the above example involving Bean Managed transactions), but if you create the EntityManager first using your JpaUtil class, and then start a transaction later, then you must use the EntityManager.joinTransaction() method to join the persistence context with the JTA transaction. Quite evidently, any changes flushed from the persistence context not associated with a transaction, will be ignored.

Solution 2

You should use the transaction support provided by the container. This is an examples from the book Pro JPA 2 Mastering the Java Persistence API. I hope this helps:

public class EmployeeServlet extends HttpServlet {
    @PersistenceUnit(unitName="EmployeeService")
    EntityManagerFactory emf;
    @Resource UserTransaction tx;

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // ...
        int id = Integer.parseInt(request.getParameter("id"));
        String name = request.getParameter("name");
        long salary = Long.parseLong(request.getParameter("salary"));
        tx.begin();
        EntityManager em = emf.createEntityManager();
        try {
            EmployeeService service = new EmployeeService(em);
            service.createEmployee(id, name, salary);
        } finally {
            em.close();
        }
        tx.commit();
        // ...
    }
}

Solution 3

This guidance is for integrating hibernate.4.3.5 and EJB and JTA and GlassFish.4.0 in NetBeans.8.0 IDE. Create a web project in net beans (attention: do not make web project with maven because there is a bug in Netbeans.8.0 IDE ) and add hibernate jar files to the project, other setting related to configuring MySql and glassfish is very simple(Just define Connection Pool and JDBC in Resources>JDBC:JDBC Connection Pools & JDBC Resources, guidance for that is in the web if you search for it)(attention: for defining a correct JNDI, first create a temporary project which depends on JNDI like JPA project in glassfish, then copy the settings that is created in Glassfish for this Project because there is a bug in glassfish when you are going to get a ping to MySQl in creating your first Connection Pool if you create it by yourself inside the glassfish) so I do not describe in this article then create persistence.xml file as follow :

<persistence-unit name="omidashouriPU" transaction-type="JTA">
    <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<jta-data-source>jdbc/yourJNDI (which you defined in glassfish) </jta-data-source>
    <exclude-unlisted-classes>false</exclude-unlisted-classes>
    <properties>
            <property name="hibernate.archive.autodetection" value="class"/>
            <property name="hibernate.connection.driver_class"  value="com.mysql.jdbc.Driver"/>
<property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.SunOneJtaPlatform"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
            <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/YourSchemaName"/>
            <property name="hibernate.connection.username" value="root"/>
            <property name="hibernate.connection.password" value="yourpassword"/>
            <property name="hibernate.show_sql" value="true"/>
    </properties>
</persistence-unit>

In Your EJB class (class that annotated with @Stateless) for creating EntityManager use following syntax :

@PersistenceContext(unitName = " omidashouriPU ")
EntityManager em;
em.persist(YourEntityObject);

As you Know when you are using “transaction-type="JTA", management of transaction is not with you, mean that, managing of opening and closing the transaction is application server (Here GlassFish) responsibility. Indeed if you check your persistence.xml in mode design, in front of persistence provider drop down box you can see hibernate is now added.

Share:
12,265
mw88
Author by

mw88

Updated on June 26, 2022

Comments

  • mw88
    mw88 almost 2 years

    I'm new to hibernate and I want it to use the database connection from the application server via JNDI.

    The Strange thing is, that it creates my tables in the database but it doesn't save the entity. It seems, that it doesn't commit.

    Has someone experienced similar problems with hibernate?

    This is a little test-servlet:

    public class WriteTest extends HttpServlet
    {
        @Override
        public void doGet(HttpServletRequest request, HttpServletResponse response)
        {
            /*try
            { 
                User user = new User("Hans", "Maulwurf", "[email protected]");
    
                InitialContext ctx = new InitialContext();
                UserFacadeBean bean = (UserFacadeBean) ctx.lookup("ejb/UserFacadeBeanService");
                bean.persist(user);
            }
            catch (NamingException e)
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }*/
    
            EntityManager em = JpaUtil.getEntityManagerFactory().createEntityManager();
            //em.getTransaction().begin();
    
            System.out.println("Begin transfer");
    
            User user = new User("Hans", "Maulwurf", "[email protected]");
            Adress adress = new Adress("Deppenstraße 3","Deppingen");
            //user.setAddress(adress);
    
            System.out.println("Save User 'Hans Maulwurf'");
    
            em.persist(user);
            //em.persist(adress);
            //em.getTransaction().commit();
            em.close();
    
            System.out.println("Everything went better than expected!");
        }
    }
    

    This is the little helper-class:

    public class JpaUtil
    {
        private static final EntityManagerFactory emf;
    
        static
        {
            try
            {
                System.out.println("Initialize EntityManagerFactory...");
                emf = Persistence.createEntityManagerFactory("testPU");
            }
            catch (Throwable ex)
            {
                System.err.println("Initial EntityManagerFactory creation failed." + ex);
                throw new ExceptionInInitializerError(ex);
            }
        }
    
        public static EntityManagerFactory getEntityManagerFactory()
        {
            return emf;
        }
    }
    

    My user-object:

    @Entity
    @Table(name = "T_UserJpa")
    public class User implements Serializable
    {
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private Long id;
        @Embedded
        @AttributeOverrides(
        {
            @AttributeOverride(name = "street", column =
            @Column(name = "user_street")),
            @AttributeOverride(name = "city", column =
            @Column(name = "user_city", length = 50))
        })
        private Adress adress;
        private String firstname;
        private String lastname;
        private String email;
    
        public User()
        {
        }
    
        public User(String firstname, String lastname, String email)
        {
            this.firstname = firstname;
            this.lastname = lastname;
            this.email = email;
        }
    
        public Long getId()
        {
            return id;
        }
    
        public void setId(Long id)
        {
            this.id = id;
        }
    
        public Adress getAddress()
        {
            return adress;
        }
    
        public void setAddress(Adress adress)
        {
            this.adress = adress;
        }
    
        public String getFirstname()
        {
            return firstname;
        }
    
        public void setFirstname(String firstname)
        {
            this.firstname = firstname;
        }
    
        public String getLastname()
        {
            return lastname;
        }
    
        public void setLastname(String lastname)
        {
            this.lastname = lastname;
        }
    
        public String getEmail()
        {
            return email;
        }
    
        public void setEmail(String email)
        {
            this.email = email;
        }
    
        @Override
        public boolean equals(Object obj)
        {
            if (this == obj)
            {
                return true;
            }
            if (!(obj instanceof User))
            {
                return false;
            }
            final User user = (User) obj;
            return !(email != null ? !email.equals(user.email) : user.email != null);
        }
    
        @Override
        public int hashCode()
        {
            return 29 * (email != null ? email.hashCode() : 0);
        }
    }
    

    My persistence.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence">
      <persistence-unit name="testPU" transaction-type="JTA">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <jta-data-source>jdbc/testdb</jta-data-source>
        <properties>
          <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
          <property name="hibernate.show_sql" value="true"/>
          <property name="hibernate.transaction.factory_class" value="org.hibernate.transaction.JTATransactionFactory"/>
          <property name="hibernate.hbm2ddl.auto" value="update"/>
          <property name="hibernate.connection.autocommit" value="true"/>
        </properties>
      </persistence-unit>
    </persistence>
    

    Edit: I forgot to mention, that I already used the information provided in this thread: Learning resource for Configuring Hibernate JPA 2.0 on Glassfish server

  • mw88
    mw88 almost 13 years
    When I use em.getTransaction() I get this exception: java.lang.IllegalStateException: A JTA EntityManager cannot use getTransaction()
  • Chris
    Chris almost 13 years
    i believe what u need to do is em.getsession and check that as you cannot use the getTransaction as you arent using this api
  • mw88
    mw88 almost 13 years
    The EntityManager has no such method because it seems to handle the session and transaction and doesn't allow me to access it. I played a bit with flush but it's already set to auto.
  • Chris
    Chris almost 13 years
    I would refrain from using auto commit in the configuaration as it is very dangerous and can lead to broken entities and has problems with certain db drivers from experience, if you set up your servket to commit for you , the usual way is to have this layer as a DAO that do these operations for you