在第一个hibernate小程序中我们保持了一个实体类Event到数据库的表中,这次让我们扩展一下,增加一些类的关联。
1. 映射Person类
创建Person.java
package com.iteye.hibernate.domain; public class Person { private Long id; private int age; private String firstname; private String lastname; public Person() {} public Long getId() { return id; } public void setId(Long id) { this.id = id; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getFirstname() { return firstname; } public void setFirstname(String firstname) { this.firstname = firstname; } public String getLastname() { return lastname; } public void setLastname(String lastname) { this.lastname = lastname; } }
2. 创建Person类的映射文件
Person.hbm.xml
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.iteye.hibernate.domain"> <class name="Person" table="PERSON"> <id name="id" column="PERSON_ID"> <generator class="native"/> </id> <property name="age"/> <property name="firstname"/> <property name="lastname"/> </class> </hibernate-mapping>
然后在hibernate.cfg.xml中添加一行
<mapping resource="com/iteye/hibernate/domain/Person.hbm.xml"/>
3. 添加基于Set的单向关联
这里我们通过java.util.Set向Person添加Event的关联 我们在Person类中添加如下代码
private Set events = new HashSet(); public Set getEvents() { return events; } public void setEvents(Set events) { this.events = events; }
这里只是添加了一个Person到Event的单向关联,如果需要你可以在Event中添加Event到Person的关联,但这通常不是必须的。这样的一个关联我们叫做多对多的关联many-to-many。因此我们需要修改Person的映射文件
<set name="events" table="PERSON_EVENT"> <key column="PERSON_ID"/> <many-to-many column="EVENT_ID" class="Event"/> </set>
通常对于一个n:m的关联,我们需要一个中间表。这个表的每一行都是标示一个Person到一个Event的链接。表的名称使用table属性来指定。而表的主键在Person这表通过key来指定,而在Event这边通过many-to-many来指定
表之间的关系如下
4. 我们在EventManager中添加方法来实现数据的关联
private void addPersonToEvent(Long personId, Long eventId) { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); Person aPerson = (Person) session.load(Person.class, personId); Event anEvent = (Event) session.load(Event.class, eventId); aPerson.getEvents().add(anEvent); session.getTransaction().commit(); }
这里我们不用调用save或者update方法,hibernate会自动检测到集合已经改变,自动进行更新,这叫做自动脏数据检测
你可以在不同的工作单元里面加载person和Event。或者你可以更改一个不是持久状态的对象(关于在Hibernate中对象的几种状态,在最后进行说明),你也可以修改一个托管的对象,可以参考一下代码
private void addPersonToEvent(Long personId, Long eventId) { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); Person aPerson = (Person) session .createQuery("select p from Person p left join fetch p.events where p.id = :pid") .setParameter("pid", personId) .uniqueResult(); // Eager fetch the collection so we can use it detached Event anEvent = (Event) session.load(Event.class, eventId); session.getTransaction().commit(); // End of first unit of work aPerson.getEvents().add(anEvent); // aPerson (and its collection) is detache // Begin second unit of work Session session2 = HibernateUtil.getSessionFactory().getCurrentSession(); session2.beginTransaction(); session2.update(aPerson); // Reattachment of aPerson session2.getTransaction().commit(); }
调用update通过绑定到一个新的工作单元使一个托管的对象重新持久化,以便于你所做的改变可以存储到数据库
值的集合
让我们想Person类中添加一个邮箱地址的集合
private Set emailAddresses = new HashSet(); public Set getEmailAddresses() { return emailAddresses; } public void setEmailAddresses(Set emailAddresses) { this.emailAddresses = emailAddresses;
为Person添加映射
<set name="emailAddresses" table="PERSON_EMAIL_ADDR"> <key column="PERSON_ID"/> <element type="string" column="EMAIL_ADDR"/> </set>
和之前不同的是我们通过element来告诉hibernate我们的set中包含的是一些值而不是另外一个实体的引用。
这里的类型string是一个hibernate的类型转换器。Table属性依然是用来指定表名,key用来指定一个外键
private void addEmailToPerson(Long personId, String emailAddress) { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); Person aPerson = (Person) session.load(Person.class, personId); // adding to the emailAddress collection might trigger a lazy load of the collection aPerson.getEmailAddresses().add(emailAddress); session.getTransaction().commit(); }
添加双向的关联
你同样可以为person和event指定一个双向的关联,数据库的结构没有任何改变,依然是一个多对对的关联。
首先添加一个集合到Event类
private Set participants = new HashSet(); public Set getParticipants() { return participants; } public void setParticipants(Set participants) { this.participants = participants; } 然后修改mapping文件
<set name="participants" table="PERSON_EVENT" inverse="true"> <key column="EVENT_ID"/> <many-to-many column="PERSON_ID" class="Person"/> </set>
依然是一个set,和以前介绍的一样,唯一不同的是多了inverse="true"
这个是告诉hibernate从另一边也就是Person找到两者之间的关联