Reputation: 1488
please can somebody help me to explain the following (for me) very strange JPA behaviour. I intentionally change primary key of entity which is prohibed in JPA.
So first commit correctly throws "Exception Description: The attribute [date] of class [some.package.Holiday] is mapped to a primary key column in the database. Updates are not allowed.".
But second (third, fourth, ...) succeed...! 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");
}
It will printout:
First commit failed
Second commit succeed
OMG, how is this possible?!
(Using EclipseLink 2.2.0.v20110202-r8913 with MySQL.)
Upvotes: 4
Views: 1148
Reputation: 76719
The failure of the commit operation for the first transaction has no bearing on the second transaction. This is due to the fact that when the first commit fails, the EntityTransaction
is no longer in the active state. When you issue the second em.getTransaction().begin
invocation, a new transaction is initiated that does not have any knowledge of the first.
It is important to note that although your code may use the same EntityTransaction
reference in both cases, it is not necessary that this class actually represent the transaction. In the case of EclipseLink, the EntityTransaction
reference actually wraps an EntityTransactionWrapper
instance that further uses a RepeatableUnitOfWork
, the latter two classes being provided by EclipseLink implementation and not JPA. It is the RepeatableWriteUnitOfWork
instance that actually tracks the collection of changes made to entities that will be merged into the shared cache (and the database). When the first transaction fails, the underlying UnitOfWork
is invalidated, and new UnitOfWork
is established when you start the second EntityTransaction
.
The same will apply to most other JPA providers as the EntityTransaction
class is not a concrete final class. Instead, it is an interface that is typically implemented by another class in the JPA provider, and which may likewise wrap a transaction thereby requiring clients to use the EntityTransaction
reference instead of directly working with the underlying transaction (which may be a JTA transaction or a resource-local transaction).
Additionally, you ought to remember that:
EntityTransaction.begin()
should be invoked only once. Invoking it a second time will result in an IllegalStateException
exception being thrown as it cannot be invoked when a transaction is active. So, the fact that you are able to invoke it the second time, implies that the first transaction is no longer active.Upvotes: 1