JPA - only the first commit failed, but it failed

Please help me explain the following (for me) very strange JPA behavior. I intentionally change the primary key of an object that is prohibited in JPA.

So, first, commit correctly throws "Exception Description: the [date] attribute of the [some.package.Holiday] class is mapped to the primary key column in the database. Updates are not allowed.".

But the second (third, fourth, ...) success ...! How is this possible ?!

Holiday h1 = EM.find(Holiday.class, new GregorianCalendar(2011, 0, 3).getTime()); try { EM.getTransaction().begin(); h1.setDate(new GregorianCalendar(2011, 0, 4).getTime()); EM.getTransaction().commit(); System.out.println("First commit succeed"); } catch (Exception e) { System.out.println("First commit failed"); } try { EM.getTransaction().begin(); EM.getTransaction().commit(); System.out.println("Second commit succeed"); } catch (Exception e) { System.out.println("Second commit failed"); } 

Will be printed:

 First commit failed Second commit succeed 

OMG, how is that possible ?!

(Using EclipseLink 2.2.0.v20110202-r8913 with MySQL.)

+4
source share
1 answer

A commit operation failure for the first transaction does not affect the second transaction. This is because when the first error fails, the EntityTransaction no longer in an active state. When you call the second call to em.getTransaction().begin , a new transaction is initiated that does not know about the first.

It is important to note that while in both cases your code can use the same EntityTransaction reference, it is not necessary for this class to actually represent the transaction. In the case of EclipseLink, the EntityTransaction reference actually wraps the EntityTransactionWrapper instance, which then uses RepeatableUnitOfWork , with the last two classes provided by the EclipseLink implementation, not the JPA. The RepeatableWriteUnitOfWork instance actually tracks the collection of changes made to objects that will be merged into a common cache (and database). When the first transaction fails, the underlying UnitOfWork invalid and the new UnitOfWork set when the second EntityTransaction .

The same applies to most other JPA providers, since the EntityTransaction class EntityTransaction not a specific final class. Instead, it is an interface that is usually implemented by another class in the JPA provider and which can also wrap a transaction, thereby requiring clients to use the EntityTransaction link instead of directly working with the underlying transaction (which may be a JTA transaction or a local resource transaction).

In addition, you must remember that:

  • EntityTransaction.begin() should be called only once. Raising it a second time will throw an IllegalStateException , because it cannot be IllegalStateException when a transaction is active. Thus, the fact that you can call it a second time implies that the first transaction is no longer active.
  • If you require that changes made in the context of the first transaction are available for the second, you must merge the objects back into a common context in the second transaction after they are detached by the first. Although this may seem ridiculous, you should remember that individual elements can be changed by clients (readable, end users) before combining them, so changes made by end users can be saved and errors (for example, modification of primary keys) can be fixed interim period.
+1
source

All Articles