1.many-to-one 以學生和部門之間的關係為例: Department.hbm.xml student.hbm.xml 應用: Student stu1=new Student(); stu.setName("宋江"); Student stu2=new Student();stu.setN ...
1.many-to-one
以學生和部門之間的關係為例:
Department.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.xidian.domain">
<class name="Department" lazy="false">
<!-- 配置主鍵屬性 -->
<id name="id" type="java.lang.Integer">
<!-- 生成策略 -->
<generator class="identity">
</generator>
</id>
<property name="name" type="java.lang.String">
<column name="name" length="64" not-null="true"/>
</property>
</class>
</hibernate-mapping>
student.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.xidian.domain">
<class name="Student">
<id name="id" type="java.lang.Integer">
<generator class="identity">
</generator>
</id>
<property name="name" type="java.lang.String">
<column name="name" length="64"/>
</property>
<!-- 對於private Department dept;就不能使用property
column="dept_id" 表示將來自動生成的表的外鍵名-->
<many-to-one name="dept" column="dept_id"/>
</class>
</hibernate-mapping>
應用:
Student stu1=new Student();
stu.setName("宋江");
Student stu2=new Student();
stu.setName("宋江");
Department d=new Department();
d.setName("財務部");
stu1.setDept(d);//指定該學生是哪個部門
stu2.setDept(d);
s.save(d);
s.save(stu1);
s.save(stu2);
如果先保存學生再保存部門,也是可以的。在保存學生的時候部門還不存在,外鍵會設置為空,然後再保存部門,當commit提交事務的
時候發現它們具有映射關係,就會再來設置學生的外鍵引用(這種情況下hibernate會多一條update語句)。
在應用多對一的情況時會發現,只做了many到one單方向的映射,只是能將多個學生通過外鍵關係添加到一個部門中。
問題:反過來如果需要通過一個部門號(1),來獲取該部門的所有學生?如果用上面的many to one形式需要hql語句進行查詢:
String hql=”from Student where dept.id=1”
這時就需要one-to-many的反方向查詢。
在這裡引入懶載入的概念:
簡述: 多表查詢時,當我們查詢一個對象的時候,在預設情況下,返回的只是該對象的普通屬性,當用戶去使用對象屬性時,
才會向資料庫發出再一次的查詢.這種現象我們稱為 lazy現象.
student=(Student) s.get(Student.class, 3);
System.out.println(student1.getName()+" 所在部門="
+student1.getDept().getName());//當session還在,依然可以向資料庫發sql語句。可是假若我們只是獲取到student對象,
//沒有對對象屬性進行查詢,當session關閉後就不可以再向資料庫發sql語句,無法查詢。
解決方法:
1.顯示初始化代理對象 Hibernate.initized( student.getdept())
2.修改對象關係文件 class屬性 lazy 改寫 lazy=false
3.通過過濾器(web項目) openSessionInView (從上面的例子可以看出,當session關閉後,就不能向資料庫發sql語句,那麼就擴大session的範圍
這個後面會詳細介紹)
2.one-to-many
在one那一方配置<set ></set>
在many一方必須要一個外鍵指向one這方的屬性,所以配置<many-to-one>是必須的。
而one這方是否配置<one-to-many>看是否需要指定一個集合屬性指向回many一方,這樣從主表一方獲取從表的屬性是非常方便的。
在這裡再看通過獲取到部門查看學生信息:
Department department1=(Department) s.get(Department.class, 3);
//取出該部門的學生
Set<Student> stus= department1.getStus();
for(Student ss: stus){
System.out.println(ss.getName());
}
添加學生
Department department=new Department();
department.setName("業務部門");
Student stu1=new Student();
stu1.setName("順平");
Student stu2=new Student();
stu2.setName("小明");
Set<Student> sets=new HashSet<Student>();
sets.add(stu1);
sets.add(stu2);
department.setStus(sets);
s.save(department); //這裡涉及到級聯操作,需要在Department.hbml.xml中配置
//<set name="stus" cascade="save-update">這樣將在保存department的時候將
//部門中的學生一併保存。
3.one-to-one
一對一有有兩種方式 區別是基於主鍵的情況是會在主鍵之間形成映射關係
(從表的某一屬性既是自身表的主鍵也是引用到其他表的外鍵,主鍵充當外鍵),基於外鍵是以非主鍵的外鍵形成映射關係。
(1) 基於主鍵的一對一
一定要設置constrained=”true” 才能形成外鍵約束關係
一對一關係中典型的問題是人與身份證的關係。
Person p1=new Person();
p1.setId(1224);
p1.setName("xkj");
IdCard idCard=new IdCard();
idCard.setValidateDte(new Date());
idCard.setPerson(p1);//表示idCard對象是屬於p1這個對象.
//p1.setIdCard(idCard);錯誤方式
s.save(p1); //先保存人
s.save(idCard);
資料庫中結果:
idCard表
person表
這裡有兩個註意問題:
1.如果我使用p1.setIdCard(idCard)來指定person和idCard之間的關係會出錯:
只能使用idCard.setPerson(p1)方式。關係應該是在idCard上有一個外鍵指向person,而不是相反。報錯行在
說明在保存idCard的時候,外鍵person屬性是空而導致錯誤。
2.如果調換保存順序:
s.save(idCard); //先保存卡
s.save(p1);
報出錯誤:
java.sql.SQLException: [Microsoft][SQLServer 2000 Driver for JDBC][SQLServer]
INSERT 語句與 FOREIGN KEY 約束"FKB8CDF6CB2A59F864"衝突。該衝突發生於資料庫"test",表"dbo.person", column 'id'。
解析理論:外鍵約束,比如B表存在一個欄位b,有外鍵約束,引用於A表的主鍵a,那麼在向B表插入數據時,
欄位b必須為A表中a已經存在的值,如過向b中存放一個a中沒有的值,則會報違反外鍵約束。
可是如果回頭看one-to-many那個例子中卻發現學生表中有外鍵指向部門的主鍵,先保存學生再保存部門也是可以的,
只是會多一個update語句,這是hibernate的優化機制。可是放在這個地方卻不可以,why?
希望大家指點填坑!
(二)基於外鍵的一對一
只改動上面的IdCard.hbm.xml映射文件
<id name=”id” type=”java.lang.Integer”>
<generator class=”assigned” />
</id>
<property ame=”VaidateDate” type=”java.util.Date”>
<column name=”validateDate” />
</property>
<many-to-one name=”person” unique=”true” /> //person是外鍵 在idCard表中會生成這個外鍵,它指向的是主表的主鍵id
在idCard這邊,可看成是many-to-one的一個特例,加unique保證唯一。
Person p1=new Person();
p1.setId(122);
p1.setName("xkj");
IdCard idCard=new IdCard();
idCard.setId(1905);
idCard.setValidateDte(new Date());
idCard.setPerson(p1);//表示idCard對象是屬於p1這個對象.
s.save(p1); //先保存人
s.save(idCard);
資料庫結果:
在這裡我依然測試了前面出現的兩個問題:
第一個問題沒有報錯,只是idCard的person外鍵為空。
第二個問題hibernate又給解決了,同樣是多一句update語句。
4.many-to-many
學生和課程是經典的多對多的案例。
在實際開發中,如果出現了many-to-many關係,我們應該將其轉換成兩個one-to-many 或者 many-to-on, 這個程式好控制,同時不會有冗餘。
在stucourse中有兩個外鍵分別指向student和course
//添加一個學生,一門課程,選課
Student stu1=new Student();
stu1.setName("小明");
Course course=new Course();
course.setName("java");
StuCourse sc=new StuCourse();
sc.setCourse(course);
sc.setStudent(stu1);
//順序保存.
s.save(stu1);
s.save(course);
s.save(sc);
資料庫結果: