Save same object multiple times

Asked

Viewed 1,547 times

2

Solution: setar id = null

Code saved 10 times if running on the Tomcat server, but running on Glassfish only adds once.

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.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_2_0.xsd">
<persistence-unit name="default" transaction-type="RESOURCE_LOCAL">
    <class>entities.Secao</class>
    <class>entities.Funcionario</class>
    <class>entities.Unidade</class>
    <class>entities.Patrimonio</class>
    <class>entities.Descricao</class>
    <class>entities.Classificacao</class>

    <validation-mode>AUTO</validation-mode>
    <properties>
        <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost/patrimonio" />
        <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
        <property name="javax.persistence.jdbc.user" value="root" />
        <property name="javax.persistence.jdbc.password" value="root" />
        <property name="hibernate.hbm2ddl.auto" value="update" />
        <property name="hibernate.show_sql" value="true" />
        <property name="hibernate.format_sql" value="true" />
        <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect" />

    </properties>
</persistence-unit>

public void save() {
    int i = 0;
    EntityManager em = JpaUtil.getEntityManager();

    while (i < 10) {
        em.merge(item);         
        i = i + 1;          
    }
    item = new Item();      
}

Jpautil class:

package persistence;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

import org.hibernate.Session;

public class JpaUtil {

    private static final String PERSISTENCE_UNIT_NAME = "default";

    private static ThreadLocal<EntityManager> manager = new ThreadLocal<EntityManager>();

    private static EntityManagerFactory factory;

    private JpaUtil() {
    }

    public static boolean isEntityManagerOpen() {
        return JpaUtil.manager.get() != null && JpaUtil.manager.get().isOpen();
    }

    public static EntityManager getEntityManager() {
        if (JpaUtil.factory == null) {
            JpaUtil.factory = Persistence
                    .createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
        }
        EntityManager em = JpaUtil.manager.get();
        if (em == null || !em.isOpen()) {
            em = JpaUtil.factory.createEntityManager();
            JpaUtil.manager.set(em);
        }
        return em;
    }

    public static void evictCache(EntityManager em, String region) {
        ((Session) em.getDelegate()).getSessionFactory().getCache()
                .evictQueryRegion(region);
    }

    public static void closeEntityManager() {
        EntityManager em = JpaUtil.manager.get();
        if (em != null) {
            EntityTransaction tx = em.getTransaction();
            if (tx.isActive()) {
                tx.commit();
            }
            em.close();
            JpaUtil.manager.set(null);
        }
    }

    public static void closeEntityManagerFactory() {
        closeEntityManager();
        JpaUtil.factory.close();
    }
}
  • 1

    Erick, welcome to Stack Overflow in English. Does your problem occur in the same database? Have you checked whether the queries generated in both Tomcat and Glassfish are equal? Could you post them?

  • And also enter the code of Jpautil. By the way: "saved" means "creates a record", or in both cases only one record is created, but saved 10 times in one, and only one in the other?

  • @jpkrohling On Tomcat it creates 10 records with different ids in the database and on Glassfish only one. All the code is here: https://github.com/erickdeoliveiraleal/sispatri

  • As interesting as the code may seem, it is Stackoverflow’s practice to put the relevant code into the question. The reason is that for someone who is trying to help, it is difficult to find the code snippets and/or get a complete view of the code in just a few minutes.

  • @jpkrohling Posted to Jpautil, this might help you to look here: http://stackoverflow.com/questions/21421261/save-same-object-multiple-times ... But so far I could not even using in.persist

  • I think the problem is with the transaction-type. Try using JTA this way: <persistence-Unit name="default" transaction-type="JTA"> You can take a look at this tutorial, I believe it helps: link

Show 1 more comment

3 answers

2

I’ve had this same problem a few times in the past. And I’ve managed to solve it.

The idea is that each request uses a new EntityManager for you.

What happens is that your EntityManager has a thread scope, after all it is kept in a variable ThreadLocal. However, thread scope is not the same as request scope and that’s where your business sticks. The application container reuses threads between a request and another and as a result a request can receive a EntityManager polluted with cache data from any previous request, and worse, this tends to be quite random and non-deterministic.

So you ALWAYS has to clean up the EntityManager before using it on any request to prevent it from being polluted:

 EntityManager em = ...;
 em.clear();

And you must ensure that in your application, right at the beginning of each request, before doing anything else, that the EntityManager is clean.

I have been bitten by this problem a few times in the past, today I am vaccinated. This is a common mistake to commit.

Okay, but why does it work on Tomcat and not glassfish? The answer to this is that they manage the thread pool in different ways, and the code is wrong on both: If you run this 10,000 times, you will force Tomcat to also reuse threads and it will also go wrong.

1

Unfortunately, I can’t give you a precise explanation of what’s going on. Maybe it’s best to do a debugging session, especially at Jpautil.

My guess: Tomcat always returns 10 new sessions, while Glassfish always returns the same session. Because the object you are trying to save is in the Glassfish session, subsequent calls are ignored, as the end of the request happens. In Tomcat, a new object would be persisted at each call, since the new session does not know anything about the object being persisted. The expected, from what I could see in the code, is to have one record in the database.

But I will take advantage of the answer and give a hint or two about JPA in web applications.

  • Try using the JPA provider of your "container". If you need JPA, then it is best to use a container that already provides an implementation (Glassfish, Jboss AS/Wildfly, ...).
  • Consume the Entitymanager provided by the container. The easiest way is to use CDI and inject a @Persistencecontext.
  • Do not specify connection details directly in your persistence.xml . It is best to set up a Datasource in your container, and point out this feature in your persistence.xml. Not that it’s wrong, but you’ll get a lot of benefits from doing it. For example, you will start making use of a pooling Connection without making any effort.

The way you are using JPA is completely outdated for modern web applications (ie: newer than some 5 years). The form you use is recommended only for non-web applications, but unfortunately several tutorials teach this way, since it is more "practical" than explaining how to make the necessary settings.

There are several web tutorials on how to do the right thing, but maybe you can take a look at this small project to have an example on Wildfly:

Example of persistence.xml

Example of "Jpautil"

Example of how to get an Entitymanager.

How to set up a data source for Wildfly stays as a home exercise :-)

1

In this code snippet, you have tried using the persist() method instead of merge()?

The merge() method has a distinct purpose of persist(), since it actually persists() inserts the record into the database.

Obtained in java doc Java EE 6

void persist(java.lang.Object Entity)

Make an instance Managed and persistent.

It is very likely that the call of merge() in different containers will result in different results. Depending on the providers, transaction management, etc.

public void save() {
    int i = 0;
    EntityManager em = JpaUtil.getEntityManager();

    while (i < 10) {
        //em.merge(item);         
        em.persist(item);
        i = i + 1;          
    }
    item = new Item();      
}

Browser other questions tagged

You are not signed in. Login or sign up in order to post.