Ich habe vorhin im Hibernate Wiki den Artikel zu "Sessions and transactions" gelesen:
http://www.hibernate.org/42.html
Im Abschnitt "Transaction demarcation with JTA" gibt es 2 Codebeispiele und beim zweiten Beispiel frage ich mich, ob das wirklich so hinhaut oder nicht vielleicht was vergessen wurde (weil der Code ja nur das Konzept verdeutlichen soll).
Code:
// code from the Hibernate wiki
UserTransaction tx = (UserTransaction)new InitialContext()
.lookup("java:comp/UserTransaction");
Session session = factory.openSession();
try {
tx.begin();
// Do some work
session.load(...);
session.persist(...);
session.flush();
tx.commit();
}
catch (RuntimeException e) {
tx.rollback();
throw e; // or display error message
}
finally {
session.close();
}
In dem zweiten Codebeispiel (siehe oben) wird zunächst eine Hibernate Session erzeugt, die dann vom Programmierer selber verwaltet (gemänaged) wird. Das geschieht noch ausserhalb einer Transaktion.
Anschliessend wird dann mittels der über JNDI erfragten UserTransaction eine JTA-Transaction begonnen und innerhalb dieser Entities geladen und gespeichert (session.load(), session.find()). Anschliessend wird dann die Session geflushed ( session.flush() ) und danach dann die Transaktion commited ( tx.commit() ).
Unklar ist mir, wie jetzt die Hibernate Session überhaupt an dieser Transaktion teilnimmt. Denn sie wurde ja bereits eröffnet, als die JTA-Transaktion noch gar nicht begonnen wurde.
Beim Aufsetzen auf JPA wäre die Analogie zum Codebeispiel aus dem Hibernate-Wiki die Verwendung eines application-managed Entity Managers. In JPA gibt es dazu die Regel, dass wenn ein Application-Managed EntityManager innerhalb einer Transaktion erzeugt wird, er automatisch an diese Transaktion gebunden wird, sodass beim Transaction Commit der PersistenceContext mit der Datenbank synchronisiert wird. Wenn ein Application-Managed EntityManager jedoch ausserhalb einer Transaktion erzeugt wird, also bevor eine JPA Transaction gestartet wurde, dann muss er später von Hand an diese JTA Transaction gebunden werden, damit eine Synchronisierung des PersistenceContextes erfolgt. Das erfolgt durch einen Expliziten Aufruf von EntityManager.joinTransaction().
Ungefähr würde das mit JPA so aussehen:
Code:
// comparable pattern with JPA
@Stateful
public class JPAExampleBean {
@PersistenceUnit(unitName="xxx")
EntityManagerFactory emf;
EntityManager em;
public void init() {
// application-managed EntityManager created outside of a transaction
em = emf.createEntityManager();
...
}
public void persistSomeEntity(Entity e) {
// as the EntityManager was created outside of a transaction,
// we need to call em.joinTransaction(); in order to synchronize
// the persistence context with the database when the transaction
// commits.
em.joinTransaction();
em.persist(e);
...
}
... more methods of the StateFul Session Bean...
@Remove
public void finish () {
...
}
}
Zurück zum oben zitierten Code-Beispiel aus dem Hibernate-Wiki:
Ein explizites Binden der Hibernate Session an die JTA Transaktion fehlt dort. Die Frage ist also: Wie erfolgt dann diese Bindung?
Bzw. anders ausgedrückt: Führt vielleicht eine der aufgerufenen Hibernate Methoden ( session.load(), session.persist(), session.flush() ) vielleicht die Bindung durch?