Hibernate学习英文文档翻译2

本文介绍Hibernate框架中如何进行实体类间的关联映射,包括单向集合关联、双向关联及值类型集合的映射方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >



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.

到目前为止,我们已经匹配了一个简单的持久化的实体类到孤立的一个表中。让我们扩展一些并且添加一些类的关联。我们将会添加people到这个应用中并且存储他们分担的一系列的时间。

1.2.1. Mapping the Person class

关联Person类

The first cut of the Person class looks like this:

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

保存这个文件,命名为:Person.

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 callingPerson#getEvents. Multi-valued associations are represented in Hibernate by one of the Java Collection Framework contracts; here we choose ajava.util.Set because the collection will not contain duplicate elements and the ordering is not relevant to our examples:

通过增加一个事件的集合到Person类中,对于一个特定的人,你可以很容易的找到对应的事件,在没有执行一个明确的查询-通过调用Person的getEvents方法。

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 theEvent, 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 amany-to-many association. Hence, we use Hibernate's many-to-many mapping:

匹配这个关联之前,让我们考虑另一种情况。我们应该

<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 thetable attribute of theset element. The identifier column name in the association, for the person side, is defined with thekey element, the column name for the event's side with thecolumn attribute of themany-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). 

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 inEventManager:

 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 anEvent, simply modify the collection using the normal collection methods. There is no explicit call toupdate() orsave(); Hibernate automatically detects that the collection has been modified and needs to be updated. This is calledautomatic 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 inpersistent state, that is, bound to a particular Hibernateorg.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 calledflushing. In our code, the unit of work ends with a commit, or rollback, of the database transaction.

You can load person and event in different units of work. Or you can modify an object outside of aorg.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:
 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.

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 theEventManager and call it from the command line. If you need the identifiers of a person and an event - thesave() method returns it (you might have to modify some of the previous methods to return that identifier):

 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 anint or a java.lang.String. We call these classesvalue types, and their instancesdepend on 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 samefirstname 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 anAddress orMonetaryAmount class. In fact, in a Hibernate application all JDK classes are considered value types.

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.

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 ofjava.lang.String instances:

  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 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 theelement 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 typestring. The lowercase name tells you it is a Hibernate mapping type/converter. Again thetable attribute of theset element determines the table name for the collection. Thekey element defines the foreign-key column name in the collection table. Thecolumn attribute in theelement element defines the column name where the email address values will actually be stored.

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.

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:

 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.

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:

  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.
  <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 inkey andmany-to-many swap in both mapping documents. The most important addition here is theinverse="true" attribute in theset element of theEvent's collection mapping.

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.


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 aPerson and anEvent in the unidirectional example? You add an instance ofEvent to the collection of event references, of an instance ofPerson. If you want to make this link bi-directional, you have to do the same on the other side by adding aPerson reference to the collection in anEvent. This process of "setting the link on both sides" is absolutely necessary with bi-directional links.

Many developers program defensively and create link management methods to correctly set both sides (for example, inPerson):

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 associationinverse tells Hibernate to consider it amirror 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 asinverse. In a one-to-many association it has to be the many-side, and in many-to-many association you can select either side.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值