2015年12月20日 星期日

一對多單向FK關聯 (Hibernate3.x 二十七)

官方不推薦使用,看原文,希望我們使用join table,也就是多對多的方法


※準備工作

DROP TABLE STUDENT PURGE;
DROP TABLE CLAZZ PURGE;
    
CREATE TABLE CLAZZ(
    CID NUMBER(5),
    CNAME VARCHAR(15),
    CONSTRAINT CLAZZ_PK PRIMARY KEY(CID)
);
    
CREATE TABLE STUDENT(
    SID NUMBER(5),
    SNAME VARCHAR(15),
    CLAZZ_ID NUMBER(5),
    CONSTRAINT STUDENT_PK PRIMARY KEY(SID),
    CONSTRAINT STUDENT_FK FOREIGN KEY(CLAZZ_ID) REFERENCES CLAZZ(CID) ON DELETE CASCADE
);
    
CREATE SEQUENCE CLAZZ_SEQ;
CREATE SEQUENCE STUDENT_SEQ;

※和一對多雙向FK關聯一模一樣



※XML設定


Clazz.hbm.xml

<class name="vo.Clazz" table="CLAZZ">
    <id name="cid" type="java.lang.Integer">
        <column name="CID" />
        <generator class="sequence">
            <param name="sequence">CLAZZ_SEQ</param>
        </generator>
    </id>
    
    <property name="cname" type="java.lang.String">
        <column name="CNAME" />
    </property>
    
    <set name="setStudent" inverse="false" cascade="all" lazy="false" fetch="join">
        <key column="CLAZZ_ID" not-null="true" />
        <one-to-many class="vo.Student" />
    </set>
</class>

※inverse一定要false(因為Student不知道Clazz存在),要自己維護,所以會產生多餘的SQL,可能就是因為這樣,所以官方不推薦使用


Student.hbm.xml

<class name="vo.Student" table="STUDENT">
    <id name="sid" type="java.lang.Integer">
        <column name="SID" />
        <generator class="sequence">
            <param name="sequence">STUDENT_SEQ</param>
        </generator>
    </id>
    
    <property name="sname" type="string">
        <column name="SNAME" />
    </property>
</class>

Clazz.java

public class Clazz {
    private Integer cid;
    private String cname;
    private Set<Student> setStudent = new HashSet<>();
    // setter/getter...
}

Student.java

public class Student {
    private Integer sid;
    private String sname;
    // setter/getter...
}

新增測試

Clazz clazz = new Clazz();
clazz.setCname("1年1班");

for (int i = 1; i <= 2; i++) {
    Student stu = new Student();
    stu.setSname("小" + i + "號");
    clazz.getSetStudent().add(stu);
}
    
s.save(clazz);
tx.commit();

※如果inverse="true",關聯的字段(CLAZZ_ID)是null,不會報錯,因為多方Student不知道一方Clazz的存在,我的<key> 有設not null,但也沒報錯誤訊息; 而<set>沒有not null 選項

修改測試

Clazz clazz = new Clazz();
clazz.setCid(51);
clazz.setCname("1年2班");
    
String sql = " SELECT * FROM STUDENT WHERE CLAZZ_ID = :id ";
SQLQuery query = s.createSQLQuery(sql)
        .addScalar("SID", StandardBasicTypes.INTEGER)
        .addScalar("SNAME", StandardBasicTypes.STRING);
query.setParameter("id", clazz.getCid());
    
@SuppressWarnings("unchecked")
List<Object> list = query.list();
for (int i = 0; i < list.size(); i++) {
    Object[] o = (Object[]) list.get(i);
    
    Student stu = new Student();
    stu.setSid((Integer) o[0]);
    stu.setSname("小" + i);
    clazz.getSetStudent().add(stu);
}
    
s.update(clazz);
tx.commit();

查詢一方(Clazz)測試

Clazz clazz = (Clazz) s.get(Clazz.class, 51);
s.close();
System.out.println(clazz.getCname());
    
for (Student stu : clazz.getSetStudent()) {
    System.out.println(stu.getSname());
}

※預設是select,lazy="false"才能查到Student的表

刪除一方(Clazz)測試

Clazz clazz = (Clazz) s.get(Clazz.class, 51);
s.delete(clazz);
tx.commit();



※Annotation設定

Clazz.java

@Entity
@Table
public class Clazz {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "XXX")
    @SequenceGenerator(name = "XXX", sequenceName = "CLAZZ_SEQ")
    private Integer cid;
    
    @Column
    private String cname;
    
    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinColumn(name = "CLAZZ_ID")
    @LazyCollection(value = LazyCollectionOption.FALSE)
    private Set setStudent = new HashSet<>();
    
    // setter/getter...
}

Student.java

@Entity
@Table
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "OOO")
    @SequenceGenerator(name = "OOO", sequenceName = "STUDENT_SEQ")
    private Integer sid;
    
    @Column
    private String sname;
    
    // setter/getter...
}

※測試的結果和XML設定一樣,包括查詢的預設值也是select(因為之前的X對X,Annotation的設定預設幾乎都是join)

沒有留言:

張貼留言