※準備工作
DROP TABLE PERSON_TOILET PURGE; DROP TABLE TOILET PURGE; DROP TABLE PERSON PURGE; CREATE TABLE PERSON( PID NUMBER(5), PNAME VARCHAR(20), CONSTRAINT PERSON_PK PRIMARY KEY(PID) ); CREATE TABLE TOILET( TID NUMBER(5), TNAME VARCHAR(20), CONSTRAINT TOILET_PK PRIMARY KEY(TID) ); CREATE TABLE PERSON_TOILET( PERSON_ID NUMBER(5), TOILET_ID NUMBER(5), CONSTRAINT FK_PID FOREIGN KEY(PERSON_ID) REFERENCES PERSON(PID) ON DELETE CASCADE, CONSTRAINT FK_TID FOREIGN KEY(TOILET_ID) REFERENCES TOILET(TID) ON DELETE CASCADE ); CREATE SEQUENCE TOILET_SEQ; CREATE SEQUENCE PERSON_SEQ;
※XML設定
Person.java
public class Person { private Integer pid; private String pname; private Set<Toilet> toilets = new HashSet<>(); // setter/getter... }
Toilet.java
public class Toilet { private Integer tid; private String tname; private Set<Person> persons = new HashSet<>(); // setter/getter... }
Person.hbm.xml
<class name="vo.Person" table="PERSON"> <id name="pid" type="java.lang.Integer"> <column name="PID" /> <generator class="sequence"> <param name="sequence">PERSON_SEQ</param> </generator> </id> <property name="pname" type="java.lang.String" column="PNAME" /> <set name="toilets" table="PERSON_TOILET" cascade="save-update"> <key column="PERSON_ID" /> <many-to-many column="TOILET_ID" class="vo.Toilet" /> </set> </class>
Toilet.hbm.xml
<class name="vo.Toilet" table="TOILET"> <id name="tid" type="java.lang.Integer"> <column name="TID" /> <generator class="sequence"> <param name="sequence">TOILET_SEQ</param> </generator> </id> <property name="tname" type="java.lang.String" column="TNAME" /> <set name="persons" table="PERSON_TOILET" cascade="save-update" inverse="true"> <key column="TOILET_ID" /> <many-to-many column="PERSON_ID" class="vo.Person" /> </set> </class>
※兩個xml設定的cascade都設成save-update,因為多對多很少刪除的時候,另一張也刪除的
新增測試
Toilet t1 = new Toilet(); t1.setTname("便所1號"); Toilet t2 = new Toilet(); t2.setTname("便所2號"); Toilet t3 = new Toilet(); t3.setTname("便所3號"); Person p1 = new Person(); p1.setPname("掃便所英俠"); Person p2 = new Person(); p2.setPname("茅坑王"); // -----設定互相關聯----- // 掃便所英俠可以上廁所1~3號 p1.getToilets().add(t1); p1.getToilets().add(t2); p1.getToilets().add(t3); t1.getPersons().add(p1); t2.getPersons().add(p1); t3.getPersons().add(p1); // 茅坑俠可以上廁所1、3號 p2.getToilets().add(t1); p2.getToilets().add(t3); t1.getPersons().add(p2); t3.getPersons().add(p2); s.save(p1); s.save(p2); // s.save(t1); // s.save(t2); // s.save(t3); tx.commit();
※只新增其中之一也是可以的
※不管是save(person)或save(toilet)都可以新增成功,三張表都有值因為有互相關聯,因為有設cascade="all"的關係
※如果全不設cascade,也就是cascade="none"且在person設定inverse="true"
如果不打開,只新增p1、p2,那就只會新增person表,其他兩張表不會新增
而新增t1、t2、t3會報「org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: vo.Toilet」的錯
如果是在toilet設定inverse="true",情況就相反,解決方法是將那三行註解打開
修改Person測試
Person person = (Person) s.get(Person.class, 33);// 掃便所英俠的ID person.setPname("吸茅坑大王"); for (Toilet toilet : person.getToilets()) { toilet.setTname("廁" + toilet.getTname().substring(1)); } s.update(person); tx.commit();
修改Toilet測試
Toilet toilet = (Toilet) s.get(Toilet.class, 63);// 便所1號的ID toilet.setTname("廁所1號"); for (Person p : t.getPersons()) { p.setPname("ooooo"); } s.update(toilet); tx.commit();
查詢Person測試
// 查詢掃便所英俠能上哪些廁所 Person p1 = (Person) s.get(Person.class, 33);// 掃便所英俠的ID s.close(); System.out.println(p1.getPname()); // 必需在Person.hbm.xml設定lazy="false" for (Toilet toi : p1.getToilets()) { System.out.println(toi.getTname()); }
查詢Toilet測試
// 查詢便所1號能被哪些人使用 Toilet t1 = (Toilet) s.get(Toilet.class, 63);// 便所1號的ID s.close(); System.out.println(t1.getTname()); // 必需在Toilet.hbm.xml設定lazy="false" for (Person per : t1.getPersons()) { System.out.println(per.getPname()); }
刪除Person測試
Person person = (Person) s.get(Person.class, 33); s.delete(person); tx.commit();
※記得要將cascade改成delete相關,才能一次刪兩張表,不然會出「deleted object would be re-saved by cascade (remove deleted object from associations): [vo.Toilet#61]」的錯
※如果只想刪Person和中間表,可以多加一行person.setToilets(null);將關聯刪除
刪除Toilet測試
Toilet t = (Toilet) s.get(Toilet.class, 64); s.delete(t); tx.commit();
※刪除Toilet要小心,如果資料庫有值,會將表清空
※如果只想刪Toilet和中間表,可以多加一行t.setPersons(null);將關聯刪除
※Annotation設定
Person.java
@Entity @Table public class Person { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "OOO") @SequenceGenerator(name = "OOO", sequenceName = "PERSON_SEQ") private Integer pid; private String pname; @ManyToMany(cascade = CascadeType.ALL, mappedBy = "persons") @LazyCollection(value = LazyCollectionOption.FALSE) private Set<Toilet> toilets = new HashSet<>(); // setter/getter... }
Toilet.java
@Entity @Table public class Toilet { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "XXX") @SequenceGenerator(name = "XXX", sequenceName = "TOILET_SEQ") private Integer tid; private String tname; @ManyToMany(cascade = CascadeType.ALL) @LazyCollection(value = LazyCollectionOption.FALSE) @JoinTable(name = "PERSON_TOILET", joinColumns = { @JoinColumn(name = "TOILET_ID", updatable = false) }, inverseJoinColumns = { @JoinColumn(name = "PERSON_ID", updatable = false) }) private Set<Person> persons = new HashSet<>(); // setter/getter... }
※有問題的地方是刪除,只要資料庫真的有值,就會將全部的表(三張表)清空
※我將cascade改成 cascade = { CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH }就可以成功了,但中間表也都會刪除
Annotation的cascade我知道的就有三種,我也不知道差在哪?等我有空在研究了,先把原始碼貼出來方便觀看
@ManyToMany(cascade = javax.persistence.CascadeType.ALL) @Cascade(value = { org.hibernate.annotations.CascadeType.ALL }) @OnDelete(action = org.hibernate.annotations.OnDeleteAction.CASCADE)
※有兩個enum都叫CascadeType,但package是不同的哦
package javax.persistence; public enum CascadeType { /** Cascade all operations */ ALL, /** Cascade persist operation */ PERSIST, /** Cascade merge operation */ MERGE, /** Cascade remove operation */ REMOVE, /** Cascade refresh operation */ REFRESH, /** * Cascade detach operation * * @since Java Persistence 2.0 * */ DETACH }
package org.hibernate.annotations; /** * Cascade types (can override default EJB3 cascades */ public enum CascadeType { ALL, PERSIST, MERGE, REMOVE, REFRESH, DELETE, SAVE_UPDATE, REPLICATE, /** @deprecated use @OneToOne(orphanRemoval=true) or @OneToMany(orphanRemoval=true) */ @Deprecated DELETE_ORPHAN, LOCK, /** @deprecated use javax.persistence.CascadeType.DETACH */ @Deprecated EVICT, DETACH }
package org.hibernate.annotations; public enum OnDeleteAction { /** * the default */ NO_ACTION, /** * use cascade delete capabilities of the DD */ CASCADE }
沒有留言:
張貼留言