简体   繁体   中英

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

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", "hans.maulwurf@test.de");

            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", "hans.maulwurf@test.de");
        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

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", "hans.maulwurf@test.de"); 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.

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();
        // ...
    }
}

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.

Does it commit when you maunally commit your changed ie when you uncomment the commit line?

Also try checking you configuration is being set by

toggleAutoCommit = session.connection().getAutoCommit();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM