# Hibernate Cascade Annotation



## mr.warft (14. Mrz 2008)

Hallo

Ich habe folgendes Problem bei dem Cascade:


```
@ManyToMany(targetEntity=Sparten.class)
	@Cascade({org.hibernate.annotations.CascadeType.ALL})
	@JoinTable(name = "spartenmapping",
			joinColumns = @JoinColumn(name = "bonuspositionsID"),
			inverseJoinColumns = @JoinColumn(name = "spartenID"))
	public List<Sparten> getSparten() {
		return sparten;
	}
```

und wenn ich nun einen Testlauf mit einer noch leeren DB mache (befülle diese auch mit Werten). Das erstellen der Tabellen funktioniert auch. Ich bekomme eine Fehlermeldung beim erstellen der Testdaten



> org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [de.leuphana.edding.model.Sparten#1]



und das soll an dieser Stelle sein


```
public static Session getSession() {
		Session s = (Session) session.get();  
		if (s == null) {  
			s = sessionFactory.openSession();  
			session.set(s);  
		}  
		return s; 
	}
	
	public static boolean saveOrUpdateObject(Object obj){
		Session s = null;
		try {
			s = HibernateUtil.getSession();
			s.beginTransaction();
			s.saveOrUpdate(obj);
			s.flush();
			s.getTransaction().commit();
		} catch (Exception e) {
			s.getTransaction().rollback();
			e.printStackTrace();
			return false;
		}
		return true;
	}
```

und dort an der Stelle 
	
	
	
	





```
s.saveOrUpdate(obj);
```

Wo liegt dort das genaue Problem? Vielleicht weiß jemand eine Antwort denn ich bin schon seid Stunden beim suchen.

PS: lasse ich das Cascade an der Stelle weg, wird die DB erstellt aber ich bekomme ein Problem bei löschen da die Sparte mit drin hängt ;-) also ein  ???:L


----------



## maki (14. Mrz 2008)

zeig mal deinen testcode


----------



## mr.warft (15. Mrz 2008)

```
public class TestValueGenerator {

	private static Umsatz u;
	private static ArrayList<Umsatz> hmU = new ArrayList<Umsatz>(); 
    private static Kunde k;
    private UmsatzStack stack = new UmsatzStack();

      .
      . 
      .
	
    public static void main(String[] args){
 
    	
    /*
	 * Automatische Generierung von GruppenTypen
	 */
    	
    	GruppenTyp gt;
    	gt = new GruppenTyp();gt.setName("Großkunde");
        HibernateUtil.saveOrUpdateObject(gt);
        gt = new GruppenTyp();gt.setName("Mittelstand");
        HibernateUtil.saveOrUpdateObject(gt);
        gt = new GruppenTyp();gt.setName("Kleinkunde");
        HibernateUtil.saveOrUpdateObject(gt);
        gt = new GruppenTyp();gt.setName("Sonstiges");
        HibernateUtil.saveOrUpdateObject(gt);
    	
    	   
    /*
     * Automatische Generierung von Sparten 
     */
   
    	Sparten p;
    	p = new Sparten(); p.setName(Sparte.x);
    	HibernateUtil.saveOrUpdateObject(p);
    	p = new Sparten(); p.setName(Sparte.y);
    	HibernateUtil.saveOrUpdateObject(p);
    	p = new Sparten(); p.setName(Sparte.z);
    	HibernateUtil.saveOrUpdateObject(p);
        
    /*
	 * Automatische Generierung von Kundendaten
	 */
	
    KundeConnect kc = new KundeConnect();
   
    kc.erstelleKunde("sap001", "A");
    kc.erstelleKunde("sap002", "B");
    kc.erstelleKunde("sap003", "C");
    kc.erstelleKunde("sap004", "D");
    kc.erstelleKunde("sap005", "E");
    kc.erstelleKunde("sap006", "F");
    kc.erstelleKunde("sap007", "G");
    
    /*
     * Automatische GEnerierung von Gruppen
     */
    
    
    GruppeConnect gc = new GruppeConnect();
    
    gc.erstelleGruppe(1, "sap01", "Karstadt");
    gc.erstelleGruppe(2, "sap02", "Staples");
    gc.erstelleGruppe(3, "sap03", "OTTO Office");
    gc.erstelleGruppe(3, "sap04", "Kaufland");
    
    List<Gruppe> g = gc.gibAlleGruppen();

   
    /*
     * Autoamtische Zuordnung von Kunden an Gruppen
     */
    

    List<Kunde> k = kc.getAlleKunden();

    kc.hinzufuegenZuGruppe(k.get(0).getKundenID(), g.get(0).getGruppenID());
    kc.hinzufuegenZuGruppe(k.get(0).getKundenID(), g.get(1).getGruppenID());
    kc.hinzufuegenZuGruppe(k.get(0).getKundenID(), g.get(2).getGruppenID());

      .
      .
      .
    
   
    
    /*
     * Automatische Generierung von Vertr�gen	mit Zuordung der Kunden und Gruppen
     */
    	
	    VertragConnect vc = new VertragConnect();
	    BonusPositionConnect bpc = new BonusPositionConnect();
	    BonusDetailsConnect bdc = new BonusDetailsConnect();
	    ArrayList<Sparten> sp = null;
	  	
	    Vertrag v = null;
	    BonusPosition bp = null;
	    Sparten s = null;
    
    // HIer die Einzelverträge mit Positionen und Details
           
      //Vertrag
      v = vc.getVertrag(vc.erstelleVertrag(VertragTyp.EINZELVERTRAG,1,2008,0,0,0,0,0,0,"Davincci"));
      //Sparten
      sp = new ArrayList();
      s = new Sparten();
      s.setName(Sparte.EDDING);
      s.setSid(Sparte.ID_EDDING);
      sp.add(s);
      //Bonus Position
      bp = bpc.gibBonusPosition(bpc.erstelleBonusPosition(v.getVertragsID(),BonusTyp.Staffelbonus, "Sonstiges", sp));
      //Bonus Detail
      bdc.erstelleBonusDetails(bp.getPositionID(),315000,0.5); 
      bdc.erstelleBonusDetails(bp.getPositionID(),330000,1.5); 
      bdc.erstelleBonusDetails(bp.getPositionID(),355000,2.0);
```

das ist jetzt nur ein Teil des Codes denn die Testklasse ist ziemlich groß
der Fehler taucht beim erstelleBonusPositoin und dann bekomme ich dadurch noch einen NullPointer für bp.getPosition, was nur ein Folgefehler darauf ist.

Danke für die Hilfe.


----------



## semi (15. Mrz 2008)

Das sieht verdächtig aus (zwei Gruppen mit gleicher Id)
	
	
	
	





```
gc.erstelleGruppe(3, "sap03", "OTTO Office"); 
gc.erstelleGruppe(3, "sap04", "Kaufland");
```
Vielleicht hast du noch mehr von dieser Sorte im Code.

Hat zwar nichts mit dem Fehler zu tun, aber du machst für jeden einzelnen Datensatz eine Transaktion.  :shock:


----------



## mr.warft (15. Mrz 2008)

Ja danke aber das hat alles nichts mit dem Fehler zu tun...halt einfach nicht aufgepasst. Mit den Tranksaktionen ist das auch egal dient ja auch nur zum Test ;-)


----------



## semi (15. Mrz 2008)

Ersetze mal das List<Sparten> durch Set<Sparten>. Mal sehen, was passiert.


----------



## mr.warft (15. Mrz 2008)

Also ich habe angefangen das auf Set zu ändern aber das hat noch soviele andere Änderungen im Code. Ich denke auch, dass dies eine gute Idee das mit Set zu machen aber das wird nicht das Problem sein (wenn ich das ganze ohne 
	
	
	
	





```
@Cascade
```
 mache, kommt es zu keinem Fehler...

Wie kommst du auf das Set? Meinst du das es mit doppelten Einträgen verursacht werden könnte, denn ohne Cascade funktioniert das anlegen ja ohne Probleme.

Danke und ich bin offen für weitere Ideen...eine Idee habe ich zwar noch aber das wollte ich nicht machen


```
@ManyToMany(targetEntity=Sparten.class)
	@Cascade({org.hibernate.annotations.CascadeType.ALL})
	@JoinTable(name = "spartenmapping",
			joinColumns = @JoinColumn(name = "bonuspositionsID"),
			inverseJoinColumns = @JoinColumn(name = "spartenID"))
	public List<Sparten> getSparten() {
		return sparten;
	}
```

das ist ja eine unidirektionale Beziehung...kann es da beim Cascade zu Schwierigkeiten kommen? Oder hat es was damit zu tun, das es eine ManyToMany Beziehung ist, denn bei "normalen" Beziehungen funktioniert das bis jetzt alles ohne Fehlermeldung.


----------



## semi (15. Mrz 2008)

Die Cascade-Annotation von Hibernate habe ich noch nie gebraucht. Ich nutze auch reines JPA mit Hibernate 
als Implementierung.
Klartext: Statt @Cascade, verwende ich @ManyToMany(cascade=CascadeType.ALL) und nach Möglichkeit
keine Annotationen, die nicht im javax.persistence Package definiert sind. (Ausnahme Index und FK Generierung) 
Das hat den Vorteil, dass man die Implementierung austauschen kann und auch JPA-konform entwickeln kann.
Hibernutte hat eine Macke bei ManyToMany, die zeigt sich aber etwas anders und zwar, wenn man über den
EntityManager merge statt persist aufruft, werden die geänderten Einträge nicht mit der DB synchronisiert.
Das ist die einzige Ausnahme, die mir bei den Relationships bekannt ist. Alle anderen funktionieren wie erwartet.


----------



## HoaX (15. Mrz 2008)

wie ist dein hibernate konfiguriert?

ich vermute mal dein commit/rollback beendet nicht die session, so dass alle transaktionen auf der selben session laufen.

was macht erstelleBonusPosition? und n stacktrace des fehlers wäre auch nicht schlecht....

ich nehme an in erstelleBonusPosition erstellst du nochmal ein objekt  mit der selben id wo du vorher schon gespeichert hast und hibernate versucht dieses wegen dem Cascaade zu speichern -> fehler


----------



## mr.warft (16. Mrz 2008)

Hallo

das mit dem rollback verstehe ich nicht ganz. Ich dachte das damit die Session immer geschlossen wird. Ist das nicht so der Fall? Aber du hast recht, die Session bleibt so lange offen, bis ein Fehler auftritt und dann sollte die Session mit rollback geschlossen werden. Wo definiere ich das denn, dass bei einem rollback die Session geschlossen wird?

Hier der Code für die Funktion erstelleBonusPosition


```
public int erstelleBonusPosition(int vertragsID, int bonusTyp, String name, List<Sparten> sparten){		
		Vertrag vertrag = (Vertrag) HibernateUtil.getObject(Vertrag.class.getName(), vertragsID);
		if (vertrag == null) {
			Debug.log.error("###BonusPositionConnect-erstelleBonusPosition failed => Vertrag "+vertragsID+" existriert nicht");
			return -1;			
		}
		
		if (!BonusTyp.isValid(bonusTyp)) {
			Debug.log.error("###BonusPositionConnect-erstelleBonusPosition failed => BonusTyp "+bonusTyp+" existriert nicht");
			return -1;
		}
		
		// Position erzeugen
		BonusPosition bp = new BonusPosition();
		bp.setBonusTyp(bonusTyp);
		bp.setName(name);
		bp.setVertrag(vertrag);
		for (Sparten s : sparten){
			bp.getSparten().add(s);
		}
		
		if (HibernateUtil.saveOrUpdateObject(bp)) {
			return bp.getPositionID();			
		} else {
			Debug.log.error("###BonusPositionConnect-erstelleBonusPosition failed => Fehler beim Datenbankzugriff");
			return -1;
		}
	}
```

und der Stacktrace sieht folgend aus


```
org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [de.leuphana.edding.model.Sparten#1]
	at org.hibernate.engine.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.java:590)
	at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:284)
	at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:223)
	at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:89)
	at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70)
	at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:507)
	at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:499)
	at org.hibernate.engine.CascadingAction$5.cascade(CascadingAction.java:218)
	at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:268)
	at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:216)
	at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:169)
	at org.hibernate.engine.Cascade.cascadeCollectionElements(Cascade.java:296)
	at org.hibernate.engine.Cascade.cascadeCollection(Cascade.java:242)
	at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:219)
	at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:169)
	at org.hibernate.engine.Cascade.cascade(Cascade.java:130)
	at org.hibernate.event.def.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:456)
	at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:334)
	at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:181)
	at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:107)
	at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:187)
	at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:172)
	at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:94)
	at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70)
	at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:507)
	at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:499)
	at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:495)
	at de.leuphana.edding.util.HibernateUtil.saveOrUpdateObject(HibernateUtil.java:54)
	at de.leuphana.edding.util.BonusPositionConnect.erstelleBonusPosition(BonusPositionConnect.java:33)
	at de.leuphana.edding.tests.TestValueGenerator.main(TestValueGenerator.java:208)
Exception in thread "main" java.lang.NullPointerException
	at de.leuphana.edding.tests.TestValueGenerator.main(TestValueGenerator.java:210)
```

Hier ist auch noch ein Teil der HibernateUtil




```
public class HibernateUtil {
	
	private static final SessionFactory sessionFactory;
	public static final ThreadLocal<Session> session = new ThreadLocal<Session>();  

	static {
		try {
			// Create the SessionFactory from hibernate.cfg.xml
//			sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
			Debug.log.info("### Hibernate wird gestartet ###");
			AnnotationConfiguration cfg = new AnnotationConfiguration();  
			            cfg.configure("hibernate.cfg.xml");  
			sessionFactory = cfg.buildSessionFactory();
		} catch (Throwable ex) {
			// Make sure you log the exception, as it might be swallowed
			Debug.log.error("Initial SessionFactory creation failed." + ex);
			throw new ExceptionInInitializerError(ex);
		}
	}

	public static SessionFactory getSessionFactory() {
		return sessionFactory;
	}
	
	public static Session getSession() {
		Session s = (Session) session.get();  
		if (s == null) {  
			s = sessionFactory.openSession();  
			session.set(s);  
		}  
		return s; 
	}
	
	public static boolean saveOrUpdateObject(Object obj){
		Session s = null;
		try {
			s = HibernateUtil.getSession();
			s.beginTransaction();
			s.saveOrUpdate(obj);
			s.flush();
			s.getTransaction().commit();
		} catch (Exception e) {
			s.getTransaction().rollback();
			e.printStackTrace();
			return false;
		}
		return true;
	}
	
	public static boolean deleteObject(String obj, int id){
		Session s = null;
		try {
			s = HibernateUtil.getSession();
			s.beginTransaction();
			s.delete(s.load(obj, id));
			s.getTransaction().commit();
		} catch (Exception e) {
			s.getTransaction().rollback();
			e.printStackTrace();
			return false;
		}
		return true;
	}
	
	public static Object getObject(String obj, int id){		
		Session s = null;
		Object o = null;
		
		try {
			// get session
			s = HibernateUtil.getSession();
			
			// Start Transaction
			s.beginTransaction();
			
			/*
			 * FS: Okay, laut Doku sollen wir nicht load() verwenden, um zu testen, ob ein Objekt überhaupt existiert, sondern get().
			 * Bevor wir das Objekt also anfordern, fragen wir nach, ob es überhaupt existiert.
			 */

			o = s.get(obj, id);
			if (o == null) return null;
			
			// Das eigentliche Laden des Objektes in die Sitzung.
			o = s.load(obj, id);
			s.getTransaction().commit();
			
		} catch (Exception e) {
			/* 
			 * Oops: Irgend etwas ist mit der Datenbankanbindung oder
			 * der Abfrage nicht in Ordnung. Die Transaktion wird
			 * umgehend zurückgenommen und es wird ein null als Objekt
			 * zurückgeliefert.
			 */
			s.getTransaction().rollback();
			
			// Report problem
			e.printStackTrace();
			
			return null;
		}		
		return o;
	}
```


----------



## mr.warft (18. Mrz 2008)

Hallo,

kann mir denn keiner noch ein Tip geben???


----------



## semi (18. Mrz 2008)

Schau doch in die Datenbank rein, ob die Einträge, die du erstellst, bereits existieren.
Da du für jeden Datensatz eine Transaktion startest, sind Teile davon bereits "commited", während du 
einen erneuten Versuch startest. Ich tippe darauf, dass die Fehler genau so wörtlich zu nehmen sind, 
wie sie kommen. 
z.B. in diesen Zeilen
	
	
	
	





```
s = new Sparten();
s.setName(Sparte.EDDING); 
s.setSid(Sparte.ID_EDDING); // <-- Unique?
sp.add(s);
```
ist mir unklar, was die Identität von Sparten ist. Die SID ist es vermutlich nicht. Es sieht mehr nach einem 
Kürzel bzw. einem logischen Key des Datensatzes. Kann es sein, dass keine Id generiert wird und die 
Constraint Verletzung dadurch zustande kommt, dass die Id bei mehreren Datensätzen z.B. 0 ist?
Oder ist dieses Sparte.ID_EDDING tatsächlich die Id des Datensatzes. In diesem Fall hast du u.U. zwei
Instanzen von Sparten, die die gleiche Id haben.
Eine Ferndiagnose ist schwierig, wenn man das Ganze nicht sieht und selbst debuggen kann. Es gibt in 
den ganzen Einstellungen vieles, was man falsch machen kann.
Der Code sieht mir nach irgendeiner Initialbefüllung der Datenbank aus und ist sicherlich einfacher lösbar.
Insbesondere vieles von der Logik würde ich in die Entities verlagern (die ganzen Collection-Zugriffe etc.)
Du neigst stark dazu, die Interna der Entities ausserhalb der Entities zu manipulieren und sie wieder
zu aktualisieren. Das ist viel "Drumherum", was den Code unübersichtlicher und ganz sicher schwieriger 
wartbar macht.

Ich würde dir wirklich empfehlen auf JPA umzusteigen. Du setzt bereits Annotations ein, was mindestens 
JDK 1.5 impliziert, trotzdem immer noch die "alten" Mappingdateien von Hibernate. Die Sessions und 
HibernateUtils kannst du dir dann auch sparen und stattdessen einfach den EntityManager und eine einzige
Transaktion für das ganze "Import/Export-Business"  verwenden.


----------

