学习如何使上一课中创建的一对一关系成为双向的。本课还展示了如何解决JSON的无限递归问题。
现在,我们有一个单向的一对一的映射,这意味着如果我们有一个Twitter账户,我们无法找到拥有该账户的球员的名字。对/profiles
的GET请求只能得到PlayerProfile
对象,而不是它所关联的Player
。
然而,如果我们有Player
实体,就有可能找到Twitter账户。从对/players
的GET请求中可以看出,PlayerProfile
实体也被获取了。
对/players和/profiles的GET请求
在上一课创建的单向的一对一关系中,Player
类保持着这种关系。PlayerProfile
类不能看到这个关系的任何变化。为了使这种关系成为双向的,我们需要对PlayerProfile
类做一些修改。我们将添加一个字段来引用回到Player
类,并添加@OneToOne
注解。我们还将添加getter
和setter
方法来设置PlayerProfile
类中的Player
值。这将使我们能够从两个方向获取实体。
双向关系
为了建立双向关系,我们将在PlayerProfile
类中添加一个Player
类的字段,并为该字段添加getter
和setter
方法。这个字段持有对相关的Player
实体的引用。
package io.datajek.databaserelationships.onetoone;
@Entity
public class PlayerProfile {
@Id
@GeneratedValue(GenertionType.IDENTITY)
private int id;
private String twitter;
private Player player;
//add getter and setter for player field.
}
toString
方法也需要更新,以包含新增加的Player
字段。
接下来,我们将在Player
字段上添加@OneToOne
注解。
mappedBy
属性mappedBy
是@oneToOne
注解的一个可选的属性,它指定了拥有该关系的字段的名称。在我们的例子中,它是Player
类中的playerProfile
字段。mappedBy
属性只被放置在关系的对方一侧。拥有者的一方不能有这个属性。
@Entity
public class PlayerProfile {
@Id
@GeneratedValue(GenertionType.IDENTITY)
private int id;
private String twitter;
@OneToOne(mappedBy="playerProfile")
private Player player;
}
如果我们访问H2数据库的web控制台(在http://localhost:8080/h2-console
,连接URL为jdbc:h2:mem:testdb
),我们会看到数据库结构没有变化。如下面的实体关系图所示,player_profile
表的主键被存储为player
表中的外键。然而在Java方面,我们现在可以使用PlayerProfile
实体访问Player
实体。
表结构
为了测试双向关系,我们将使用以下JSON用POST请求创建一个带有嵌套PlayerProfile
对象的Player
到http://localhost8080/players 。
{
"name": "Djokovic",
"playerProfile": {
"twitter" : "@DjokerNole"
}
}
这个请求的结果是在player
和player_profile
表中分别有两个INSERT请求。
双向关系意味着德约科维奇拥有**@DjokerNole** Twitter账户,反之亦然,@DjokerNole账户属于德约科维奇。如果我们现在向/profiles
发送一个GET请求,我们也会得到球员的详细信息。
对/profiles的GET请求(双向关系)
此时,你一定在试图访问/profiles
时遇到了一个错误。
当使用双向关系时,当我们试图检索对象时,JSON会抛出一个无限递归的错误。这是因为Player
对象包含对PlayerProfile
对象的引用,而PlayerProfile
对象也包含对Player
对象的引用。
双向关系中的无限递归问题
邮递员中的响应看起来是这样的: