您的位置:首页 > 移动开发

Hibernate 官方文档(入门) 第一章 1.2 - Mapping associations 映射联系

2016-07-19 11:02 519 查看



1.2. Part 2 - Mapping associations 映射联系(表与表直接的关系)

So far we have mapped a single persistent entity class to a table in isolation. Let's expand on that a bit and add some class associations. We will add people to the application and store a list of events in which they
participate.

到目前为止,我们已经将一个持久实体类映射到一个孤立的表中。接下来让我们进行一些扩展,并添加一些类关联。我们将向应用程序添加"人"这个实体,这个实体类存储着他们参与的事件列表。


1.2.1. Mapping the Person class 映射person类

The first cut of the 
Person
 class looks like this:

第一个
Person
 类代码片段看起来像这样:

package org.hibernate.tutorial.domain;

public class Person {

private Long id;
private int age;
private String firstname;
private String lastname;

public Person() {}

// Accessor methods for all properties, private setter for 'id'

}


Save this to a file named 
src/main/java/org/hibernate/tutorial/domain/Person.java


保存这个文件,并命名为 
src/main/java/org/hibernate/tutorial/domain/Person.java




Next, create the new mapping file as 

下一步,创建新的映射文件src/main/resources/org/hibernate/tutorial/domain/Person.hbm.xml


<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"/>
</class>

</hibernate-mapping>


Finally, add the new mapping to Hibernate's configuration:

最后,在Hibernate配置文件中添加新的映射:

<mapping resource="org/hibernate/tutorial/domain/Event.hbm.xml"/>
<mapping resource="org/hibernate/tutorial/domain/Person.hbm.xml"/>

Create an association between these two entities. Persons can participate in events, and events have participants. The design questions you have to deal with are: directionality, multiplicity, and collection behavior.

在两个实体之间创建一个关联。人可以参与多个事件,并且事件有多个参与者。设计一个你将要处理的问题是:方向性、多样性、和收集行为。


1.2.2. A unidirectional Set-based association 一种基于单向集的关联

By adding a collection of events to the 
Person
 class, you can easily navigate to the events for a particular person, without executing an explicit query
- by calling 
Person#getEvents
. Multi-valued associations are represented in Hibernate by one of the Java Collection Framework contracts; here we choose
java.util.Set
because the collection will not contain duplicate elements and the ordering is not relevant to our examples:

通过向Person类添加一个事件集合,您可以轻松地导航到一个特定的人的事件,没有执行一个明确的查询 - 通过调用Person#getEvents。多值关联在Hibernate中通过java的集合框架其中的一个集合类被体现出来;在这里,我们选择java.util.set
集合,因为这个集合不包含重复的元素,并且对于我们的例子,元素的顺序是不相关的:

public class Person {

private Set events = new HashSet();

public Set getEvents() {
return events;
}

public void setEvents(Set events) {
this.events = events;
}
}


Before mapping this association, let's consider the other side. We could just keep this unidirectional or create another collection on the 
Event
, if
we wanted to be able to navigate it from both directions. This is not necessary, from a functional perspective. You can always execute an explicit query to retrieve the participants for a particular event. This is a design choice left to you, but what is clear
from this discussion is the multiplicity of the association: "many" valued on both sides is called a many-to-many association. Hence, we use Hibernate's many-to-many mapping:

在映射这个关联之前,让我们思考另一方面。我们可以只保持这个单向或在Event类上创建另一个集合,如果我们想从两个方向都能够导航它。从功能的角度来看,这是没有必要的。您始终可以执行一个显式查询,以检索特定事件的参与者。这是一个设计选择权留给你,但关联的多重性定义是明确的:两边都是多值被称为多对多关联。因此,我们使用Hibernate的多对多映射:

<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 supports a broad range of collection mappings, a 
set
 being most common. For a many-to-many association, or n:m entity
relationship, an association table is required. Each row in this table represents a link between a person and an event. The table name is declared using the 
table
 attribute
of the 
set
 element. The identifier column name in the association, for the person side, is defined with the 
key
 element,
the column name for the event's side with the 
column
 attribute of the 
many-to-many
.
You also have to tell Hibernate the class of the objects in your collection (the class on the other side of the collection of references).

Hibernate支持范围广泛的集合映射,set集合是最常见的。对于一个多对多的关联,或n:m实体关系,必需要有一张关联表。在该表中的每一行表示一个人和一个事件之间的一个链接(就是将人和事件关联起来)。表名是使用set集合元素的表属性声明的。对于person类的映射配置,关联中的标识符列名称是用<key>标签定义,关联事件那边的列名称用<many-to-many>标签的column属性定义。事件的列的名称与多对多的列属性的列一起。你还要告诉你Hibernate关联事件那边的类名称(这个类的定义在见Hibernate
第一个Hibernate应用程序)。(如果Event类与Person类不在一个包下,可能还需要带上包名)

The database schema for this mapping is therefore:

因此此时映射的数据库结构如下:

_____________        __________________
|             |      |                  |       _____________
|   EVENTS    |      |   PERSON_EVENT   |      |             |
|_____________|      |__________________|      |    PERSON   |
|             |      |                  |      |_____________|
| *EVENT_ID   | <--> | *EVENT_ID        |      |             |
|  EVENT_DATE |      | *PERSON_ID       | <--> | *PERSON_ID  |
|  TITLE      |      |__________________|      |  AGE        |
|_____________|                                |  FIRSTNAME  |
|  LASTNAME   |
|_____________|



1.2.3. Working the association 联系的运作

Now we will bring some people and events together in a new method in 
EventManager
:
现在,我们将在EvenManager类中新建一个方法,把一些人和事件关联起来:


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();
}


After loading a 
Person
 and an 
Event
,
simply modify the collection using the normal collection methods. There is no explicit call to 
update()
 or 
save()
;
Hibernate automatically detects that the collection has been modified and needs to be updated. This is called automatic dirty checking. You can also try it by modifying the name or the date property of any of your objects.
As long as they are in persistent state, that is, bound to a particular Hibernate 
org.hibernate.Session
, Hibernate
monitors any changes and executes SQL in a write-behind fashion. The process of synchronizing the memory state with the database, usually only at the end of a unit of work, is called flushing. In our code, the unit of
work ends with a commit, or rollback, of the database transaction.
在加载一个Person对象和一个Even对象后,只需简单的使用集合标准的方法修改集合。没有显式调用update()或save();Hibernate自动检测,集合已被修改,需要更新。这被称为自动脏数据检查。您也可以通过修改您的任何对象的名称或日期属性来尝试。只要对象是在持久态状态,即,对象绑定到一个特定的Hibernate的
org.hibernate.session对象,Hibernate监控任何变化,并且在后台执行对应的SQL。将内存状态与数据库同步的过程,通常只在一个工作单元的结束,被称为“刷新”。在我们的代码中,工作单元的结束以数据库事务的提交或回滚结束。

You can load person and event in different units of work. Or you can modify an object outside of a
org.hibernate.Session
, when it is not in persistent
state (if it was persistent before, this state is calleddetached). You can even modify a collection when it is detached:

你可以在不同的工作单元中加载人和事件对象。或者,当对象不在持久态(如果它之前是,现在这个状态状态被叫做托管态),你可以修改这个对象,这个修改在org.hibernate.session之外(即session被提交或者关闭了)。甚至当一个集合是托管态时,你扔能修改:


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();
}


The call to 
update
 makes a detached object persistent again by binding it to a new unit of work, so any modifications you
made to it while detached can be saved to the database. This includes any modifications (additions/deletions) you made to a collection of that entity object.
调用update方法,通过将一个托管的对象重新绑定到一个新的工作单元,使一个托管对象再次变为持久态,所以,你在对象处于托管态时,对他做的任何修改,都可以保存到数据库。这包括对实体对象的集合所做的任何修改(添加/删除)。(实体对象的集合应该指的是实体类里定义了关联关系的集合属性)
This is not much use in our example, but it is an important concept you can incorporate into your
own application. Complete this exercise by adding a new action to the main method of the EventManager and call it from the command line. If you need the identifiers of a person and an event - the save() method returns it (you might have to modify some of the
previous methods to return that identifier):


在我们的例子中,这是没有多大用处,但它是一个重要的概念,你可以把他运用到你自己的应用中。完成接下来的练习,请在[b]Eventmanager的main方法中添加一个新的动作,并且从命令行调用它。如果你需要一个人和一个事件对象的标识符,可以使用save()方法,它将返回的标识符(您可能必须修改一些以前的方法来返回该标识符):
[/b]



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);
}


This is an example of an association between two equally important classes : two entities. As mentioned earlier, there are other classes and types in a typical model, usually "less important". Some you have already seen, like
an 
int
 or a 
java.lang.String
. We call these
classes value types, and their instances dependon a particular entity. Instances of these types do not have their own identity, nor are they shared between entities. Two persons
do not reference the same 
firstname
 object, even if they have the same first name. Value types cannot only be found in the JDK , but you can also write
dependent classes yourself such as an
Address
 or 
MonetaryAmount
 class.
In fact, in a Hibernate application all JDK classes are considered value types.
这是两个同样重要的类之间的关联的一个例子:两个实体。如前所述,在一个典型的模型中有其他类和类型,通常“不那么重要”。一些你已经看到的,如int或java.lang.string。我们称这些类为值类型,它们的实例依赖于一个特定的实体。这些类型的实例没有自己的标识,它们也不在实体之间共享。两个persons引用的不是同一个对象,即使他们有同样的firstname名字。值类型只是在JDK中不能被发现,但是你也可以写自己的类作为依赖,如Address
MonetaryAmount类
。事实上,在一个Hibernate应用所有JDK的类都被认为是值类型。
You can also design a collection of value types. This is conceptually different from a collection
of references to other entities, but looks almost the same in Java.


您还可以设计一个值类型的集合。这是从收集到的其他实体引用不同的概念,但[b]在java中看起来几乎相同的。[/b]


1.2.4. Collection of values  值集合

Let's add a collection of email addresses to the 
Person
 entity. This will be represented as a 
java.util.Set
 of
java.lang.String
 instances:

让我们向Person实体添加一个电子邮件地址的集合。这将为一个String的Set集合实例被体现:

private Set emailAddresses = new HashSet();

public Set getEmailAddresses() {
return emailAddresses;
}

public void setEmailAddresses(Set emailAddresses) {
this.emailAddresses = emailAddresses;
}


The mapping of this 
Set
 is as follows:

这个Set集合的映射配置如下:

<set name="emailAddresses" table="PERSON_EMAIL_ADDR">
<key column="PERSON_ID"/>
<element type="string" column="EMAIL_ADDR"/>
</set>


The difference compared with the earlier mapping is the use of the 
element
 part which tells Hibernate that the collection does not contain references
to another entity, but is rather a collection whose elements are values types, here specifically of type 
string
. The lowercase name tells you it is a Hibernate
mapping type/converter. Again the 
table
 attribute of the 
set
 element
determines the table name for the collection. The
key
 element defines the foreign-key column name in the collection table. The 
column
 attribute
in the 
element
element defines the column name where the email address values will actually be stored.

与先前的映射相比,差异是<element>标签部分,告诉Hibernate,集合不包含另一个实体引用的使用,而是值类型元素的一个集合,这里专门指的是string类型。小写的名字告诉你,这是一个Hibernate映射类型/转换器。再次,<set>标签的table属性确定集合映射的数据库表名。<key>标签在集合表中定义外键列的名称,<element>标签中的column属性定义了将要存储的电子邮件地址值的列名称。

Here is the updated schema:

下面是更新后数据库结构:

_____________        __________________
|             |      |                  |       _____________
|   EVENTS    |      |   PERSON_EVENT   |      |             |       ___________________
|_____________|      |__________________|      |    PERSON   |      |                   |
|             |      |                  |      |_____________|      | PERSON_EMAIL_ADDR |
| *EVENT_ID   | <--> | *EVENT_ID        |      |             |      |___________________|
|  EVENT_DATE |      | *PERSON_ID       | <--> | *PERSON_ID  | <--> |  *PERSON_ID       |
|  TITLE      |      |__________________|      |  AGE        |      |  *EMAIL_ADDR      |
|_____________|                                |  FIRSTNAME  |      |___________________|
|  LASTNAME   |
|_____________|


You can see that the primary key of the collection table is in fact a composite key that uses both columns. This also implies that there cannot be duplicate email addresses per person, which is exactly the semantics we need for a set in Java.

你可以看到关系表的主键实际上是一个使用两个列的复合键。这也意味着不能每个人重复的电子邮件地址,这正是我们需要在java中使用set的语义。

You can now try to add elements to this collection, just like we did before by linking persons and events. It is the same code in Java:

您现在可以尝试添加对象到这个集合,就像我们以前所做的,通过关联人和事件。这是java代码相同:

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();
}


This time we did not use a fetch query to initialize the collection. Monitor the SQL log and try to optimize this with an eager fetch.

这一次,我们没有使用一个fetch 查询来初始化集合。监控SQL日志并尝试使用eager fetch查询优化。


1.2.5. Bi-directional associations 双向关联

Next you will map a bi-directional association. You will make the association between person and event work from both sides in Java. The database schema does not change, so you will still have many-to-many multiplicity.

下一步你将映射一个双向关联。你将做一个人和事件之间的双向关联。数据库表结构不会改变,所以您仍然会有多对多的多样性。


Note 备注
A relational database is more flexible than a network programming language, in that it does not need a navigation direction; data can be viewed and retrieved in any possible way.
关系数据库比网络编程语言更灵活,它不需要一个导航方向;数据可以以任何可能的方式来查看和检索。

First, add a collection of participants to the 
Event
 class:

首先,向Event类新增一个名为参与者的集合类型属性:


private Set participants = new HashSet();

public Set getParticipants() {
return participants;
}

public void setParticipants(Set participants) {
this.participants = participants;
}


Now map this side of the association in 
Event.hbm.xml
.

现在,在Event.hbm.xml文件中配置关系映射。


<set name="participants" table="PERSON_EVENT" inverse="true">
<key column="EVENT_ID"/>
<many-to-many column="PERSON_ID" class="Person"/>
</set>


These are normal 
set
 mappings in both mapping documents. Notice that the column names in 
key
 and
many-to-many
 swap
in both mapping documents.

The most important addition here is the 
inverse="true"
 attribute in the 
set
 element
of the 
Event
's collection mapping.

在两边的映射文档中的这些都是标准的集合映射。注意,在两个映射文件中的key和many-to-many交换。
这里最重要的除了是事件[b]集合映射的<set>标签中的inverse="true"属性。[/b]



What this means is that Hibernate should take the other side, the 
Person
 class, when it needs to find out information about the link between the two. 

This will be a lot easier to understand once you see how the bi-directional link between our two entities is created.

当需要了解两者之间的联系信息时,这意味着Hibernate应该采取的另一边,人类。一旦你看到我们的两个实体之间的双向链接是如何创建的,这将是更容易理解。


1.2.6. Working bi-directional links 双向联系工作流程

First, keep in mind that Hibernate does not affect normal Java semantics. How did we create a link between a 
Person
 and an 
Event
 in
the unidirectional example? You add an instance of 
Event
 to the collection of event references, of an instance of 
Person
.
If you want to make this link bi-directional, you have to do the same on the other side by adding a 
Person
 reference to the collection in an 
Event
.
This process of "setting the link on both sides" is absolutely necessary with bi-directional links.

首先,请记住,Hibernate不影响正常java语义。我们如何在一个单向的例子中创建一个人和一个事件之间的联系?您将事件的一个实例添加到事件引用的集合,一个人的实例。如果你想使这个链接的双向,你必须做相同的另一边,通过添加一个引用到集合中的事件。这个“设置双方的链接”的过程是绝对必要的,与双向链接。

Many developers program defensively and create link management methods to correctly set both sides (for example, in 
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);
}


The get and set methods for the collection are now protected. This allows classes in the same package and subclasses to still access the methods, but prevents everybody else from altering the collections directly. Repeat the steps for the collection on the
other side.

集合的获取和设置方法现在已被保护。这允许在同一个包和子类中的类仍然访问该方法,但防止其他人直接修改集合。重复另一边的集合的步骤。

What about the 
inverse
 mapping attribute? For you, and for Java, a bi-directional link is simply a matter of setting the references on both sides correctly.
Hibernate, however, does not have enough information to correctly arrange SQL 
INSERT
 and 
UPDATE
 statements
(to avoid constraint violations). Making one side of the association 
inverse
 tells Hibernate to consider it a mirror of
the other side. That is all that is necessary for Hibernate to resolve any issues that arise when transforming a directional navigation model to a SQL database schema. The rules are straightforward: all bi-directional associations need one side as 
inverse
.
In a one-to-many association it has to be the many-side, and in many-to-many association you can select either side.

inverse映射属性是做什么的呢?对于你,和对于java来说,双向链接是一件简单的事,设定双方的引用正确。然而,Hibernate没有足够的信息来正确设置SQL
INSERT和UPDATE语句(以避免违反约束)。使联系的一边告诉Hibernate来考虑它的另一面镜子。这就是所有的Hibernate解决时出现的定向导航模型转化到SQL数据库架构中任何问题是必要的。规则很简单:所有的双向关联都需要一方作为inverse。在一对多的关联中,它必须是多的那一方。在多对多的关联中,你可以选择任何一方。

附上项目源码:git@code.csdn.net:xiaozaq/hibernatewebdemo.git
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: