# hibernate probleme



## ARadauer (12. Aug 2008)

grundsätzlich hab ich folgendes vor.. ganz simple master detail ansichten.... ich hab ein sehr komplexes objekt, eine berstellung, ich zeige alle bestellungen in einem jtable an, klickt der benutzer auf eine zeile wird ein detail formular gefüllt. Das detail benötigt aber viel mehr daten, kunden, bestellte artikel usw.. die bestellung verweißt auf diese objekte... eager loading lädt alles brav mit, aus performance gründen will ich aber lazy nachladen...

aber wie mach ich das, ich lade eine liste mit den bestellungen und stell sie in der table dar, klickt der benutzer auf eine bstellung greif ich auf die objekte in meiner bestellung zu.. klar LazyInitializationException... jetzt hab ich mir gedacht ich lade das eine objekt neu.. öffne eine session und greif auf alle elemente zu dich ich später brauche... klappt aber das ist doch nicht sinn der sache, wie mach ich sowas normalerweise.... session die ganze zeit offen lassen?

ich lade ungefähr so...

```
public class OrderDao extends HibernateDaoSupport implements IOrderDao {
...
    //alle orders
    public List<Order> getOrders() {
        List<Order> result = (List<Order>) getHibernateTemplate().find("from Order");
        return result;
    }
    ...
    public Order getOrderById(Integer id) {
        	Session session = getHibernateTemplate().getSessionFactory().openSession();
	session.beginTransaction();
	Order o =  (Order) session.load(Order.class, id);
	if(o.getOrderedProducts().size()>0)
		o.getOrderedProducts().get(o.getOrderedProducts().size()-1);
	if(o.getOrderHistory().size()>0)
		o.getOrderHistory().get(o.getOrderHistory().size()-1);
	o.getDelivery().getFirstname();
	o.getShipping().getShippingPartner();
	o.getPayment().getPaymentInfo();
	session.close();
	return o;
    }    
..
}
```

geht das irgendwie eleganter?

ok das war mein erstes problem.

dann mein zweites... ich speicher meine Bestellungen über den merge befehl...

```
getHibernateTemplate().merge(o);
```
ich hab ein paar versandarten, verkäufer usw.. diese lade ich, ich lade eine orderobjekt, das zufällig schon die selbe versandart gesetzt hat, ich speicher die order mit

```
getHibernateTemplate().saveOrUpdate(o);
```
bekomm ich fehler..


> a different object with the same identifier value was already associated with the session:


mhn mit merge passiert das nicht... funtioniert soweit alles 
also zu meinem problem ich hab zb bestellte produkte als list in der order


```
@OneToMany(mappedBy = "order", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public List<OrderedProduct> getOrderedProducts() {
   return orderedProducts;
}
```
wenn ich nun einen artikel lösche und mit merge die order speicher, bleibt der artikel noch immer in der datenbank... muss ich den artikel seperat löschen.. schaft mir das nicht das CascadeType.ALL wie mach ich das am besten?

kann sein dass das ein wenig verwirrend ist, aber ich hab heute schon 6 bier getrunken ;-)


----------



## ARadauer (12. Aug 2008)

aja in meinem Ordered Product hab ich einen Verweis auf die Order


```
@ManyToOne
	@JoinColumn(name= "order_fk")
	public Order getOrder() {
		return order;
	}
```


----------



## semi (12. Aug 2008)

Das erste würde ich in eine NamedQuery auslagern, statt jedes mal die Sachen nachzuladen.
z.B.
	
	
	
	





```
@NamedQueries( {
   @NamedQuery(
      name = Order.FETCHDETAILS,
     query = "select o"
           + "  from Order o"
           + "       join fetch o.orderProducts"
           + "       join fetch o.orderHistory"
           + "       join fetch o.shipping s"
           + "       join fetch s.shippingPartner"
           + "       join fetch o.payment p"
           + "       join fetch p.paymentInfo"
           + " where o.id = :orderId"
   )
})
...
public class Order ...
{
   /**
   * Liefert eine Bestellung mit allen dazugehörigen Details.
   *
   * Parameter
   *   orderId: Id der Bestellung
   */
   public static final String FETCHDETAILS = "Order.FetchDetails";

   ...
}

Session session = ...
Query query = session.getNamedQuery(Order.FETCHDETAILS);
query.setParameter("orderId", id);
Order order = (Order)query.getSingleResult();
...
```

Deine Fragen zu der Session und dem Cascade lassen sich nicht beantworten, ohne zu wissen, 
wie deine Anwendung aufgebaut ist. Speziell klingt es so, als ob du tatsächlich die Entities, wie
sie sind, bis zum Client schleifen würdest.


----------



## ARadauer (12. Aug 2008)

> Deine Fragen zu der Session und dem Cascade lassen sich nicht beantworten, ohne zu wissen,
> wie deine Anwendung aufgebaut ist.


mhn also grob eine order und orderedproducts...
so sehen die annotions aus

oder:

```
@OneToMany(mappedBy = "order", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
	public List<OrderedProduct> getOrderedProducts() {
		return orderedProducts;
	}
```

beim product hab ich noch einen verweis auf die order


```
@ManyToOne
	@JoinColumn(name= "order_fk")
	public Order getOrder() {
		return order;
	}
```

ich möchte das ordered product über remove aus der list von order entfernne können und das irgendwie persistieren, anscheinend reicht ein merge der order nicht....

das mit der NamedQuery hört sich gut an... danke


----------



## semi (12. Aug 2008)

Ich meinte eher die grobe Architektur. Ist es eine Web-Anwendung, eine Desktop-Anwendung, irgendeine 
Client/Server-Anwendung etc.?
Wenn es auf einem Application Server läuft, würde ich dir empfehlen ganz normal einen EntityManager zu 
verwenden, statt die Sessions von Hibernate direkt zu verwenden. Einfach direkt JPA, wo es geht, und nur 
in Ausnahmefällen Hibernate-spezifische Features einstreuen, wo es sich nicht vermeiden lässt.


----------



## ARadauer (12. Aug 2008)

es ist eine swing desktop anwendung, im moment nur 2 tier, also vom desktop direkt in die db, ich verwende einen hirachischen mvc ansatz mit einer service schicht von der aus meine model klassen über daos auf die db zugreifen. in den daos verwende ich spring und hibernate.


----------



## ARadauer (12. Aug 2008)

ich fass glaub ich meine frage zusammen:

kann ich bei einer bidirektionalen OneToMany beziehung zb eine bestellung hat mehrer produkte, einfach ein produkt aus der list der bestellung löschen, dann die bestellung speichern und das produkt soll gelöscht werden... geht das?

oder muss ich vorher das produkt löschen, was ich jetzt gemacht habe, ist aber nicht nach meinem sinn, ich will die order speichern und das produkt soll gelöscht werden...


----------



## byte (13. Aug 2008)

Also wegen der Lazy / Eager Geschichte:

Die Properties vorzuladen, indem Du die Getter aufrufst, ist keine tolle Idee. Die Properties werden dann per SELECT nachgeladen. Besser ist die Lösung von semi mit NamedQuery. Oder halt einfach ein spezifisches HQL oder Criteria bauen und die Properties per JOIN mitladen, die Du benötigst.


Was das zweite Problem angeht:

merge() lädt zunächst die Entität in die Session. Offenbar hast Du Deine Session nicht geschlossen, so dass noch ein älteres Objekt an die Session gebunden ist, so dass es beim saveOrUpdate() knallt?


----------



## semi (13. Aug 2008)

ARadauer hat gesagt.:
			
		

> kann ich bei einer bidirektionalen OneToMany beziehung zb eine bestellung hat mehrer produkte, einfach ein produkt aus der list der bestellung löschen, dann die bestellung speichern und das produkt soll gelöscht werden... geht das?


So viel ich weiss, nein. Vielleicht mit den Annotationen von Hibernate, aber mit den Annotationen aus 
javax.persistence nicht. Grundsätzlich ist man für die Relationships selbst zuständig.



			
				ARadauer hat gesagt.:
			
		

> oder muss ich vorher das produkt löschen, was ich jetzt gemacht habe, ist aber nicht nach meinem sinn, ich will die order speichern und das produkt soll gelöscht werden...


Du kannst das Produkt löschen, ohne es aus der Collection in Order zu entfernen (OK, nicht das, was du willst). 
Das sollte funktionieren und wird mit Order synchronisiert.

Die Session für die gesamte Laufzeit der Anwendung offen zu halten sollte funktionieren, erfordert aber etwas Disziplin
beim Umgang mit den Entities. Ein Problem, das du haben wirst, ist das Rückgängig machen von Änderungen
in der GUI und (versehentliches) Laden mehrere Instanzen in die Session. Wenn du z.B. eine Order und die 
dazugehörigen Produkte anzeigst und die Möglichkeit anbietest einzelne Produkte zu löschen und diese direkt 
aus der Collection entfernst. Da gibt es viel Raum für Fehler. Ich würde in diesem Fall eher zu VOs tendieren, 
um eine Manipulation an den Daten zu ermöglichen, ohne mir Gedanken über die Entities zu machen. 
Das erfordert wieder eine Mapping-Schicht in deinen DAOs. Die DAOs würde ich aber sowieso etwas erweitern,
um die Sessions und Transaktionen nicht jedes mal, in jeder Methode, getrennt verwalten zu müssen.
(Stichwort: Factory für DAOs und darin einen Proxy mit Session- und Transaktions- und Exceptionhandling 
instanziiieren)

Um Produkte zum Löschen zu markieren könntest du auch folgendes tun, ist aber nicht so sexy.
	
	
	
	





```
class Product {
   ...
   @Transient
   private boolean deleted;
   ...
}
```
Dann die Liste der Produkte durchgehen und alle mit deleted == true entfernen, wenn der Anwender
auf Speichern geklickt hat.

Machst du die Updates an Produkten und an Order in modalen Dialogen, wo es auch die Möglichkeit gibt die
Änderungen zu verwerfen? Wenn ja, dann kommst du um VOs bzw. ein Model für die GUI nicht herum.


----------



## byte (13. Aug 2008)

semi hat gesagt.:
			
		

> ARadauer hat gesagt.:
> 
> 
> 
> ...


Das geht in der Tat nur mit Hibernate. Guck Dir mal _CascadeType.DELETE_ORPHAN_ an.


----------



## semi (13. Aug 2008)

@byto
 :toll: Genau das.

```
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
@org.hibernate.annotations.Cascade(
   value = org.hibernate.annotations.CascadeType.DELETE_ORPHAN
)
public List<OrderedProduct> getOrderedProducts() {
   return orderedProducts;
}
```


----------



## ARadauer (13. Aug 2008)

super leute!!! 

CascadeType.DELETE_ORPHAN und das NamedQuery von maki, genau das was ich gesucht habe... funktioniert super!

danke


----------



## ARadauer (14. Aug 2008)

ich glaub es ist sinnvoll wenn ich mir mal ein gutes hibernte buch rein ziehe...

kann mir jemand eines empfehlen


----------



## maki (14. Aug 2008)

> das NamedQuery von maki,


War ich doch gar nicht  Denk du meinst semi, Ehre wem Ehre gebürt...

Hibernate Buch? Nimm doch das "Original": http://www.amazon.com/Java-Persistence-Hibernate-Christian-Bauer/dp/1932394885


----------

