TA的每日心情 | 开心 2021-3-12 23:18 |
---|
签到天数: 2 天 [LV.1]初来乍到
|
关联映射
我们已经将一个持久化的实体类映射到了数据库表中。现在以这个为基础,再增加一些类的关联。首先添加
people
到应用程序中,然后存储他们所参与的一系列
events
。
1.
映射
Person
类
Persion
类的第一部分片段代码如下:
package
events;
publicclass
Person {
private
Long
id
;
privateint
age
;
private
String
firstname
;
private
String
lastname
;
public
Person() {}
// Accessor methods for all properties, private setter for "id"
}
创建一个新的映射文件,命名为
Person.hbm.xml
(不要忘记
DTD
关联的声明)
<hibernate-mapping>
<class name="events.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
配置文件中:
<mapping resource="events/Event.hbm.xml"/>
<mapping resource="events/Person.hbm.xml"/>
现在,我们在两个实体中创建了暗恋。很显然,人可以参与事件,
person
可以参与
events
,而
events
可以有参与者。我们需要处理的设计问题是:方向性,多元性以及集合行为。
Directionality
,
mulitiplicity
和
collection behavior
。
2.
单向
Set-based
关联
我们将在
Person
类中添加一个
events
集合。这样我们就可以通过调用
aPerson.getEvents()
查看某个人参与的事件,而不需要通过执行显示的查询。我们使用一个
java
集合,
Set
,因为集合不允许包含重复的元素,并且我们并不关心顺序。
我们需要一个单向的,多值的关联,通过
Set
来实现。
Java
代码如下:
public class Person {
private Set events = new HashSet();
public Set getEvents() {
return events;
}
public void setEvents(Set events) {
this.events = events;
}
}
在映射关联之前,考虑一下另外一个方面。很显然,可以只维持单向性。或者,可以在
Event
类中也创建一个集合,如果我们需要双向查询功能,例如
anEvent.getParticipants()
。从功能的角度来说,这不是必须的。可以通过显示的查询来获取某个事件
event
的参与者
participants
。但是从这个讨论中,关联的多重性
mulitiplicity
是清楚的:
”many”
作用两者,我们把它叫做
many-to-many
关联。因此,我们使用
Hibernate
的
many-to-many
映射:
<class name="events.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="events.Event"/>
</set>
</class>
Hibernate包含所有的集合映射,<set>是最常用的。对于一个many-to-many关联,,需要一个关联数据库表,表中的每一行代表一个person与event的连接。表的名字通过set元素的table属性进行设置。Person端的关联的主键的列名通过<key>定义,使用<many-to-many>元素的column属性对event端进行设置。同时,Hibernate需要知道集合中对象的类名称。(关联的集合的另一端的类)。
这个映射的数据库
schema
如下所示:

3.
在
EventManager
中,将
people
和
events
结合在一起,代码如下:
privatevoid
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();
}
加载完一个
Person
和
Event
对象后,使用正常的集合方法修改集合。正如你所看到的,没有显示的调用
update()
或者
save()
方法,
Hibernate
自动的检测集合是否被修改,是否需要保存。这叫做
automatic dirty checking
。(也就是说,他们只在一个工作单元内就已经被加载和保存)。
Hibernate
通过在后台进行变化的监视和
SQL
的执行。同步数据库内存状态的处理,通常实在工作单元的结束时进行的,叫做
flushing
。在我们的代码中,工作单元以事务的
commit
或者
rollback
结束的。这是在
Hibernate
配置文件的
thread
选项中设置的
CurrentSessionContext
。
到目前为止,完成这个练习只需要添加一个新的
action
,以便在命令行进行调用的时候使用。如果你需要
person
或者
event
的标志符,
save()
方法可以返回。
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);
}
运行: C: est>h.bat C: est>set classpath=.;c: estlibhibernate3.jar;c: estlibmysql-connector-java-3.1.10-bin.jar;
c: estlibcommons-logging-1.0.4.jar;c: estlibdom4j-1.6.1.jar;c: estlibcommons-collections-2.1.1.jar;
c: estlibc3p0-0.9.1.jar;c: estlibasm.jar;c: estlibcglib-2.1.3.jar;c: estlibjta.jar;c: estlibantlr- 2.7.6.jar C: est>javac -d . *.java
C: est>java events.EventManager addpersontoevent 结果:
4.
我们给
Person
实体添加了一个集合的值类型对象。如果需要存储邮件地址,此时的值类型是
String
,集合的类型仍然是
Set
:
private Set emailAddresses = new HashSet();
public Set getEmailAddresses() {
return emailAddresses;
}
public void setEmailAddresses(Set emailAddresses) {
this.emailAddresses = emailAddresses;
}
这个集合的映射为:
<set name="emailAddresses" table="PERSON_EMAIL_ADDR">
<key column="PERSON_ID"/>
<element type="string" column="EMAIL_ADDR"/>
</set>
对比两个集合的不同:
<
set
name
=
"events"
table
=
"PERSON_EVENT"
>
<
key
column
=
"PERSON_ID"
/>
<
many-to-many
column
=
"EVENT_ID"
class
=
"events.Event"
/>
</
set
>
<
set
name
=
"emailAddresses"
table
=
"PERSON_EMAIL_ADDR"
>
<
key
column
=
"PERSON_ID"
/>
<
element
type
=
"string"
column
=
"EMAIL_ADDR"
/>
</
set
>
和之前的配置不同的是
element
元素,这个元素说明了集合中不包含对其他实体的关联,而只是
String
类型的元素。(小写的
string
说明它是一个
Hibernate
映射的类型
/
转换器)。同样,
set
元素的
table
属性指定了集合的数据库表的名称,
key
元素定义了集合表的外键,
element
元素的
column
属性定义了存储
String
值的列的名称。
更新后的
Schema
如下:

可以发现,集合表的主键事实上是一个联合主键,使用所有的列。这也表明了每个人的邮件地址不可以重复,这也是
Java
语言中
Set
类所要求的。
现在可以将元素加入到这个集合中,和关联
persons
和
events
的方式一样。
privatevoid
addEmailToPerson
(Long personId, String emailAddress) {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
Person aPerson = (Person) session.load(Person.
class
, personId);
// The getEmailAddresses() might trigger a lazy load of the collection
aPerson.getEmailAddresses().add(emailAddress);
session.getTransaction().commit();
}
这次,我们不使用查询语句来初始化集合元素,而是使用
getter
方法来获取它。
5.
双向关联
接下来创建一个双向关联映射
-
创建
person
和
event
的双向关联。当然,数据库
Schema
不变,仍然使用
many-to-many
的多重关系。关系数据库比网络编程语言要灵活得多,所以不需要做任何修改,数据就可以以任何方式来查看和获取。
首先,给
Event
类添加一个集合。
private
Set
participants
=
new
HashSet();
public
Set getParticipants() {
return
participants
;
}
publicvoid
setParticipants(Set participants) {
this
.
participants
= participants;
}
然后,在
Event.hbm.xml
中添加关联的映射:
<set name="participants" table="PERSON_EVENT" inverse="true">
<key column="EVENT_ID"/>
<many-to-many column="PERSON_ID" class="events.Person"/>
</set>
这和
Person
中的配置是一样的。需要注意的是
key
元素和
many-to-many
元素中的列的名字。最重要的添加是
inverse=”true”
属性。
这意味着当需要查询两者之间的关联信息时,需要考虑
Persion
类。
6.
首先,记住
Hibernate
不影响
Java
语义。我们在单向的例子中是如何创建的
Person
与
Event
之间的关联的?添加一个
Event
对象到一个
Person
实例的
Event
集合中。所以,所以,很显然,如果想使这个
link’
是双向工作的,我们必须将一个
Person
实例添加到一个
Event
的集合中。在两边都设置
link
(
setting the link on both sides
)是必须的,千万别忘记这样做。
很多程序开发人员为了通过创建一个
link
管理方法来预防上面的问题,例如在
Person
中:
protected Set getEvents() {
return events;
}
protected void setEvents(Set events) {
this.events = events;
}
public void addToEvent(Event event) {
this.getEvents().add(event);
event.getParticipants().add(this);
}
public void removeFromEvent(Event event) {
this.getEvents().remove(event);
event.getParticipants().remove(this);
}
注意,这里的
get
和
set
方法都是
protected
,允许同一个包中,子类来访问这些方法,而除此之外的类却不能访问。在
Event
类中也需要同样处理。
那么
inverse
属性到底是做什么的呢?对于,对于
Java
来说,一个双向的
link
,存在一个在双向正确的设置引用的问题。
Hibernate
没有足够的信息来正确的安排
SQL
的
INSERT
和
UPDATE
语句(为了避免违反约束),需要一个合适的办法来处理双向关联。使关联的一方是
inverse
,可以使
Hibernate
忽略它,只是把它当作另一方的镜像。这就是
Hibernate
所需要的。你所需要记住的规则是明了的:所有的双向关联需要将一方设置为
inverse
。在
one-to-many
关联中,必须为
many
�,在
many-to-many
中,可以是任意一侧。
源码下载:http://www.hnzz3z.com:8103/zz3zcwb/test1.zip |
|