niedziela, 14 października 2012

Hibernate - Problem z listami przy relacji ManyToMany

Hibernate mimo swoich wielu zalet, ma również wiele wad :). Jedną z nich jest sposób obsługi java.util.List przy próbie mapowania @ManyToMany. W sumie przy pierwszym kontakcie wszystko działa bez zarzutu, ale w momencie gdy chcemy dodać coś do listy, w której są już jakieś inne elementy, to okazuje się, że jednak nie wszystko działa w 100% dobrze. W naszej konsoli widzimy mniej więcej taki log:
Hibernate: insert into Item (name, id) values (?, ?)
Hibernate: delete from OrderTab_Item where OrderTab_id=?
Hibernate: insert into OrderTab_Item (OrderTab_id, items_id) values (?, ?)
Hibernate: insert into OrderTab_Item (OrderTab_id, items_id) values (?, ?)
Hibernate: insert into OrderTab_Item (OrderTab_id, items_id) values (?, ?)
Hibernate: insert into OrderTab_Item (OrderTab_id, items_id) values (?, ?)
Czyli w momencie wstawienia jakiegoś wiersza do listy, Hibenrate usuwa wszystkie relacje a następnie z powrotem je dodaje. W moim przykładzie dodajemy 1 element do listy już 3 elementowej. W związku z tym Hibenrate postanowił usunąć wszystkie relacji a następnie po kolei dodał całkiem nowe 4 wiersze. Przykład encji oraz kodu który który dodaje do encji widzimy poniżej.
Encje w relacji many-to-many.
@Entity
@Table(name="OrderTab")
public class Order implements Serializable {

 @Id
 @GeneratedValue
 private Long id;

 private String name;

 @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.EAGER)
 private List items;

 // ... GET and SET
}
@Entity
public class Item implements Serializable {

 @Id
 @GeneratedValue
 private Long id;

 private String name;

 // ... GET and SET

}
Prosta metoda dodająca Item do Orderu.
public void addItem(long orderId, Item item) {

 Order o = em.find(Order.class, orderId);
 o.getItems().add(item);

 em.merge(o);

}
Rozwiązanie problemu
Wystarczy zmienić typ kolekcji w której przechowywujemy elementy z java.util.List na java.util.Set i wszystko będzie działać zgodnie z oczekiwaniami. Encja Order po zmianie wygląda tak:
@Entity
@Table(name="OrderTab")
public class Order implements Serializable {

 @Id
 @GeneratedValue
 private Long id;

 private String name;

 @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.EAGER)
 private Set items;

 // ... GET and SET
}
a log w konsoli tak:
Hibernate: insert into Item (name, id) values (?, ?)
Hibernate: insert into OrderTab_Item (OrderTab_id, items_id) values (?, ?)
Pamiętajcie tylko że jak korzystacie ze zbiorów zamiast list, to należy doimplementować metody equals i hashCode.

Brak komentarzy:

Prześlij komentarz