今天学习了hibernate集合映射中many-to-one的用法,现将一些体会写下来。
还是采用教程中的例子:两个实体类person和Address
package eg;
public class Person{
private Long id;
private Address address;
public Person() {
}
public Long getId(){
return this.id;
}
private void setId(Long id){
this.id = id;
}
public void setAddress(Address address){
this.address = address;
}
public Address getAddress(){
return this.address;
}
}
package eg;
import java.util.*;
public class Address{
private Long addressId;
private List people = new ArrayList();
public Address(){}
public Long getId(){
return this.addressId;
}
private void setId(Long addressId){
this.addressId = addressId;
}
public void setPeople(List people){
this.people = people;
}
public List getPeople(){
return this.people;
}
}
映射文件:Person.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="eg">
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address"
class="Address"
column="addressId"
not-null="true"
insert="false"
update="false"
/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<list name="people" inverse="false" lazy="true" cascade="all">
<key column="addressId" not-null="true"/>
<list-index column="peopleidx" base="0"/>
<one-to-many class="Person" />
</list>
</class>
</hibernate-mapping>
测试代码:
package eg;
import org.hibernate.Session;
import org.hibernate.impl.SessionImpl;
import org.hibernate.Hibernate;
import java.util.*;
import org.hibernate.tutorial.util.HibernateUtil;
public class Manager {
public static void main(String[] args) throws Throwable{
Manager mgr = new Manager();
if (args[0].equals("store")) {
mgr.createAndStorePerson();
}else if (args[0].equals("storel")) {
mgr.createAndStorePerson(3L);
}else if (args[0].equals("loadA")) {
List s = mgr.loadAddress(3L);
}
HibernateUtil.getSessionFactory().close();
}
private Long createAndStorePerson(){
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
Person p = new Person();
Address a = new Address();
p.setAddress(a);
Integer i = Integer.valueOf(new Random().nextInt(20));
a.getPeople().add(p);
session.save(a);
session.getTransaction().commit();
return p.getId();
}
private void createAndStorePerson(Long l){
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
Person p = new Person();
Address a = (Address)session.load(Address.class,l);
a.getPeople().add(p);
p.setAddress(a);
session.save(a);
session.getTransaction().commit();
}
private List loadAddress(Long l) {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
Address a = (Address)session.load(Address.class,l);
List s = a.getPeople();
Hibernate.initialize(s);
session.getTransaction().commit();
return s;
}
}
在整个测试的过程中有几点需要记住:
1、单向映射时:只在多端声明many-to-one即可单端不用知道映射,也只在多端维护关系。
<many-to-one name="address"
column="addressId"
not-null="true"/>
如上面的address是指向实体类Person中引用的实体对象address,在程序中通过设定对Address实体的引用而建立关系的。
Person p = new Person();
Address a = new Address();
p.setAddress(a);
session.save(a);
2、双向映射时,须在两端设置,也要让单端知道关系,而哪一端来维护关系要看具体的情形。一般情况下,单端要通过一个集合来引用对应的多端实体对象。可用的集合有map、set、list、array、bag、idbag.
Hibernate要求持久化集合值字段必须声明为接口。
private List people = new ArrayList();
实际的接口可能是java.util.Set
, java.util.Collection
, java.util.List
, java.util.Map
, java.util.SortedSet
, java.util.SortedMap
或者...任何你喜欢的类型!("任何你喜欢的类型" 代表你需要编写 org.hibernate.usertype.UserCollectionType
的实现.)
注意我们是如何用一个HashSet
实例来初始化实例变量的.这是用于初始化新创建(尚未持久化)的类实例中集合值属性的最佳方法。当你持久化这个实例时——比如通过调用persist()
——Hibernate 会自动把HashSet
替换为Hibernate自己的Set
实现。
根据不同的接口类型,被Hibernate注射的持久化集合类的表现类似HashMap
, HashSet
, TreeMap
, TreeSet
or ArrayList
。
集合类实例具有值类型的通常行为。当被持久化对象引用后,他们会自动被持久化,当不再被引用后,自动被删除。假若实例被从一个持久化对象传递到另一个,它 的元素可能从一个表转移到另一个表。两个实体不能共享同一个集合类实例的引用。因为底层关系数据库模型的原因,集合值属性无法支持空值语 义;Hibernate对空的集合引用和空集合不加区别。
如果你使用List
(或者其他有序集合类),你需要设置外键对应的key
列为 not null
( 假若集合映射的<key>
元素对应的底层外键字段是NOT NULL
的,那么为这一key元素定义not-null="true"
是很重要的。不要仅仅为可能的嵌套<column>
元素定义not-null="true"
,<key>
元素也是需要的。 ),让Hibernate来从集合端管理关联,维护每个元素的索引(通过设置update="false"
and insert="false"
来对另一端反向操作)。同时由于list要在多端维护一个索引列,所以list映射应该设为在集合端具有inverse="false" cascade="save-update"属性。 通过list-index指定的索引字段所起的作用是保证list中可同时出现多个相同的外关键值,当hebernate自动维护索引时,只有在产生了多值的情况下才增加索引值。刚开始测试时不知道这点,见其值一直是0(没有产生多对一的值)还以为没有配合适,浪费了好多时间。这种情况下关系的维护应在集合端。
Person p = new Person();
Address a = new Address();
p.setAddress(a);
a.getPeople().add(p);
session.save(a);
但是相同的设置在换成array后只能存储实体值,list-index字段的值却不能自动生成,不知道在哪里解决。真是好像看懂了,拿到手里就傻了。