关联关系总结:
如何考虑hibernate处理对象之间的关联关系的底层细节时,可以从两方面进行思考
(1)如何将对象之间的关联关系保存到数据库中
(2)如何检索出关联的对象(主语句要看sql)
(映射文件id缺省的情况下)
一对一:1条sql
多对一或是一对多:2条sql
多对多:3条sql
对象集合使用类的一部分
单个属性,来取代一对多(比如只要员工类的名字)
一、set特性:没有顺序,不允许元素重复
表结构:
SQL> desc department;
Name Type Nullable Default Comments
---- ------------- -------- ------- --------
ID NUMBER(10)
NAME VARCHAR2(255)
SQL> desc employee;
Name Type Nullable Default Comments
--------- ------------- -------- ------- --------
ID NUMBER(10)
NAME VARCHAR2(255)
DEPART_ID NUMBER(10)
映射文件:
<set name="emps" >
<key column="depart_id"></key><!-- 告诉hibernate按照什么字段检索employee表数据,必须和employee.hbm.xnml中的外键定义一样-->
<one-to-many class="Employee"></one-to-many> <!--告诉hibernate集合set中装什么类型 -->
</set>
二、list特性:有顺序的,可以元素重复,但是索引不可重复
在employee表中会由hibernate根据Department.hbm.xml一对多的映射关系添加一字段,用于记录是加入的第几个员工
table employee结构
Name Type Nullable Default Comments
--------- ------------- -------- ------- --------
ID NUMBER(10)
NAME VARCHAR2(255)
DEPART_ID NUMBER(10)
ORDER_COL NUMBER(10) Y
映射文件:
<list name="emps">
<key column="depart_id"/>
<list-index column="order_col"/>因为List是有顺序的,hibernate通过这个标签记录员工是第几个加进来的
<one-to-many class="Employee" />
</list>
list总结:对于有些时候我们是不需要保存list的加入顺序,因为也会消耗不少的内存资源。
此时不需保存顺序时,可以在映射文件使用bag标签,jdk没有特别与之对应的集合,但是使用了bag标签,domain则必须是list相对应才可使用
三、bag标签必须与list接口对相应
与list的标签时的表结构相比,少了list-index的这一列。从而减少了内存开销
employee 表结构
Name Type Nullable Default Comments
--------- ------------- -------- ------- --------
ID NUMBER(10)
NAME VARCHAR2(255)
DEPART_ID NUMBER(10)
映射文件:
<bag name="emps">
<key column="depart_id"/>
<one-to-many class="Employee" />
</bag>
测试程序:
package cn.itcast.RelationalMapping;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.Transaction;
import cn.itcast.dao.HibernateUtil;
import cn.itcast.domain.Department;
import cn.itcast.domain.Employee;
/**
* 一对多测试类
* @author Mars
*
*/
public class One2many {
/**
* @param args
*/
public static void main(String[] args) {
//Department dept = add();
Department dept = add2();
queryDepart(dept.getId());
}
/**
* 一对多查询方法
* @param empId
* @return
*/
static Department queryDepart(int deptId){
Session s = null;
Transaction tx =null;
try {
s=HibernateUtil.getSession();
tx=s.beginTransaction();
Department depart = (Department)s.get(Department.class, deptId);
// 分两步:1,根据departid查询部门的相关数据
// 2.根据departid查询相应的employee相关数据
// System.out.println("emp size:"+depart.getEmps().size());
/**
* 为了显示更直观下,我没覆盖Employee类的toString()
*/
System.out.println("emp:"+depart.getEmps());
//涉及懒加载先这么写吧
Hibernate.initialize(depart.getEmps());
tx.commit();
return depart;
} finally{
if(s!=null){
s.close();
}
}
}
/**
* 一对多添加方法
* 告诉用户所从属的部门
* @return
*/
static Department add(){
Session s = null;
Transaction tx =null;
try {
Department depart = new Department();
depart.setName("depart name");
//告诉用户所从属的部门
Employee emp1 = new Employee();
emp1.setDepart(depart);//对象模型,建立两个对象的关联
emp1.setName("emp name1");
Employee emp2 = new Employee();
emp2.setDepart(depart);//对象模型,建立两个对象的关联
emp2.setName("emp name2");
s=HibernateUtil.getSession();
tx=s.beginTransaction();
s.save(depart);
s.save(emp1);
s.save(emp2);
tx.commit();
return depart;
} finally{
if(s!=null){
s.close();
}
}
}
/**
* 一对多添加方法
* 告诉用户所从属的部门及告诉部门所从属的员工
* @return
*/
static Department add2(){
Session s = null;
Transaction tx =null;
try {
Department depart = new Department();
depart.setName("depart name");
//告诉用户所从属的部门
Employee emp1 = new Employee();
emp1.setDepart(depart);//对象模型,建立两个对象的关联
emp1.setName("emp name1");
Employee emp2 = new Employee();
emp2.setDepart(depart);//对象模型,建立两个对象的关联
emp2.setName("emp name2");
//告诉部门所从属的员工
List<Employee>emps = new ArrayList<Employee>();
emps.add(emp1);
emps.add(emp2);
depart.setEmps(emps);
s=HibernateUtil.getSession();
tx=s.beginTransaction();
s.save(depart);
s.save(emp1);
s.save(emp2);
tx.commit();
return depart;
} finally{
if(s!=null){
s.close();
}
}
}
}
四、Map
实体映射文件:
<map name="emps">
<key column="depart_id"/>
<map-key type="string" column="name"/>
<one-to-many class="Employee" />
</map>
Map是键值对的形式,key不重复
SQL> desc department;
Name Type Nullable Default Comments
---- ------------- -------- ------- --------
ID NUMBER(10)
NAME VARCHAR2(255)
SQL> desc employee;
Name Type Nullable Default Comments
--------- ------------- -------- ------- --------
ID NUMBER(10)
NAME VARCHAR2(255)
DEPART_ID NUMBER(10)
五、数组array
实体映射文件:
<array name="emps">
<key column="depart_id"/>
<list-index column="order_colum"/> 表中有单独的整型列表示list-index
<one-to-many class="Employee" />
</array>
SQL> desc department;
Name Type Nullable Default Comments
---- ------------- -------- ------- --------
ID NUMBER(10)
NAME VARCHAR2(255)
SQL> desc employee;
Name Type Nullable Default Comments
----------- ------------- -------- ------- --------
ID NUMBER(10)
NAME VARCHAR2(255)
DEPART_ID NUMBER(10)
ORDER_COLUM NUMBER(10) Y
六、 hibernate中集合的选择:
一般都选择set集合,配置方便,而且不允许重复也符合一般的业务逻辑。
如果就是想使用list集合,不想保证正确顺序记录时用bag标签,想保证记录顺序时list标签
array一般很少用,因为数组的尺寸需要掌控,很容易造成数组越界,不便于管理
七、关于类型的一个小实验
第一步:将domain中的属性Set类型修改为HashSet类型和修改相关的setter和getter方法
public HashSet<Employee> getEmps() {
return emps;
}
第二步:
static Department add2(){
Session s = null;
Transaction tx =null;
try {
Department depart = new Department();
depart.setName("depart name");
//告诉用户所从属的部门
Employee emp1 = new Employee();
emp1.setDepart(depart);//对象模型,建立两个对象的关联
emp1.setName("emp name1");
Employee emp2 = new Employee();
emp2.setDepart(depart);//对象模型,建立两个对象的关联
emp2.setName("emp name2");
/**
* set时
*/
Set<Employee>emps = new HashSet<Employee>();
emps.add(emp1);
emps.add(emp2);
depart.setEmps(emps);
/* //告诉部门所从属的员工
//list集合时
List<Employee>emps = new ArrayList<Employee>();
emps.add(emp1);
emps.add(emp2);
depart.setEmps(emps);*/
/**
* Map集合时
*/
// Map<String,Employee>emps = new HashMap<String,Employee>();
// emps.put(emp1.getName(), emp1);
// emps.put(emp2.getName(), emp2);
// depart.setEmps(emps);
/**
* 数组
*/
/* Employee[] emps = {emp1,emp2};
depart.setEmps(emps);*/
s=HibernateUtil.getSession();
tx=s.beginTransaction();
s.save(depart);
s.save(emp1);
s.save(emp2);
tx.commit();
//做个试验
HashSet hs =(HashSet)depart.getEmps();
return depart;
} finally{
if(s!=null){
s.close();
}
}
}
HashSet hs =(HashSet)depart.getEmps();
此时报错:
Exception in thread "main" java.lang.ClassCastException: org.hibernate.collection.internal.PersistentSet cannot be cast to java.util.HashSet
at cn.itcast.RelationalMapping.One2many.add2(One2many.java:147)
at cn.itcast.RelationalMapping.One2many.main(One2many.java:26)
分析:Hibernate为了方便对集合懒加载操作,在顶层对所有的jdk中的集合进行了改写,所以只要以保存之后再取出来,hibernate已经对数据类型进行转换
所以domain定义时属性不能定义成具体的类,而是定义成接口(如定义成Set而不是HashSet)。
否则无法再hibernate中运行报类型异常
2013-6-2 15:44:18 org.hibernate.property.BasicPropertyAccessor$BasicSetter set
ERROR: HHH000123: IllegalArgumentException in class: cn.itcast.domain.Department, setter method of property: emps
2013-6-2 15:44:18 org.hibernate.property.BasicPropertyAccessor$BasicSetter set
ERROR: HHH000091: Expected type: java.util.HashSet, actual value: org.hibernate.collection.internal.PersistentSet
Exception in thread "main" org.hibernate.PropertyAccessException: IllegalArgumentException occurred while calling setter of cn.itcast.domain.Department.emps
at org.hibernate.property.BasicPropertyAccessor$BasicSetter.set(BasicPropertyAccessor.java:119)
at org.hibernate.tuple.entity.AbstractEntityTuplizer.setPropertyValues(AbstractEntityTuplizer.java:710)
at org.hibernate.tuple.entity.PojoEntityTuplizer.setPropertyValues(PojoEntityTuplizer.java:371)
at org.hibernate.persister.entity.AbstractEntityPersister.setPropertyValues(AbstractEntityPersister.java:4509)
at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:275)
at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:192)
at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:135)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:206)
at org.hibernate.event.internal.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:55)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:191)
at org.hibernate.event.internal.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:49)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:90)
at org.hibernate.internal.SessionImpl.fireSave(SessionImpl.java:764)
at org.hibernate.internal.SessionImpl.save(SessionImpl.java:756)
at org.hibernate.internal.SessionImpl.save(SessionImpl.java:752)
at cn.itcast.RelationalMapping.One2many.add2(One2many.java:142)
at cn.itcast.RelationalMapping.One2many.main(One2many.java:26)
Caused by: java.lang.IllegalArgumentException: argument type mismatch
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.hibernate.property.BasicPropertyAccessor$BasicSetter.set(BasicPropertyAccessor.java:65)
... 16 more
因为org.hibernate.collection.internal.PersistentSet 是实现了Set接口的,到但是不是从HashSet中继承过来的
结论:在持久化类中(domain类中),类中属性(成员变量)定义成接口不要定义成具体的类(如定义成Set而不是HashSet)。
剩余相关代码:
package cn.itcast.domain;
import java.util.Set;
/**
* 部门类
* @author Mars
*
*/
public class Department {
private int id;
private String name;
private Set<Employee>emps;
// private List<Employee>emps;
// private Map<String,Employee>emps;
// private Employee []emps;
public int getId() {
return id;
}
public Set<Employee> getEmps() {
return emps;
}
public void setEmps(Set<Employee> emps) {
this.emps = emps;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package cn.itcast.domain;
/**
* 员工类
*
* @author Mars
*
*/
public class Employee {
private int id;
private String name;
private Department depart;
/**
* 为了显示更直观下,我没覆盖Department类的toString()
*/
public String toString() {
// TODO Auto-generated method stub
return "id:"+this.getId()+" name:"+this.getName();
}
public int getId() {
return id;
}
public Department getDepart() {
return depart;
}
public void setDepart(Department depart) {
this.depart = depart;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}