JPA Transaction

rmacher

Mitglied
Hallo allerseits

Folgendes (etwas vereinfachtes) Szenario:

Habe die Entity-Klasse Person , die wiederum eine Entity-Klasse Adresse hat. Eine separate Klasse ist für das Speichern, Löschen, Updaten etc. von Person-Entities zuständig:

Java:
public void save(Person person) throws Exception {

		if (person.getId() != null && person.getId().intValue() > 0) {
			update(person);
		} else {
			EntityManager em = HelperClass.createEntityManager();

		
			Adresse a = person.getAdresse();

			em.getTransaction().begin();

			if (a.getId() == null) {
				em.persist(a);
			} else {
				em.merge(a);
			}

			em.persist(person);

			try {
				em.getTransaction().commit();
			} catch (Exception e) {
				logger.error(
						"Fehler ... ", e);

				if (em.getTransaction().isActive()) {
					em.getTransaction().rollback();
				}

				throw e;

			} finally {
				if (em.isOpen()) {
					em.close();
				}
			}

		}

	}

Nun, habe ich eine weitere Entity-Klasse namens Firma, die wiederum unter anderen Referenzen auch eine Kontaktperson vom Typ Person hat. Eine separate Klasse ist auch hier für das Speichern, Updaten und Löschen von firma-Instanzen zuständig. Wenn ich jetzt Firma-Entity speichere, muss ich auch Person-Entity speichern (auf CASCADE habe ich verzichtet). Und, ich möchte ein rollback machen können, falls etws schief geht. Da meine sava-Methode in der Person-Persister Klasse einen eigenen EntityManager hat, Transaction startet und die Änderungen commitet, eignet sich diese beim Speichern der Firma-Entity nicht, da ich damit ein rollback evtl. nicht mehr machen kann. Heisst, ich muss den ganzen Code noch einmal in der Firma-Persister klasse implementeiren :(. Oder, in ich habe in der Person-Persister Klasse (oder einer anderen Helper-Klasse) die save-Methode, welche die Person-Entity speichert, jedoch ohne commit auszuführen. Dazu würde ich beim Aufruf dieser Methode den EntityManager übergeben, mit dem ich die Transaktion gestartet und auch am Schluss commiten werde. Sollte etwas irgendeinmal innerhalb dieser Transaction schief gehen, kann ich so problemloss ein rollback machen.

Nun finde ich das nicht schön: redundanter Code etc. Und, auf die Verwendung eines EntityManager auf der Application-Ebene würde ich auch gerne verzichten.

Gibt es da eine bessere Möglichkeit, das Problem zu lösen? Könnte ich evtl. in der save-Methode der Person-Persister abfragen, ob eine Transaction schon aktiv ist und falls ja, würde ich diese nutzen (die Frage ist aber auch, wie ich auf die EntityManager-Instanz kommen würde), und fall nicht, würde ich eine neue Transaction starten, nachdem ich zuerst einen neuen EntityManager erstellt habe?

Danke für jeden Typ.

Hinweis:
Es handelt sich um eine JavaSE Anwendung:

[XML]<persistence-unit name="mypu" transaction-type="RESOURCE_LOCAL">[/XML]
 
Zuletzt bearbeitet:

fischefr

Aktives Mitglied
Ich würde das commiten aus dem save rausziehen. Evtl. wäre auch Spring ein geeignetes Framework, um deine Transaktionen besser managen zu können. Ich kenne dein genaues Szenario nicht, aber normalerweise läuft das z.B. bei Client-Server-Anwendungen so:

1. Request kommt vom Client
2. Server eröffnet DB-Transaktion
3. Server arbeitet Anfrage ab (diverse Methodenaufrufe etc.)
4. Server committet Transaktion
5. Rücksprung
 

rmacher

Mitglied
Vielen Dank.

Ich habe das Problem jetzt so gelöst, dass ich für jede entity zwei "Persister-Klassen" implementiert habe. Folgender Code zeigt es exemplarisch:

Java:
public class AdressePersister {

	public void save(Adresse adresse) throws Exception {

		if (adresse.getId() != null) {
			throw new PersisterException(" ... message ...");
		} else {
			EntityManager em = HelperClass.createEntityManager();
			em.getTransaction().beginn();
			
			AdressePersisterUtil.save(adresse, em);
			
			try {
				em.getTransaction().commit();
			} catch (Exception) {
				logger.error(" ... message ...");
				
				if (em.getTransaction().isActive()) {
					em.getTransaction().rollback();
				}

				throw e;
			} finally {
				if (em.isOpen()){
					em.close();
				}
			}
		}
	}
	
	// weitere Methoden ...

	
}

Die Klasse AdressePersisterUtil sieht wie folgt aus:
Java:
public class AdressePersisterUtil {

	public static void save(Adresse adresse, EntityManager em) throws Exception {
	
		Ort ort = adresse.getOrt();
		
		if (ort.getId() != null) {
			OrtPersisterUtil.update(ort, em);
		} else {
			OrtPersisterUtil.save(ort, em);
		}
	
		em.persist(adresse);
	}
	
	// weitere Methoden (update / delete ...)
	
}

Für die Entity Ort gibt es auch zwei Klassen (OrtPersister und OrtPersisterUtil), die ähnlich wie AdressePersister und AdressePersisterUtil aufgebaut sind und funktionieren. In deder xxxPersistetUtil-Klasse sind Klassenmethoden, die an einer schon bestehenden Transaktion teilnehmen können. Wenn ich jetzt Adresse als Bestandteil einer Person-Entity behandeln muss, kann ich die AdressePersisterUtil-Methoden save bzw. update verwenden. Bevor ich sie aufrufe, wird ein EntityManager erstellt und die Transaktion gestartet. Wenn etwas schief geht, kann ich in der Aufrufer-Methode problemlos ein rollback machen, da keine der aufgerufenen Methoden ein commit gemacht hat.

Bei diesem Ansatz hält sich die Coderedundanz im Rahmen, da z.B. in der AdressePersister.save Methode die Methode AdressePersisterUtil.save aufgerufen wird. Jedoch gibt es etwas mehr Methodenaufrufe, was sich evtl. auf die Performance negativ auswirken könnte.

Ich hoffe, dass ich mit diesem Ansatz einigermassen richtig liege und bin für weitere Tipps dankbar.
 

Ähnliche Java Themen


Oben