Hibernate组件映射

组件映射又称聚合。

聚合分为:基本聚合和组合聚合。

基本聚合所表示的局部类的生命周期独立于整体类,也就是说整体类生命周期结束之后,局部类还可以长期存在,如上例中的汽车与零部件的关系。组合聚合所表示的局部类的生命周期依赖于整体类,整体类的生命周期结束后,局部类也失去了意义,如公司与部门的关系。

在数据库设计中,如果需要表示组成关系,程序员一般会将组成的元素分别作为字段存储在一个表中,如一位职工的姓名由姓和名两部分组成,那么程序员可能在职工表中用两个字段来存储相应的内容,在面向对象的设计中,程序员可能会将这种多种实体组成的实体专门设计成一个类,然后再需要引用这个类的其他类中,增加相应的属性,这也更加体现了面向对象的思想。

Hibernate将组件类作为值对象而非实体引用来进行映射,也就是说组件不支持共享引用。换句话说,两个人可能重名,但是两个Person对象应该包含两个独立的Name对象,只不过两个Name对象的具有同样的值。而不是在两个Person对象中存储了两个引用共同指向同一个Name对象。

在Hibernate中,component是某个实体的逻辑组成部分,它与实体的根本区别是没有oid(对象标识符),component是一个被包含的对象,它作为值类型被持久化,而非一个实体。

下面举两个例子说明其使用和配置:

1.以User和Address为例,一个User有id(Long类型)、name(String类型)、address地址(Address类型),其中Address类中没有id属性(即无对象标识符),则address就是User的一个component组件。

User持久化类:

  1. public class User  {  
  2.     private Long id;  
  3.     private String name;  
  4.     private Address address;  
  5.     //省略set、get方法  
  6. }  
组件类Address:
  1. public class Address {//注意Address类中无id属性(Address并不会被映射为一张表)  
  2.     private String addressName;  
  3.     private String addressValue;  
  4.         //省略set、get方法  
  5. }  
User.hbm.xml配置:
  1. <hibernate-mapping>  
  2.     <class name="bean.User" table="users">  
  3.         <id name="id" column="id" type="long">  
  4.             <generator class="increment"></generator>  
  5.         </id>  
  6.         <property name="name" column="name" type="string"></property>  
  7.         <component name="address" class="bean.Address">  
  8.             <property name="addressName" column="address_name"></property>  
  9.             <property name="addressValue" column="address_value"></property>  
  10.         </component>  
  11.     </class>  
  12. </hibernate-mapping>  
说明:

(1).User类映射的表为users,id为其主键,name为User的一般属性,被映射为name字段。

(2).<component>指定User类的address为User的组件,address并不是一个可以直接映射为表字段的属性而是一个类对象,class指定类的名称,<component>的子元素<property>指定组件类的属性与users表字段的映射关系。

(3)不必为Address写一个Address.hbm.xml映射文件,因为Address是作为User的一部分即值类型而不是实体被持久化的,所以Address的属性会与User的其它属性一样被映射为users表中的字段,而不会将Address映射为单独的一个表。

(4)User与Address的关系是"整体与部分",也可以将他们表示为一对一关联关系,这样的话两者就是“平等"关系,那么Address就会被映射为单独的一张表(需要配置Address.hbm.xml文件)。

(5)users表的结构为:

保存User对象:

  1. tx=session.beginTransaction();  
  2. User user=new User();  
  3. user.setName("zhangsan");  
  4. Address address=new Address();  
  5. address.setAddressName("HomeAddress");  
  6. address.setAddressValue("青岛");  
  7. user.setAddress(address);  
  8. session.save(user);  
  9. tx.commit();  
执行的SQL语句为:
  1. Hibernate: insert into users (name, address_name, address_value, id) values (?, ?, ?, ?)  

2.若User可有多个地址,即User类中的address属性为Set<Address>类型时,配置如下:

持久化类User:

  1. public class User  {  
  2.     private Long id;  
  3.     private String name;  
  4.     private Set<Address> address;  
  5.     //省略set、get方法  
  6. }  
Address类不变。

配置User.hbm.xml:

  1. <class name="bean.User" table="users">  
  2.     <id name="id" column="id" type="long">  
  3.         <generator class="increment"></generator>  
  4.     </id>  
  5.     <property name="name" column="name" type="string"></property>  
  6.     <!--注意<set>中并不是<one-to-many>而是<composite-element>-->  
  7.     <set name="address" table="address_table">  
  8.         <key column="user_id"></key>  
  9.         <composite-element class="bean.Address"> 
  10.              <parent name=" users">
  11. //本示例中实际上定义了一个“不使用连接表的双向多对一关联”,而是使用了parent的标记。
  12.             <property name="addressName" column="address_name"></property>  
  13.             <property name="addressValue" column="address_value"></property>  
  14.         </composite-element>  
  15.     </set>  
  16. </class>  

说明:

组合映射不支持引用的功能,因此判断两个对象是否“相等‘至关重要,使用hashcode和equals方法实现这一功能。

需要注意的是:当使用set标记来映射组件的时候,那么set所映射的组件是不支持可能为空的值的,必须使用每一个字段的值来确定一条记录。如果为null的字段可以选择<list>、<map>、<bag>或者<ibag>等具有索引的集合映射。

(1)与一对多关联关系不同,<set>中使用的不是<one-to-many>而是<composite-element>,Hibernate会将User对象address属性(Set类型)中的所有元素映射到另外一张表中(显然users中的一个记录不能表示Set中的多个元素),table属性指定另一张表的名称为address_table,该表存放Set中的元素,并且该表中还有一个参照users表id的外键字段user_id。

(2)users表与address_table表的结构为:(使用Hibernate自动建表功能创建)

address_table表的各字段说明:

可以发现address_table表中并没有主键,只有user_id参照users表中的主键id。

保存User对象:

  1. tx=session.beginTransaction();  
  2. User user=new User();  
  3. user.setName("zhangsan");  
  4. Address address=new Address();  
  5. address.setAddressName("HomeAddress");  
  6. address.setAddressValue("青岛");  
  7. Address address2=new Address();  
  8. address2.setAddressName("SchoolAddress");  
  9. address2.setAddressValue("北京");  
  10. user.setAddress(new HashSet<Address>());  
  11. user.getAddress().add(address);  
  12. user.getAddress().add(address2);  
  13. session.save(user);  
  14. tx.commit();  
输出的SQL语句为:
  1. Hibernate: insert into users (name, id) values (?, ?)  
  2. Hibernate: insert into address_table (user_id, address_name, address_value) values (?, ?, ?)  
  3. Hibernate: insert into address_table (user_id, address_name, address_value) values (?, ?, ?)  

address_table的内容:

(因为address_table表没有主键,所以在MySQL WorkBench中无法通过图形化界面添加记录,但是可以操纵SQL语句添加记录,例:


要插入记录的user_id必须为users表中存在的id(因为user_id参照users表的id),可使用update语句更改使表中存在相同的记录,下面会查询id为1L的User对象的Set中的Address对象以检验Hibernate是否会自动排除重复的元素以符合Set中无重复元素的原则。

查询User对象:

  1. User u=(User)session.get(User.class, 1L);  
  2. Set<Address> address=u.getAddress();  
  3. for (Iterator iterator = address.iterator(); iterator.hasNext();) {  
  4.           Address a = (Address) iterator.next();  
  5.           System.out.println(a.getAddressName()+":"+a.getAddressValue());}  
控制台输出:
  1. Hibernate: select user0_.id as id0_0_, user0_.name as name0_0_ from users user0_ where user0_.id=?  
  2. Hibernate: select address0_.user_id as user1_0_, address0_.address_name as address2_0_, address0_.address_value as address3_0_ from address_table address0_ where address0_.user_id=?  
  3. HomeAddress:青岛  
  4. HomeAddress:青岛  
  5. H:S  
  6. Hibernate: delete from address_table where user_id=? and address_name=? and address_value=?  
  7. Hibernate: delete from address_table where user_id=? and address_name=? and address_value=?  
  8. Hibernate: delete from address_table where user_id=? and address_name=? and address_value=?  
  9. Hibernate: insert into address_table (user_id, address_name, address_value) values (?, ?, ?)  
  10. Hibernate: insert into address_table (user_id, address_name, address_value) values (?, ?, ?)  
  11. Hibernate: insert into address_table (user_id, address_name, address_value) values (?, ?, ?)  
可知,Hibernate并没有过滤掉多余的元素,对于输出的3条delete和insert语句,尚不知其机制,但我认为Hibernate没有过滤掉重复的元素是因为Address类中没有实现 hashCode()equals()方法,使用MyEclipse在Address类中生成hashCode及equals方法后再执行上述查询语句:

控制台输出:

  1. Hibernate: select user0_.id as id0_0_, user0_.name as name0_0_ from users user0_ where user0_.id=?  
  2. Hibernate: select address0_.user_id as user1_0_, address0_.address_name as address2_0_, address0_.address_value as address3_0_ from address_table address0_ where address0_.user_id=?  
  3. HomeAddress:青岛  
  4. H:S  
可知Hibernate已将多余的重复元素排除。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值