※而因為是雙向,所以一對多也是多對一,看你站在哪個角度想而已
※準備工作
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欄位與一那邊的PK相關聯
※XML設定
hibernate.cfg.xml
<hibernate-configuration> <session-factory> <property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property> <property name="hibernate.connection.url">jdbc:oracle:thin:@127.0.0.1:1521:orcl</property> <property name="hibernate.connection.username">username</property> <property name="hibernate.connection.password">password</property> <property name="hibernate.dialect">org.hibernate.dialect.OracleDialect</property> <property name="hibernate.show_sql">true</property> <property name="hibernate.format_sql">true</property> <!-- <property name="hibernate.use_sql_comments">true</property> --> <property name="hibernate.transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</property> <property name="hibernate.current_session_context_class">thread</property> <mapping resource="vo/Clazz.hbm.xml" /> <mapping resource="vo/Student.hbm.xml" /> </session-factory> </hibernate-configuration>
clazz.hbm.xml
<hibernate-mapping> <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="true" cascade="all" lazy="false" fetch="join"> <key column="CLAZZ_ID" /> <one-to-many class="vo.Student" /> </set> </class> </hibernate-mapping>
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> <many-to-one name="clazz" column="CLAZZ_ID" not-null="true" lazy="false" fetch="join" /> </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; private Clazz clazz; // setter/getter... }
新增測試
Session s = HibernateUtil2.getSession(); Transaction tx = s.beginTransaction(); try { Clazz clazz = new Clazz(); clazz.setCname("三年丁班"); for (int i = 1; i <= 5; i++) { Student stu = new Student(); stu.setSname("Mary" + i + "號"); stu.setClazz(clazz); clazz.getSetStudent().add(stu); } s.save(clazz); tx.commit(); } catch (Exception e) { tx.rollback(); System.err.println("例外錯誤!"); e.printStackTrace(); } finally { if (s.isOpen()) { s.close(); } }
※只有新增Clazz也可以,這就是網路上所謂的快速註冊與詳細註冊
※如果要新增Student,要設定cascade
修改測試
Clazz clazz = new Clazz(); clazz.setCid(16); clazz.setCname("九年戊班"); 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 + "號"); stu.setClazz(clazz); clazz.getSetStudent().add(stu); } s.update(clazz); tx.commit();
※也可以只修改Clazz
查詢Clazz
Clazz clazz = (Clazz) s.get(Clazz.class, 6); s.close(); System.out.println(clazz.getCname()); for (Student stu : clazz.getSetStudent()) { System.out.println(stu.getSname()); }
※查詢到多的那一方會出Session is closed的錯(查不到資料也會),所以要在Clazz設定lazy="false"
※fetch預設為select
查詢Student
Student stu = (Student) s.get(Student.class, 13); s.close(); System.out.println(stu.getSname()); System.out.println(stu.getClazz().getCname());
※查詢到一的那一方會出Session is closed的錯(查不到資料也會),所以要在Student設定lazy="false"
※fetch預設為select
※fetch="select"或不設定的結果:
Hibernate:
select
student0_.SID as SID1_0_,
student0_.SNAME as SNAME1_0_,
student0_.cid as cid1_0_
from
STUDENT student0_
where
student0_.SID=?
Hibernate:
select
clazz0_.CID as CID0_0_,
clazz0_.CNAME as CNAME0_0_
from
CLAZZ clazz0_
where
clazz0_.CID=?
Hibernate:
select
setstudent0_.cid as cid0_1_,
setstudent0_.SID as SID1_,
setstudent0_.SID as SID1_0_,
setstudent0_.SNAME as SNAME1_0_,
setstudent0_.cid as cid1_0_
from
STUDENT setstudent0_
where
setstudent0_.cid=?
Maza1號
六年甲班
※fetch="join"的結果:
Hibernate:
select
student0_.SID as SID1_1_,
student0_.SNAME as SNAME1_1_,
student0_.cid as cid1_1_,
clazz1_.CID as CID0_0_,
clazz1_.CNAME as CNAME0_0_
from
STUDENT student0_,
CLAZZ clazz1_
where
student0_.cid=clazz1_.CID
and student0_.SID=?
Hibernate:
select
setstudent0_.cid as cid0_1_,
setstudent0_.SID as SID1_,
setstudent0_.SID as SID1_0_,
setstudent0_.SNAME as SNAME1_0_,
setstudent0_.cid as cid1_0_
from
STUDENT setstudent0_
where
setstudent0_.cid=?
Maza1號
六年甲班
※因為是對Student查詢,所以會有查Student的語句,然後執行到要關聯時,又會發出語句,這時就看是select或join了
刪除測試
Clazz clazz = (Clazz) s.get(Clazz.class, 6); s.delete(clazz); tx.commit();
※刪除Clazz就會幫我們把多的一方全刪了,當然資料庫要on delete cascade
※刪除Student(就是刪除多的一方),當然只會刪Student的一筆紀錄而已
※如果資料庫沒有可以刪除的資料就會出「java.lang.IllegalArgumentException: attempt to create delete event with null entity」的錯
※Annotation設定
hibernate.cfg.xml
<mapping class="vo.Clazz" /> <mapping class="vo.Student" />
Clazz.java
package vo; import java.util.HashSet; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.OneToMany; import javax.persistence.SequenceGenerator; import javax.persistence.Table; import org.hibernate.annotations.LazyCollection; import org.hibernate.annotations.LazyCollectionOption; @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, mappedBy = "clazz" ) @LazyCollection(value = LazyCollectionOption.FALSE) private Set<Student> setStudent = new HashSet<>(); // setter/getter... }
※mappedBy裡的字串要對應Student裡的屬性名稱
※lazy="false",在Annotation有兩種一種就是上面針對集合用的,一種就是下面Student.java的,針對單一使用,如果設定錯誤,也不會拋Exception,只是沒作用而已
Student.java
package vo; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.SequenceGenerator; import javax.persistence.Table; import org.hibernate.annotations.LazyToOne; import org.hibernate.annotations.LazyToOneOption; @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; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "CLAZZ_ID") @LazyToOne(value = LazyToOneOption.FALSE) private Clazz clazz; // setter/getter... }
※測試的結果和XML設定有一點小小的不同,就是在查詢多方(Student),而且又設定fetch=join或@ManyToOne(fetch = FetchType.EAGER)時
用Annotation設定是外連接,就是有(+); 而XML設定是內連接,沒有(+)
※其實搜尋的欄位順序和欄位的數目也不太一樣,可以自行試試
※Inverse
※在一方(Clazz)時,inverse預設為false; 在多方(Student)無此設定※查詢不會影響
※inverse就是逆、反的意思,在一方設false,當然就是自己(一方)維護關係;
設成true就是Student(多方)維護關係
※設定為true時 控制台輸出語句如下:
新增:
Hibernate:select
CLAZZ_SEQ.nextval
from
dual
Hibernate:
select
STUDENT_SEQ.nextval
from
dual
Hibernate:
select
STUDENT_SEQ.nextval
from
dual
Hibernate:
insert
into
CLAZZ
(CNAME, CID)
values
(?, ?)
Hibernate:
insert
into
STUDENT
(SNAME, CLAZZ_ID, SID)
values
(?, ?, ?)
Hibernate:
insert
into
STUDENT
(SNAME, CLAZZ_ID, SID)
values
(?, ?, ?)
修改:
Hibernate:SELECT
*
FROM
STUDENT
WHERE
CLAZZ_ID = ?
Hibernate:
update
CLAZZ
set
CNAME=?
where
CID=?
Hibernate:
update
STUDENT
set
SNAME=?,
CLAZZ_ID=?
where
SID=?
Hibernate:
update
STUDENT
set
SNAME=?,
CLAZZ_ID=?
where
SID=?
刪除:
Hibernate:select
clazz0_.CID as CID0_0_,
clazz0_.CNAME as CNAME0_0_
from
CLAZZ clazz0_
where
clazz0_.CID=?
Hibernate:
select
setstudent0_.CLAZZ_ID as CLAZZ3_0_1_,
setstudent0_.SID as SID1_,
setstudent0_.SID as SID1_0_,
setstudent0_.SNAME as SNAME1_0_,
setstudent0_.CLAZZ_ID as CLAZZ3_1_0_
from
STUDENT setstudent0_
where
setstudent0_.CLAZZ_ID=?
Hibernate:
delete
from
STUDENT
where
SID=?
Hibernate:
delete
from
STUDENT
where
SID=?
Hibernate:
delete
from
CLAZZ
where
CID=?
※設定為false或不設定時 控制台輸出語句如下:
新增:
結果為設定true的語句加上下面兩條SQLHibernate:
update
STUDENT
set
CLAZZ_ID=?
where
SID=?
Hibernate:
update
STUDENT
set
CLAZZ_ID=?
where
SID=?
修改:
結果為設定true的語句加上下面三條SQLHibernate:
update
STUDENT
set
CLAZZ_ID=null
where
CLAZZ_ID=?
Hibernate:
update
STUDENT
set
CLAZZ_ID=?
where
SID=?
Hibernate:
update
STUDENT
set
CLAZZ_ID=?
where
SID=?
刪除:
結果為設定true的語句加上下面一條SQLHibernate:
update
STUDENT
set
CLAZZ_ID=null
where
CLAZZ_ID=?
※可以發現設定為false或不設多出來的語句都是針對多方的Student做處理,且都是update
不過多出來的語句似乎沒什麼用,所以在一對多大部分都是設成true,讓多方去維護關係,這樣才不會出現多的語句,影響效能
沒有留言:
張貼留言