※準備工作
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
}
沒有留言:
張貼留言