2016年1月1日 星期五

多對多單向FK關聯(關聯表有主鍵或其他欄位) (Hibernate3.x 三十二)

※準備工作

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(
    PT_ID NUMBER(5),
    INSERT_TIME DATE,
    OTHER VARCHAR(20),
    PERSON_ID NUMBER(5),
    TOILET_ID NUMBER(5),
    CONSTRAINT PERSON_TOILET_PK PRIMARY KEY(PT_ID),
    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;
CREATE SEQUENCE PERSON_TOILET_SEQ;



※XML設定

Person.java

public class Person {
    private Integer pid;
    private String pname;
    private Set<PersonToilet> personToilets = new HashSet<>();
    // setter/getter...
}



Toilet.java

public class Toilet {
    private Integer tid;
    private String tname;
    // setter/getter...
}



PersonToilet.java

public class PersonToilet {
    private Integer ptId;
    private Date insertTime;
    private String other;
    private Person centerPerson;
    private Toilet centerToilet;
    // 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="personToilets" inverse="true" cascade="save-update"
        lazy="false">
        <key column="PERSON_ID" />
        <one-to-many class="vo.PersonToilet" />
    </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" />
</class>



PersonToilet.hbm.xml

<class name="vo.PersonToilet" table="PERSON_TOILET">
    <id name="ptId" type="java.lang.Integer">
        <column name="PT_ID" />
        <generator class="sequence">
            <param name="sequence">PERSON_TOILET_SEQ</param>
        </generator>
    </id>
    
    <property name="insertTime" type="java.util.Date" column="INSERT_TIME" />
    <property name="other" type="java.lang.String" column="OTHER" />
    
    <many-to-one name="centerPerson" column="PERSON_ID" not-null="true" cascade="save-update" class="vo.Person" />
    
    <many-to-one name="centerToilet" column="TOILET_ID" lazy="false" not-null="true" cascade="save-update" class="vo.Toilet" />
</class>



新增測試

Session s = HibernateUtil2.getSession();
Transaction tx = s.beginTransaction();
try {
    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("茅坑王");
    
    // 因為掃便所英俠可以上3間廁所,而茅坑王可以上2間廁所,所以要創5個實體
    PersonToilet pt1 = new PersonToilet();
    PersonToilet pt2 = new PersonToilet();
    PersonToilet pt3 = new PersonToilet();
    PersonToilet pt4 = new PersonToilet();
    PersonToilet pt5 = new PersonToilet();
    
    // -----設定關聯-----
    // 掃便所英俠(p1)可以上便所1號(t1)
    // 此時的中間表就代替了Toilet的位置,然後自己在去關聯Toilet
    p1.getPersonToilets().add(pt1);
    pt1.setCenterPerson(p1);
    pt1.setCenterToilet(t1);// 就是這時去關聯Toilet
    
    // 掃便所英俠(p1)可以上廁所2號(t2)
    p1.getPersonToilets().add(pt2);
    pt2.setCenterPerson(p1);
    pt2.setCenterToilet(t2);
    
    // 掃便所英俠(p1)可以上廁所3號(t3)
    p1.getPersonToilets().add(pt3);
    pt3.setCenterPerson(p1);
    pt3.setCenterToilet(t3);
    
    // 茅坑王(p2)可以上廁所1號(t1)
    p2.getPersonToilets().add(pt4);
    pt4.setCenterPerson(p2);
    pt4.setCenterToilet(t1);
    
    // 茅坑王(p2)可以上廁所3號(t3)
    p2.getPersonToilets().add(pt5);
    pt5.setCenterPerson(p2);
    pt5.setCenterToilet(t3);
    
    // 關聯表的其他兩個欄位
    final Date date = new Date();
    pt1.setInsertTime(date);
    pt2.setInsertTime(date);
    pt3.setInsertTime(date);
    pt4.setInsertTime(date);
    pt5.setInsertTime(date);
    
    final String str = "insert";
    pt1.setOther(str);
    pt2.setOther(str);
    pt3.setOther(str);
    pt4.setOther(str);
    pt5.setOther(str);
    
    s.save(pt1);
    s.save(pt2);
    s.save(pt3);
    s.save(pt4);
    s.save(pt5);
    tx.commit();
} catch (Exception e) {
    tx.rollback();
    System.err.println("例外錯誤!");
    e.printStackTrace();
} finally {
    if (s.isOpen()) {
        s.close();
    }
}



修改測試

Person person = (Person) s.get(Person.class, 182);// 掃便所英俠的ID
person.setPname("吸茅坑大王");
    
Date date = new Date();
for (PersonToilet pt : person.getPersonToilets()) {
    pt.setInsertTime(date);
    pt.setOther("update");
    Toilet t = pt.getCenterToilet();
    t.setTname("廁" + t.getTname().substring(1));
}
    
s.update(person);
tx.commit();



查詢測試

Person p = (Person) s.get(Person.class, 182);// 掃便所英俠的ID
s.close();
System.out.println(p.getPname());
    
// 必需在Person.hbm.xml和PersonToilet.hbm.xml的centerToilet設定lazy="false"
for (PersonToilet pt : p.getPersonToilets()) {
    System.out.println(pt.getCenterToilet().getTname());
}



刪除測試

Person person = (Person) s.get(Person.class, 182);
s.delete(person);
tx.commit();

※和多對多雙向FK關聯一樣,我將cascade拿掉或設none才會成功



※Annotation設定

Person.java

@Entity
@Table
public class Person {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "XXX")
    @SequenceGenerator(name = "XXX", sequenceName = "PERSON_SEQ")
    private Integer pid;
    private String pname;
    
    @OneToMany(mappedBy = "centerPerson", cascade = CascadeType.ALL)
    @LazyCollection(value = LazyCollectionOption.FALSE)
    private Set<PersonToilet> personToilets = 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;
    
    // setter/getter...
}



PersonToilet.java

@Entity
@Table(name = "PERSON_TOILET")
public class PersonToilet {
    @Id
    @Column(name = "PT_ID")
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "XXX")
    @SequenceGenerator(name = "XXX", sequenceName = "PERSON_TOILET_SEQ")
    private Integer ptId;
    
    @Column(name = "INSERT_TIME")
    private Date insertTime;
    
    private String other;
    
    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "PERSON_ID", unique = true)
    private Person centerPerson;
    
    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "TOILET_ID", unique = true)
    private Toilet centerToilet;
    
    // setter/getter...
}

※和第三十篇一樣,刪除都是最奇怪的地方

沒有留言:

張貼留言