org.hibernate.TransientObjectException
使用JPA注解@ManyToMany做一个多对多的用例。
为了避免在删除主表数据时同时级联删除从表数据,JPA官方文档建议在主表的从表字段使用级联注解:CascadeType.PERSIST,CascadeType.MERGE,进行配置。
主表代码:
1 private Set<Hobby> hobby; 2 3 @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.EAGER) 4 @JoinTable(name = "CAT_HOBBY", 5 joinColumns = { @JoinColumn(name = "CAT_ID") }, 6 inverseJoinColumns = { @JoinColumn(name = "HOBBY_ID") }) 7 public Set<Hobby> getHobby() { 8 return hobby; 9 }
从表代码:
1 private Set<Cat> cat; 2 3 @ManyToMany(mappedBy = "hobby",fetch=FetchType.LAZY) 4 public Set<Cat> getCat() { 5 return cat; 6 } 7 public void setCat(Set<Cat> cat) { 8 this.cat = cat; 9 }
Dao层代码:
1 public static Object save(Object obj){ 2 Session session = HibernateUtil.getSession(); 3 Transaction tx = null; 4 try { 5 tx = session.beginTransaction(); 6 session.save(obj); 7 tx.commit(); 8 } catch (RuntimeException e) { 9 if (tx != null) { 10 tx.rollback(); 11 } 12 throw e; 13 } finally { 14 session.close(); 15 } 16 return obj; 17 }
main代码:
1 Cat cat1 = new Cat(); 2 Cat cat2 = new Cat(); 3 cat1.setCat_name("b12_ManyToMany5"); 4 cat2.setCat_name("b12_ManyToMany6"); 5 6 Set<Hobby> hobbies = new HashSet<Hobby>(); 7 Hobby hobby1 = new Hobby(); 8 hobby1.setName("球1"); 9 hobbies.add(hobby1); 10 11 Hobby hobby2 = new Hobby(); 12 hobby2.setName("球2"); 13 hobbies.add(hobby2); 14 15 cat1.setHobby(hobbies); 16 cat2.setHobby(hobbies); 17 18 HibernateUtil.save(cat1); 19 HibernateUtil.save(cat2); 20 System.out.println(cat1.getId()); 21 System.out.println(cat2.getId());
运行main方法,第18行报如下异常,这应该是一个Hibernate实现JPA接口的一个bug。
org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: b12_ManyToMany_JoinTable.Hobby
策略一,使用CascadeType.ALL注解,可以保存,但在删除操作时,产生ERROR: ORA-02292: 违反完整约束条件 (WXUATUSER.FK_ODDM1C9RTQFPO2HI2BCR4OUQX) - 已找到子记录。不可取。
策略二,改用persist方法保存,报异常:detached entity passed to persist。不可取。
1 tx = session.beginTransaction(); 2 session.persist(obj); 3 tx.commit();
策略三,使用Hibernate的注解@Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE}),增删改查,达到预期的级联效果,删除主表数据不会级联到从表。可取。