※而因為是雙向,所以一對多也是多對一,看你站在哪個角度想而已
※準備工作
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,讓多方去維護關係,這樣才不會出現多的語句,影響效能
沒有留言:
張貼留言