Hibernate One to Many Annotation Tutorial – Hibernate 一对多注释教程

最后修改: 2017年 2月 8日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.介绍

This quick Hibernate tutorial will take us through an example of a one-to-many mapping using JPA annotations, an alternative to XML.

这个快速的Hibernate教程将带领我们学习一个使用JPA注解的一对多映射的例子,JPA注解是XML的替代品。

We’ll also learn what bidirectional relationships are, how they can create inconsistencies, and how the idea of ownership can help.

我们还将学习什么是双向关系,它们如何造成不一致,以及所有权的概念如何能够帮助。

2. Description

2.描述

Simply put, one-to-many mapping means that one row in a table is mapped to multiple rows in another table.

简单地说,一对多映射意味着一个表中的一条记录被映射到另一个表中的多条记录。

Let’s look at the following entity-relationship diagram to see a one-to-many association:

让我们看一下下面的实体关系图,看看一个一对多的关联。

C-1

For this example, we’ll implement a cart system where we have a table for each cart and another table for each item. One cart can have many items, so here we have a one-to-many mapping.

在这个例子中,我们将实现一个购物车系统,每个购物车有一个表,每个物品有另一个表。一个购物车可以有很多物品,所以这里我们有一个一对多的映射。

The way this works at the database level is we have a cart_id as a primary key in the cart table and also a cart_id as a foreign key in items.

这在数据库层面的工作方式是,我们有一个cart_id作为cart表中的主键,还有一个cart_id作为items的外键。

The way we do it in code is with @OneToMany.

我们在代码中的方法是使用@OneToMany

Let’s map the Cart class to the collection of Item objects in a way that reflects the relationship in the database:

让我们将Cart类映射到Item对象的集合,以反映数据库中的关系。

public class Cart {

    //...     
 
    @OneToMany(mappedBy="cart")
    private Set<Item> items;
	
    //...
}

We can also add a reference to Cart in each Item using @ManyToOne, making this a bidirectional relationship. Bidirectional means that we are able to access items from carts, and also carts from items.

我们还可以在每个Item中使用@ManyToOne添加对Cart的引用,使其成为双向关系。双向意味着我们能够从购物车中访问物品,也能够从物品中访问购物车

The mappedBy property is what we use to tell Hibernate which variable we are using to represent the parent class in our child class.

mappedBy属性是我们用来告诉Hibernate我们在子类中使用哪个变量来代表父类的。

The following technologies and libraries are used in order to develop a sample Hibernate application that implements one-to-many association:

为了开发一个实现一对多关联的Hibernate示例应用程序,我们使用了以下技术和库。

  • JDK 1.8 or later
  • Hibernate 5
  • Maven 3 or later
  • H2 database

3. Setup

3.设置

3.1. Database Setup

3.1.数据库设置

We’ll use Hibernate to manage our schema from the domain model. In other words, we don’t need to provide the SQL statements to create the various tables and relationships between the entities. So let’s move on to creating the Hibernate example project.

我们将使用Hibernate来管理我们来自领域模型的模式。换句话说,我们不需要提供SQL语句来创建各种表和实体之间的关系。因此,让我们继续创建Hibernate示例项目。

3.2. Maven Dependencies

3.2.Maven的依赖性

Let’s start by adding the Hibernate and H2 driver dependencies to our pom.xml file. The Hibernate dependency uses JBoss logging, and it automatically gets added as transitive dependencies:

让我们先在pom.xml文件中加入Hibernate和H2驱动的依赖。Hibernate的依赖关系使用JBoss的日志,它被自动添加为过渡性依赖关系。

  • Hibernate version 5.6.7.Final
  • H2 driver version 2.1.212

Please visit the Maven central repository for the latest versions of Hibernate and the H2 dependencies.

请访问Maven中央仓库,了解HibernateH2依赖项的最新版本。

3.3. Hibernate SessionFactory

3.3.Hibernate的SessionFactory

Next, let’s create the Hibernate SessionFactory for our database interactions:

接下来,让我们创建Hibernate SessionFactory,用于我们的数据库交互。

public static SessionFactory getSessionFactory() {

    ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
      .applySettings(dbSettings())
      .build();

    Metadata metadata = new MetadataSources(serviceRegistry)
      .addAnnotatedClass(Cart.class)
      // other domain classes
      .buildMetadata();

    return metadata.buildSessionFactory();
}

private static Map<String, String> dbSettings() {
    // return Hibernate settings
}

4. The Models

4.模型

The mapping-related configurations will be done using JPA annotations in the model classes:

与映射相关的配置将在模型类中使用JPA注解来完成。

@Entity
@Table(name="CART")
public class Cart {

    //...

    @OneToMany(mappedBy="cart")
    private Set<Item> items;
	
    // getters and setters
}

Please note that the @OneToMany annotation is used to define the property in Item class that will be used to map the mappedBy variable. That is why we have a property named “cart” in the Item class:

请注意,@OneToMany 注解用于定义 Item 类中的属性,该属性将被用于映射 mappedBy 变量。这就是为什么我们在Item类中有一个名为”cart“的属性。

@Entity
@Table(name="ITEMS")
public class Item {
    
    //...
    @ManyToOne
    @JoinColumn(name="cart_id", nullable=false)
    private Cart cart;

    public Item() {}
    
    // getters and setters
}

It’s also important to note that the @ManyToOne annotation is associated with the Cart class variable. @JoinColumn annotation references the mapped column.

还需要注意的是,@ManyToOne 注解与Cart类变量相关。@JoinColumn 注解引用了映射的列。

5. In Action

5 在行动中

In the test program, we are creating a class with a main() method for getting the Hibernate Session, and saving the model objects into the database implementing the one-to-many association:

在测试程序中,我们正在创建一个带有main()方法的类,用于获取Hibernate Session,并将模型对象保存到实现one-to-many关联的数据库。

sessionFactory = HibernateAnnotationUtil.getSessionFactory();
session = sessionFactory.getCurrentSession();
System.out.println("Session created");
	    
tx = session.beginTransaction();

session.save(cart);
session.save(item1);
session.save(item2);
	    
tx.commit();
System.out.println("Cart ID=" + cart.getId());
System.out.println("item1 ID=" + item1.getId()
  + ", Foreign Key Cart ID=" + item.getCart().getId());
System.out.println("item2 ID=" + item2.getId()
+ ", Foreign Key Cart ID=" + item.getCart().getId());

This is the output of our test program:

这是我们测试程序的输出。

Session created
Hibernate: insert into CART values ()
Hibernate: insert into ITEMS (cart_id)
  values (?)
Hibernate: insert into ITEMS (cart_id)
  values (?)
Cart ID=7
item1 ID=11, Foreign Key Cart ID=7
item2 ID=12, Foreign Key Cart ID=7
Closing SessionFactory

6. The @ManyToOne Annotation

6.@ManyToOne 注释

As we have seen in section 2, we can specify a many-to-one relationship by using the @ManyToOne annotation. A many-to-one mapping means that many instances of this entity are mapped to one instance of another entity – many items in one cart.

正如我们在第2节所看到的,我们可以通过使用@ManyToOne注解来指定一个many-to-one关系。many-to-one映射意味着这个实体的许多实例被映射到另一个实体的一个实例–一个购物车中的许多物品

The @ManyToOne annotation lets us create bidirectional relationships too. We’ll cover this in detail in the next few subsections.

@ManyToOne注解让我们也可以创建双向关系。我们将在接下来的几个小节中详细介绍。

6.1. Inconsistencies and Ownership

6.1.不一致和所有权

Now, if Cart referenced Item, but Item didn’t in turn reference Cart, our relationship would be unidirectional. The objects would also have a natural consistency.

现在,如果Cart引用了Item,但是Item并没有反过来引用Cart我们的关系将是单向的这些对象也将具有自然的一致性。

In our case though, the relationship is bidirectional, bringing in the possibility of inconsistency.

但在我们的案例中,这种关系是双向的,带来了不一致的可能性。

Let’s imagine a situation where a developer wants to add an item1 to the cart1 instance and an item2 to the cart2 instance, but makes a mistake so that the references between cart2 and item2 become inconsistent:

让我们设想这样一种情况:一个开发者想在cart1实例中添加一个item1,在cart2实例中添加一个item2,但是犯了一个错误,这样cart2item2之间的引用变得不一致。

Cart cart1 = new Cart();
Cart cart2 = new Cart();

Item item1 = new Item(cart1);
Item item2 = new Item(cart2); 
Set<Item> itemsSet = new HashSet<Item>();
itemsSet.add(item1);
itemsSet.add(item2); 
cart1.setItems(itemsSet); // wrong!

As shown above, item2 references cart2, whereas cart2 doesn’t reference item2, and that’s bad.

如上所示,item2引用了cart2,cart2没有引用item2,这很糟糕

How should Hibernate save item2 to the database? Will item2 foreign key reference cart1 or cart2?

Hibernate应如何将item2保存到数据库中?item2的外键将引用cart1cart2

We resolve this ambiguity using the idea of an owning side of the relationship; references belonging to the owning side take precedence and are saved to the database.

我们使用关系的拥有方的概念来解决这个模糊的问题;属于拥有方的引用具有优先权并被保存到数据库。

6.2. Item as the Owning Side

6.2.Item作为所有权方

As stated in the JPA specification under section 2.9, it’s a good practice to mark the many-to-one side as the owning side.

正如JPA规范中第2.9节所述,多对一侧标记为拥有侧是一种良好的做法。

In other words, Item would be the owning side and Cart the inverse side, which is exactly what we did earlier.

换句话说,Item将是拥有方,Cart是反方,这正是我们之前做的。

So how did we achieve this?

那么,我们是如何实现这一目标的呢?

By including the mappedBy attribute in the Cart class, we mark it as the inverse side.

通过在Cart类中包含mappedBy属性,我们将其标记为反面。

At the same time, we also annotate the Item.cart field with @ManyToOne, making Item the owning side.

同时,我们也用Item.cart字段注释了@ManyToOne,使Item成为拥有方。

Going back to our “inconsistency” example, now Hibernate knows that item2‘s reference is more important and will save item2‘s reference to the database.

回到我们的 “不一致 “例子,现在 Hibernate 知道 item2 的引用更重要,并将 item2 的引用保存到数据库中。

Let’s check the result:

让我们检查一下结果。

item1 ID=1, Foreign Key Cart ID=1
item2 ID=2, Foreign Key Cart ID=2

Although cart references item2 in our snippet, item2‘s reference to cart2 is saved in the database.

尽管cart在我们的代码段中引用了item2,但item2cart2的引用被保存在数据库中。

6.3. Cart as the Owning Side

6.3.卡特尔作为所有权方

It’s also possible to mark the one-to-many side as the owning side, and the many-to-one side as the inverse side.

也可以将一对多的一方标记为拥有方,而将多对一的一方标记为反方。

Although this is not a recommended practice, let’s go ahead and give it a try.

虽然这不是一种推荐的做法,但我们还是要去试一试。

The code snippet below shows the implementation of the one-to-many side as the owning side:

下面的代码片断显示了作为拥有方的一对多方的实现。

public class ItemOIO {
    
    //  ...
    @ManyToOne
    @JoinColumn(name = "cart_id", insertable = false, updatable = false)
    private CartOIO cart;
    //..
}

public class CartOIO {
    
    //..  
    @OneToMany
    @JoinColumn(name = "cart_id") // we need to duplicate the physical information
    private Set<ItemOIO> items;
    //..
}

Notice how we removed the mappedBy element and set the many-to-one @JoinColumn as insertable and updatable to false.

注意我们如何删除了mappedBy元素,并将many-to-one @JoinColumn设置为insertableupdatablefalse

If we run the same code, the result will be the opposite:

如果我们运行同样的代码,结果将是相反的。

item1 ID=1, Foreign Key Cart ID=1
item2 ID=2, Foreign Key Cart ID=1

As shown above, now item2 belongs to the cart.

如上所示,现在item2属于cart.

7. Conclusion

7.结论

We have seen how easy it is to implement the one-to-many relationship with the Hibernate ORM and H2 database using JPA annotations.

我们已经看到用Hibernate ORM和H2数据库使用JPA注解实现一对多关系是多么容易。

Additionally, we learned about bidirectional relationships and how to implement the notion of an owning side.

此外,我们还学习了双向关系以及如何实现拥有方的概念。

The source code in this article can be found over on GitHub.

本文的源代码可以在GitHub上找到超过