EntityManagerFactory & Cache

  • Themenstarter Gelöschtes Mitglied 5909
  • Beginndatum
G

Gelöschtes Mitglied 5909

Gast
Hallo Zusammen,

mal wieder eine kleine Frage zu JPA (EclipseLink 2.0).

Ich habe mir eine eigene kleine EntityManagerFactory gebaut, welche verschiedene "Standardproperties" für die PersistenceUnits setzt. Prinzipiell würde es auch ohne gehn, aber dann kommt da noch OSGi ins Spiel. Denn in der Factory verwende ich den
OSGi Persistenceprovider, anders habe ich es nicht hingekriegt. (Man muss den Classloader bzw die Klasse mitgeben)

Java:
public synchronized EntityManager getEntityManager(Class<?> clazz, String persistenceUnit) {
//...
}

JPA-PersistenceUnits in die Manifest eintragen und dann normal Persistence.createEntityManagerFactory ging nicht. Auf der EclipseLink seite gibt es lustigerweise zwei Beispiele, eins mit dem OSGi Persistenceprovider und eins mit der Manifest. Aber es hat soweit funktioniert.

Jetzt habe ich immer wieder festgestellt, dass bei Änderungen auf der Datenbank (händisch) EclipseLink dies nicht mitkriegt.
Erste Lösung: EclipseLink Cache komplett abschalten. Gesagt getan und es schien auch alles zu klappen. Aber irgendwie dann doch nicht immer. Denn es hat die Änderungen zum Teil doch nicht mitgekriegt. Wenn ich ein EntityManager#clear reinpacke, dann funktionierts, aber das kann doch eigentlich nicht sein.

Dann hab ich heut mal weiter geforscht und anscheinend liegt es daran, dass ich die JPA EntityManagers Cache (in meiner eigenen EMF).

Die Methode von oben schaut zuerst nach, ob ein EntityManager für diese PersistenceUnit existiert.
Wenn nicht wird einer über den Mechanismus den ich oben beschrieben habe erzeugt und dann
in ne statische Map<String, EntityManager> gesteckt.

D.h. pro OSGi Domain Bundle (entspricht einer PersistenceUnit) existiert genau ein EntityManager. Ich dachte auch das ist gut, da ich ansonsten unnötig viele Connections offen habe. Außerdem kapsel ich die JPA EntityManagerFactory quasi durch meine Eigene weg, da ich ja sonst jedesmal den PersistenceProvider Code und so habe. Die eigene EntityManagerFactory verwende ich beim erzeugen der DAOs. Soweit funktioniert der Code, wenn ich jedesmal in den DAOs ein #clear aufrufe, dann krieg ich auch die richtigen Ergebnisse. Die PKs stimmen mit denen aus der DB überein und equals und hashcode sind auch überschrieben.

Jetzt habe ich zum "Spaß" mal statt die EntityManagers die original JPA EMFs in meiner eigenen EMF gecachet. Und dann rufe ich jedesmal createEntityManager auf. Dann tauch das oben beschriebene Problem komischerweise nicht auf?!?!?!?

Das hat mich ehrlichgesagt sehr verwirrt. Vorher hatte jedes DAO der gleichen PersistenceUnit den gleichen EntityManager.
Jetzt hat jedes DAO seinen eigenen EM. Und es scheint so zu funktionieren.

Wenn ich aber bedenke, dass ich Entity X lade, dann auf der DB was ändere und dann Entity X nochmal lade, die Entity sich aber nicht verändert und jetzt mit eigenem EM funktioniert es - warum? Vorher wurde auch beides mit ein und dem selben EM geladen.

Aber viel wichtiger: Egal warum - wie macht man es richtig? Jedes mal einen neuen EntityManager erzeugen - ja / nein?
Verwende ich den gleichen EntityManager in allen CRUD/finder Methoden im DAO (halte ich mir eine Referenz auf den EM)? Erzeuge ich in jeder DAO Methode einen neuen EntityManager? Ist das erzeugen teuer? Das erzeugen der EntityManagerFactory wird wohl das teure sein...

Spring DM darf ich leider nicht nutzen...
 
M

maki

Gast
Jetzt habe ich immer wieder festgestellt, dass bei Änderungen auf der Datenbank (händisch) EclipseLink dies nicht mitkriegt.
Manuelle Eingriffe in die DB sollten auch mit vorsicht genosssen werden, um nicht zu sagen "verboten" sein, wenn ein ORM im Spiel ist.
Sieh es mal so: Mit einem ORM, ist das ORM der "Master" der Daten, nicht die DB, willst du was ändern musst du übers ORM gehen.
 
G

Gelöschtes Mitglied 5909

Gast
Erstmal danke für die Antwort, auch wenn sie für mich unerfreulich ist.

Was ich nicht ganz verstehe:
- Bei komplexen Geschäftsanwendungen ist es meiner bisherigen Erfahrung nach nicht unüblich, Daten auf der DB manuell Ändern zu müssen
- Dieses Problem werden sicher viele andere Entwickler auch haben
- Das würde bedeuten, dass man JPA nur für wenig komplexe Anwendungen nutzen kann (vor allem für Webanwendungen, bei denen nur Daten Angelegt, Editiert und abgefragt werden - ohne richtige Geschäftsprozesse bzw. Berechnungen im Hintergrund)
- Wieso kann man dann den Cache abschalten? Dies habe ich explizit getan und ist auch so gewünscht. Es soll jedesmal explizit auf die Datenbank gegangen werden. Die Maschine ist dick genug als dass sie das locker packt.
- Auch wenn ich nicht mehr per Hand Daten editiere - es laufen mehrere VMs die auf die gleiche Datenbank gehen - Das Problem bleibt da dann auch bestehen.

Irgendwie kann ich mir kaum vorstellen, dass das jetzt Sonderanforderungen sind. Ich würde sogar eher sagen das es die Regel ist. Fände ich sehr enttäuschend, wenn EclipseLink das nicht auf die reihe kriegt.
 
M

maki

Gast
Ehrlich gesagt keine Ahnung, wir haben das immer so gelöst:
Um Daten direkt zu ändern, gibt es spezielle Masken in der App die das leisten, aber unter Einbeziehung des ORM.
Ja, ist mehr AUfwand und damit ein zusätzlicher Punkt auf der Liste von Dingen die der Kunde zahlen muss.
Anonsten muss die App eben runtergefahren werden, falls manuelle Eingriffe direkt in dre DB nötig sind.
 

KSG9|sebastian

Top Contributor
Was versuchst du denn da?

Du wirst einem ORM nicht beibringen können manuelle Änderungen zu erfassen. Caches auszuschalten, flush/clear aufzurufen löst deine Probleme nicht sondern bringt nur neue.

Wenn man solche Probleme hat (wir haben die auch viel zu oft) dann gibt es dafür bessere Möglichkeiten:

- Daten werden nicht auf der Datenbank geändert (ist sowieso schlecht..kein Backup, nicht nachvollziehbar, keine Revisionssicherheit)
- Sämtliche Abhängigen Prozesse (ORM u.s.w.) müssen über die Änderung benachrichtigt werden, z.B. über Messaging
 
G

Gelöschtes Mitglied 5909

Gast
Was ich jetzt noch in Erfahrung gebracht habe, ist dass man bei application-managed jpa den EntityManager immer schließen sollte.

Java:
public void foo() {

 EntityManagerFactory emf = ... ;
 EntityManager em = emf.createEntityManager();
 try {
   ...
 } finally {
   em.close();
 }
}

D.h. Wenn ich die EMs immer schließe, mir immer von der EMF einen neuen hole, dann sollte es ohne Probleme gehn.
Auch mit vielen VMs, die auf die DB zugreifen, hoffe ich.

Mitlerweile ist auch Spring DM wieder im Gespräch, wobei das bei diesem Punkt keinen Unterschied machen sollte. (Auch wenn es natürlich sonst sehr viele Vorteile bringt)

- Daten werden nicht auf der Datenbank geändert (ist sowieso schlecht..kein Backup, nicht nachvollziehbar, keine Revisionssicherheit)

Die Argumente die du da bringst sind für mich keine Argumente. Ich meine keine ddls sondern dmls. Außerdem werden alle Daten historisiert (kann man auch per Hand machen)

- Sämtliche Abhängigen Prozesse (ORM u.s.w.) müssen über die Änderung benachrichtigt werden, z.B. über Messaging

Wie stellst du dir das denn genau vor?

Caches auszuschalten, flush/clear aufzurufen löst deine Probleme nicht sondern bringt nur neue.

Wieso kann man den Cache dann ausschalten? Welche neuen Probleme meinst du? Die Kiste ist Dick genug wie gesagt,
Performance ist kein Argument (Was ja eigentlich der Grund ist einen Cache zu verwenden)

Übrigens existieren auch noch diverse VBA-Excel tools (ja ich weiß...) die auf die DB zugreifen. Ich komme also nicht daran vorbei auch direkt auf die DB zugreifen zu müssen...
 

KSG9|sebastian

Top Contributor
Wieso DDL? Ich rede doch auch von Manipulation!

Historisierung von Hand? Glückwunsch...ich würd ne Kündigung bekommen wenn ich sowas mach :)


Zum Problem:

Das Problem dass du nicht ganz aktuelle Daten bekommst wirst du immer haben wenn von einem anderen System aus Änderungen gemacht werden. Beim Select kannst du das nicht abfangen, nur beim Insert/Update (Stichwort Optimistic/Pesimistic Locking).

Szenario:

- Server1 öffnet EntityManager
- Server1 macht einen "select *"
- Server 2 macht einen "update"

Dann hat Server1 definitiv falsche Daten, zumindest so lange bis auf Server1 wieder ein Select ausgeführt wird. Das Problem bekommst du aber auch nicht in den Griff.

Mit Messaging meine ich das jede Anwendung und Benutzer das System über Änderungen benachrichtigen muss.
Sowas wird meist über einen Messagingmechanismus erledigt.
 

ARadauer

Top Contributor
Manuelle Eingriffe in die DB sollten auch mit vorsicht genosssen werden, um nicht zu sagen "verboten" sein, wenn ein ORM im Spiel ist.
Was mach ich dan bei Massenverarbeitungen? mal 5 Mio Datensätze bei Hibernate oder JPA zu aktualisieren kann da schon eine zeit dauern... Was gibt es da für Ansätze?
 
G

Gelöschtes Mitglied 5909

Gast
Zum Problem:

Das Problem dass du nicht ganz aktuelle Daten bekommst wirst du immer haben wenn von einem anderen System aus Änderungen gemacht werden. Beim Select kannst du das nicht abfangen, nur beim Insert/Update (Stichwort Optimistic/Pesimistic Locking).

Szenario:

- Server1 öffnet EntityManager
- Server1 macht einen "select *"
- Server 2 macht einen "update"

Dann hat Server1 definitiv falsche Daten, zumindest so lange bis auf Server1 wieder ein Select ausgeführt wird. Das Problem bekommst du aber auch nicht in den Griff.

Dessen bin ich mir bewusst, es ist aber nicht das Problem was ich oben beschrieben habe.

Nochmal kurz:

Server 1 macht select und bekommt x
Änderung von Hand oder sonst wie -> aus x wird y
Server 1 macht select und bekommt x

Jetzt verstanden? Und ja es müsste y rauskommen und transaktion und so stimmt auch und in der db steht das richtige. Und wenn Server 1 danach noch 20 mal den select macht kriegt er immernoch x
 

KSG9|sebastian

Top Contributor
Auch mit Hibernate kann man performant Batchverarbeitung machen!

Siehe z.B. StatelessSession - ich kenne einige Unternehmen die damit Massenverarbeitung (und zwar wirkliche Messagen) machen.

zum Thema:
Ja, rail, das ist ja normal. Der EntityManager bzw die HibernateSession arbeitet mit einem PersistenceContext. Solange die Session nicht geschlossen wird (oder zumindest per clear() geleert) bekommst du immer "den alten" Wert zurück.
Aber wie du schon sagtest: Den EntityManager solltest du unbedingt wieder schließen.

Gruß Sebastian
 

Ähnliche Java Themen


Oben