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

本文详细介绍了Hibernate中处理对象关联关系时的各种集合类型,包括一对一、多对一、一对多、多对多的关系映射,以及在数据库中的表现。重点讨论了bag、list、map和array标签的使用场景和注意事项,强调了set集合作为首选项的原因。同时,通过一个关于类型的小实验,解释了为什么在持久化类中应定义接口而非具体实现类作为属性类型,以避免类型转换异常。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关联关系总结:
如何考虑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;
	}
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值