您的位置:首页 > 其它

hibernate学习笔记12--Hibernate中使用的集合类型

2013-06-02 16:03 525 查看
关联关系总结:

如何考虑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;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: