Hallo Thomas,
vielleicht beschäftigst Du Dich inzwischen schon mit einem ganz anderen Problem oder hast schon längst selber die Lösung gefunden. Schau Dir mal in den API-Docs (
http://docs.jboss.org/hibernate/stable/core/api/) die Klasse Session an. Dort gibt es eine Methode buildLockRequest(LockOptions lockOptions). Diese liefert Dir ein Objekt vom Typ LockRequest auf welchem Du dann lock(<Dein Entity>) aufrufen kannst.
Zu dem Thema hätte ich jetzt einmal eine andere Frage an Dich: Bist Du schon einmal über eine org.hibernate.HibernateException mit dem Fehlertext "reassociated object has dirty collection" beim Aufruf von session.lock() gestolpert. Bei mir tritt die Situation auf, wenn ein Entity A eine One-To-Many-Assoziation zu einem Entity B hat. Mit einem JPQL selektiere ich nun ein A, ohne die Assoziation zu B mittels Join-Fetch einzuschließen. Durch einen separaten JPQL selektiere ich alle B, die mit diesem einen A assoziiert sind. (Dies kann ich, da ich die Foreign-Key-Spalte aus der Tabelle von B im Entity B durch ein eigenes Attribut abbilde. Wenn dieses Attribut insertable = false und updatable = false ist, dann ist der lesende Zugriff auf dieses FK-Attribut kein Problem.)
Die Assoziation zwischen A und B instanziiere ich manuell wie folgt:
Code:
String[] parameterNamen = new String[] {"aId"};
Object[] parameter = new Object[] {aId};
A a = this.executeNamedQuerySingleResult(A.class, ejbQlStringFuerA, parameterNamen, parameter); // EJBQL: select a from A a where a.aId = :aId
List<B> listOfB = this.executeNamedQueryMultipleResult(B.class, ejbQlStringFuerBs, parameterNamen, parameter); // EJBQL: select b from B b where b.aId = :aId
a.setB(listOfB); // <-- Dies ist die manuelle Instanziierung der Assoziation!
Würde ich nun flushen, dann würde Hibernate für alle B in der Assoziation ein Update erzeugen. Kann ich auch nachvollziehen, da Hibernate diese Assoziation nicht selber initialisiert habe und der EntityManager gar nicht wissen kann, ich bei meiner manuellen Instanziierung die richtigen Entities in die Assoziation gesteckt habe. Diesen Flush lasse ich aber Hibernate gar nicht ausführen, indem ich einfach einen Detach durchführe:
Code:
session.clear();
Das Problem ist nun ähmlich wie Deins. Wenn ich meine Entities reattache, indem ich session.merge() aufrufe, dann gibt es erst einmal eine Reihe von Updates für alle B, die in der Assoziation stecken. Kann ich auch verstehen: Hibernate merkt, dass die assoziierten Objekte in einer "normalen" List und nicht in einer PersistentCollection stecken. (Hibernate verpackt assouiierte Objekte ja üblicherweise immer in einer solchen PersistenCollection.) Es könnten in dieser nicht von Hibernate erzeugten List Entities enthalten sein, zu denen noch gar keine FK-Beziehungen in der Datenbank gespeichert sind. Demzufolge müssen also diese SQL-Updates beim Merge durchgeführt werden.
Ich hatte nun aber gehofft, dass ich diese aus
meiner Sicht unnützen SQL-Updates vermeiden kann, indem ich einfach session.lock(a, LockMode.NONE) aufrufe. Gemäß Dokumentation wird beim Setzten des Lock-Modes ein Entity zwar reattached, aber sein Zustand nicht sofort mit der Datenbank synchronisiert. Leider wirft Hibernate besagte HibernateException, wenn ich meine Assoziation auf die oben beschriebene Weise instanziiert habe. Wenn die assoziierten Objekte nicht non einer PersistentCollection gehalten werden, dann weigert sich Hibernate die Objekte mit session.lock() zu attachen.
Ist Dir dieses Problem bei Deiner Arbeit auch über den Weg gelaufen? Hast Du evtl. einen Lösungsansatz gefunden, der weder eine Exception, noch unnötige SQL-Updates zur Folge hat? Falls Du Ideen zu dem Thema hast, dann würde ich mich über ein Feedback freuen!
Viele Grüße
Max