关联映射-单向Set-based关联

第一个Hibernate应用程序的代码基础上做的hibernate单项Set-based关联。

package org.hibernate.tutorial.domain;

import java.util.HashSet;
import java.util.Set;

public class Person {
	private Long id;
    private int age;
    private String firstname;
    private String lastname;
    private Set events = new HashSet();
    //setter and getter
}

此处采用双向关联,Hibernate many-to-many映射,Person映射文件如下:

<class name="Person" table="PERSON">
    <id name="id" column="PERSON_ID">
        <generator class="native"/>
    </id>
    <property name="age"/>
    <property name="firstname"/>
    <property name="lastname"/>

    <set name="events" table="PERSON_EVENT">
        <key column="PERSON_ID"/>
        <many-to-many column="EVENT_ID" class="Event"/>
    </set>

</class>
Hibernate提供广泛的集合映射,Set是比较常用的一种。对于many-to-may(n:m)这样的关联关系,关联表是需要的。表中的每条记录表示person 和event的一个连接。

set元素中的table属性表示表名。标识符列名,在person这端用key元素表示,在event端用many-to-many元素的column表示。

show create table person看下建表语句:

| person | CREATE TABLE `person` (
  `PERSON_ID` bigint(20) NOT NULL AUTO_INCREMENT,
  `age` int(11) DEFAULT NULL,
  `firstname` varchar(255) DEFAULT NULL,
  `lastname` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`PERSON_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 |
| person_event | CREATE TABLE `person_event` (
  `PERSON_ID` bigint(20) NOT NULL,
  `EVENT_ID` bigint(20) NOT NULL,
  PRIMARY KEY (`PERSON_ID`,`EVENT_ID`),
  KEY `FK_7bh2i9xjn8v93x5ku5w61npwf` (`EVENT_ID`),
  CONSTRAINT `FK_8xsbl2jbyqaf5ocifr7hcjt2x` FOREIGN KEY (`PERSON_ID`) REFERENCES
 `person` (`PERSON_ID`),
  CONSTRAINT `FK_7bh2i9xjn8v93x5ku5w61npwf` FOREIGN KEY (`EVENT_ID`) REFERENCES
`events` (`EVENT_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |

数据库表结构之间的关系:

关联测试(方法在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();
    }

automatic dirty checking

注意到代码里没有显示的update()和save(),Hibernate自动检测collection的变化,称为自动脏检测(automatic dirty checking),你可以随意改变任一对象的数据属性。

只要他们都是持久态(persistent state),被org.hibernate.Session管辖。Hibernate监听变化并采用延迟写的方式执行SQL,和数据库同步内存状态的过程,通常在工作单元的末尾,称为flusing阶段。在我们的代码里,数据库事务的工作单元以commit或者rollback来结束。

你可以以load的形式获取person和event。你也可以在托管(detached 对象不再org.hibernate.Session里),你甚至可以在detached时修改集合。

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 detached

        // Begin second unit of work

        Session session2 = HibernateUtil.getSessionFactory().getCurrentSession();
        session2.beginTransaction();
        session2.update(aPerson); // Reattachment of aPerson

        session2.getTransaction().commit();
    }
通过绑定一个新的工作单元(事务),update()使得detached对象再一次持久化。所有你做的修改都会被保存到数据库。

例子里的这样的实现并不常用,但是一个重要的概念你可以并入你自己的应用。接下来完成这个联系。

代码并入EventManger:

else if (args[0].equals("addpersontoevent")) {
            Long eventId = mgr.createAndStoreEvent("My Event", new Date());
            Long personId = mgr.createAndStorePerson("Foo", "Bar");
            mgr.addPersonToEvent(personId, eventId);
            System.out.println("Added person " + personId + " to event " + eventId);
        }
看下执行的sql:

Hibernate: insert into EVENTS (EVENT_DATE, title) values (?, ?)
Hibernate: insert into PERSON (age, firstname, lastname) values (?, ?, ?)
Hibernate: select person0_.PERSON_ID as PERSON_I1_1_0_, event2_.EVENT_ID as EVENT_ID1_0_1_, person0_.age as age2_1_0_, person0_.firstname as firstnam3_1_0_, person0_.lastname as lastname4_1_0_, event2_.EVENT_DATE as EVENT_DA2_0_1_, event2_.title as title3_0_1_, events1_.PERSON_ID as PERSON_I1_1_0__, events1_.EVENT_ID as EVENT_ID2_3_0__ from PERSON person0_ left outer join PERSON_EVENT events1_ on person0_.PERSON_ID=events1_.PERSON_ID left outer join EVENTS event2_ on events1_.EVENT_ID=event2_.EVENT_ID where person0_.PERSON_ID=?
Hibernate: update PERSON set age=?, firstname=?, lastname=? where PERSON_ID=?
Hibernate: insert into PERSON_EVENT (PERSON_ID, EVENT_ID) values (?, ?)

 ==》aPerson.getEvents().add(anEvent);这段代码发出Hibernate: insert into PERSON_EVENT (PERSON_ID, EVENT_ID) values (?, ?)
需要注意的是这里的aPerson.getEvents()并不在org.hibernate.Session内,此时session已经管理,aPerson处于detached状态。


值的集合:

往Person实例中增加邮件地址集合。

package org.hibernate.tutorial.domain;

import java.util.HashSet;
import java.util.Set;

public class Person {
	private Long id;
    private int age;
    private String firstname;
    private String lastname;
    private Set events = new HashSet();
    private Set emailAddresses = new HashSet();

    //setter and getter
}
此时的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="org.hibernate.tutorial.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"/>
        
        <set name="events" table="PERSON_EVENT">
        	<key column="PERSON_ID"/>
        	<many-to-many column="EVENT_ID" class="Event"/>
    	</set>
    	
    	<set name="emailAddresses" table="PERSON_EMAIL_ADDR">
            <key column="PERSON_ID"/><!-- 外键 -->
            <element type="string" column="EMAIL_ADDR"/><!-- element表示元素 -->
        </set>
    </class>

</hibernate-mapping>
set中的key PERSON_ID为集合表的外键名,element元素的column属性定义存储邮件地址值的列名。

表结构之间的关系如下:



测试代码如下:

private void addEmailToPerson(Long personId, String emailAddress) {
    	Session session = HibernateUtil.getSessionFactory().getCurrentSession();
        session.beginTransaction();

        Person aPerson = (Person) session
                .createQuery("from Person p where p.id = :pid")
                .setParameter("pid", personId)
                .uniqueResult(); // Eager fetch the collection so we can use it detached
        
   //     Hibernate.initialize(aPerson.getEmailAddresses());	//实例化代理
        
        session.getTransaction().commit();

        // End of first unit of work
        
        aPerson.getEmailAddresses().add(emailAddress); // aPerson (and its collection) is detached 
       
        // Begin second unit of work

        Session session2 = HibernateUtil.getSessionFactory().getCurrentSession();
        session2.beginTransaction();
        session2.update(aPerson); // Reattachment of aPerson

        session2.getTransaction().commit();
    }
注意createQuery中并没有用fectch。运行结果如下:

Hibernate: select person0_.PERSON_ID as PERSON_I1_1_, person0_.age as age2_1_, person0_.firstname as firstnam3_1_, person0_.lastname as lastname4_1_ from PERSON person0_ where person0_.PERSON_ID=?
Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: org.hibernate.tutorial.domain.Person.emailAddresses, could not initialize proxy - no Session
	at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:572)
	at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:212)
	at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:551)
	at org.hibernate.collection.internal.PersistentSet.add(PersistentSet.java:202)
	at org.hibernate.tutorial.EventManager.addEmailToPerson(EventManager.java:142)
	at org.hibernate.tutorial.EventManager.main(EventManager.java:35)
报错了,could not initialize proxy-no Session(没有实例化代理-没有Session)。

因为
Hibernate: select person0_.PERSON_ID as PERSON_I1_1_, person0_.age as age2_1_, person0_.firstname as firstnam3_1_, person0_.lastname as lastname4_1_ from PERSON person0_ where person0_.PERSON_ID=?
并没有查询emailAddress,返回org.hibernate.tutorial.domain.Person.emailAddresses是一个为初始化的代理。
解决办法:
1.把Hibernate.initialize(aPerson.getEmailAddresses());//实例化代理 的注解打开,在session中实例化org.hibernate.tutorial.domain.Person.emailAddresses
2.改懒加载为false
<set name="emailAddresses" table="PERSON_EMAIL_ADDR" lazy="false">
            <key column="PERSON_ID"/><!-- 外键 -->
            <element type="string" column="EMAIL_ADDR"/><!-- element表示元素 -->
        </set>

ps:由于本来翻译水平有限,大家多多包含,我会更加努力。thx!!!

原文:http://docs.jboss.org/hibernate/orm/4.3/manual/en-US/html_single/#tutorial-firstapp-workingpersistence








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值