JPA / Hibernate problem with concurrency and persistence in JRE

2

I'm working on a project to create a simple server running on Java SE. I am using JPA + Hibernate to perform persistence, however concurrent routines are a problem. I'm wasting a lot of time trying to solve the problems generated by Hibernate without having a meaningful return. I would like to hear from your fellow colleagues already existing solutions to solving Java SE competition issues, such as container-managed J2EE%.

Problems:

  • Always close EntityManager .
  • Divergent instances of objects in instances of EntityManager different.
  • Concurrent operations with EntityManager different generating persistence error.
asked by anonymous 16.04.2015 / 05:50

1 answer

0

Let's start with these two items:

  
  • Divergent instances of objects in instances of EntityManager different.
  •   
  • Concurrent operations with EntityManager different generating persistence error.
  •   

NEVER use the same EntityManager on different threads. EntityManager is designed to live and die confined within the thread that created it.

I recommend studying how to do lock " of entities. It pays to take a look at the concepts of optimistic lock and pessimistic lock.

Wherever you prefer optimistic locking, remember the @Version annotation. . I will not go into more detail here, because this would give me some book chapters and the idea is just to show you the way to find what you want.

In addition, it is worth remembering that each instance of an entity can only be managed by a single EntityManager , and because of this it is not correct to share these instances between several EntityManager s, which also means that you have to be very careful if you want to share them in more than one thread. One way to do this is something like this:

MinhaEntidade veioDeOutraThread = ...;
MinhaEntidade minhaEntidadeNestaThread = em.find(MinhaEntidade.class, veioDeOutraThread.getId());

A safe way to work with this would be to never share any object that is touched by JPA between different threads that can access the database directly.

  
  • Always close EntityManager .
  •   

The most obvious is to use the old try-finally , but maybe you want something more magical. In this case I can suggest starting from something like this (Java 8):

 private static final EntityManagerFactory EMF = ...;

 private final ThreadLocal<EntityManager> ativo = new ThreadLocal<>();

 public <E> E transact(Function<EntityManager, E> func) {
     EntityManager em = ativo.get();
     if (em != null) return func.apply(em);
     em = EMF.createEntityManager();
     try {
         ativo.set(em);
         boolean success = false;
         EntityTransaction et = em.getTransaction();
         try {
             et.begin();
             E ret = func.apply(em);
             success = !et.getRollbackOnly();
             return ret;
         } finally {
             if (et.isActive()) {
                 if (success) {
                     et.commit();
                 } else {
                     et.rollback();
                 }
             }
         }
     } finally {
         ativo.set(null);
         em.close();
     }
 }

If you are using Java 7 or lower, it should not be too difficult to adapt this code to your needs. If you intend to make your transact method throw any exceptions checked, then the best thing to do is to use a custom functional interface.

This code above can be improved to prevent EntityManager from being recreated multiple times in the same Thread if you do not close it and use em.clear() before starting any operation and / or after it finishes them. However, if this is not done in a very precise way, you can introduce competing bugs into the application, while opening and closing several times the way you are, at the most, slows down performance a little.

In addition, you may notice that the above code emulates the required transaction scope semantics of EJB / Java EE (at least I think it emulates, unless I've done something stupid or forgotten something). It is not very difficult to modify it to emulate other scopes such as requires , mandatory , etc.

    
16.04.2015 / 09:02