今天学习了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字段的值却不能自动生成,不知道在哪里解决。真是好像看懂了,拿到手里就傻了。