# JPA Entities nicht thread safe? (Hibernate)



## mephi (15. Dez 2008)

Hi,

ich hab grad das Problem, dass ich 2 Klassen mit einer ManyToOne Beziehung hab. Ich hole mir Objekt A mit dem EntityManager und find() aus er DB, füge was in der Liste hinzu(Objekte von Klasse B) und speicher das dann mit merge(). Um das ganze auch realistisch zu simulieren starte ich 5 servlet aufrufe und dann schlägt es fehl wegen einer ConcurrentModificationException ..
Jeder Request bekommt seinen eigenen EntityManger und ein sysout hat mir auch bestätigt, dass die unterschiedlich sind. Die Objekte allerdings sind die selben. Also Objekt A gibt immer den selben HashCode. Muss ich selbst dafür sorgen dass mein Objekt und die Listen thread safe sind? Find ich ja schon etwas merkwürdig


----------



## maki (15. Dez 2008)

Servlets sind nicht threadsafe, sollte keine Geheimnis mehr sein 

Wer wirft denn die ConcurrentModificationException?
Eine Collection?


----------



## byte (15. Dez 2008)

Zwei unterschiedliche EntityManager sollten nicht identische Objekte liefern, selbst wenn es die gleichen Datensätze sind. Du solltest nicht den Hashcode vergleichen (equals vs ==  ), sondern Dir die Objekte im Debugger angucken (Eclipse zeigt Dir die Identität eines Objekts anhand einer ID, die kannst Du vergleichen).

Hast Du den 2nd Level Cache aktiviert?




> Muss ich selbst dafür sorgen dass mein Objekt und die Listen thread safe sind?


JPA Objekte sind einfache POJOs. Die sind natürlich nicht thread-safe. Aber für gewöhnlich ist das auch kein Problem. Wenn Du Servlets schreibst, dann guck Dir das Open-Session-In-View Pattern an. Wenn Du das richtig implementierst, hast Du hier keine Probleme mit Concurrency.


----------



## mephi (15. Dez 2008)

maki hat gesagt.:
			
		

> Servlets sind nicht threadsafe, sollte keine Geheimnis mehr sein
> 
> Wer wirft denn die ConcurrentModificationException?
> Eine Collection?



Also so genau steht das nicht da, die letzte Zeile in meinem Code ist em.merge(objA). Bevor ich meine ArraList mit java.util.collections.synchronizedList() ersetzt hab, flog der Fehler bei em.find(ObjAImpl.class, id)

*edit* ok, jetzt fliegts trotz synchronizedMap/Lists bei em.find() .. ist vll auch zufällig an welcher stelle er dann gleichzeitig auf eine list zugreift.
im stacktrace ganz oben steht: "at java.util.HashMap.onEntry(HashMap.java:214)"



			
				byto hat gesagt.:
			
		

> Zwei unterschiedliche EntityManager sollten nicht identische Objekte liefern, selbst wenn es die gleichen Datensätze sind. Du solltest nicht den Hashcode vergleichen (equals vs ==  ), sondern Dir die Objekte im Debugger angucken (Eclipse zeigt Dir die Identität eines Objekts anhand einer ID, die kannst Du vergleichen).
> 
> Hast Du den 2nd Level Cache aktiviert?



nicht explizit, außer er ist standardmäßig aktiviert.
*edit*
es ist auch die selbe ID im debugger :-/



> > Muss ich selbst dafür sorgen dass mein Objekt und die Listen thread safe sind?
> 
> 
> JPA Objekte sind einfache POJOs. Die sind natürlich nicht thread-safe. Aber für gewöhnlich ist das auch kein Problem. Wenn Du Servlets schreibst, dann guck Dir das Open-Session-In-View Pattern an. Wenn Du das richtig implementierst, hast Du hier keine Probleme mit Concurrency.



Ok, muss ich mir mal anschauen, ist das was spezielles von Hibernate?

Da die POJOs vom Framework verwaltet werden, dachte ich eigentlich dass die das auch thread safe machen.. war wohl ein Trugschluss.


----------



## byte (15. Dez 2008)

mephi hat gesagt.:
			
		

> Ok, muss ich mir mal anschauen, ist das was spezielles von Hibernate?
> 
> Da die POJOs vom Framework verwaltet werden, dachte ich eigentlich dass die das auch thread safe machen.. war wohl ein Trugschluss.



Bin mir relativ sicher, dass das Problem nicht Deine POJOs sind sondern der EntityManager. Der EntityManager ist *nicht* thread-safe, daher musst Du sicherstellen, dass bei jedem Request ein neuer EntityManager aufgemacht wird. Ist das bei Dir der Fall? Wenn die Exception bei em#find() kommt, dann wohl eher nicht.

Die POJOs sind zwar nicht thread-safe (das wäre auch schlimm), aber das ist auch nicht dein Problem. Denn wenn Du pro Thread = Request) einen eigenen EntityManager verwendest, dann erzeugen die auch jeweils eigene Identitäten der POJOs. Da können sich die Threads also gar nicht ins Gehege kommen.

Die Standard-Strategie ist, die Lebensdauer des EntityManager an den Request zu binden (siehe Open-Session-In-View). Das heisst, am Anfang des Request neuen EntityManager holen und diesen am Ende des Request zu schließen. Für gewöhnlich bindet man den EM an die Transaktion, so dass er beim commit() automatisch geschlossen wird.

Siehe: http://www.hibernate.org/hib_docs/entitymanager/reference/en/html_single/#transactions


----------



## mephi (15. Dez 2008)

Ich bin mir eigentlich sicher dass ich das alles umsetze. Ich hab eine System Klasse die threadlocal ist und die im Konstruktor einen neuen EM erstellt.

hab ein filter vor mein servlet geschalten:


```
try {
			SystemImpl.getInstance().beginTransaction();
			System.out.println("Start Processing in  System... ");
			chain.doFilter(request, response);
			System.out.println("Ended Processing in  System...");	
			SystemImpl.getInstance().commitTransaction();
		} catch (Exception e) {
			e.printStackTrace();
			SystemImpl.getInstance().rollbackTransaction();
			((HttpServletResponse)response).sendError(500);
		} finally {
			SystemImpl.getInstance().close();
		}
```

system leitet hier die commit/begin und rollback aufrufe einfach an den EM weiter.




```
public void beginTransaction() {
		this.em.getTransaction().begin();
	}
	
	public void commitTransaction() {
		this.em.getTransaction().commit();
	}
	
	public void rollbackTransaction() {
		this.em.getTransaction().rollback();
	}

	public void close() {
		this.em.close();	
	}
```


----------



## byte (15. Dez 2008)

Erzeugst Du Dir über die EntityManagerFactory bei jedem Request einen neuen EntityManager?


----------



## mephi (15. Dez 2008)

ja, am anfang erstell ich eine factory und dann für jeden request einen neuen entitymanager


----------



## mephi (15. Dez 2008)

Ich glaub ich hab das problem. meine system klasse ist threadlocal und da erstell ich im konstruktor den EntityManager, aber im debugger hat der immer die selbe ID. Als ob mir die Factory immer den selben gibt.
mh und mein System hat auch die selbe ID... strange..


*edit* woa.. blöder fehler meinerseits. irgendwo hatte ich eine variable in der ich eine referenz auf meine systemklasse gespeichert hab. und da servlets ja nicht thread safe sind....


----------



## byte (15. Dez 2008)

Ich würde Dein Konzept mit der System-Klasse nochmal überdenken. Mach es doch am besten gleich richtig und schreib DAOs, das übliche Pattern für diesen Anwendungsfall. Das DAO muss dann nur noch den richtigen EntityManager bekommen. Entweder, Du machst das händisch, indem Du Dir einen Filter schreibst, der am Anfang des Requests einen EntityManager erzeugen lässt über die Factory und diesen in den DAOs setzt. Oder Du benutzt gleich Spring...


----------

