学习如何将上一课中创建的单向多对多关系改为双向关系。

在一个双向的关系中,每一方都有对另一方的引用。在我们上一课的例子中,Category类没有对Tournament类的任何引用。现在我们将添加一个对锦标赛类的引用,这样关系就可以从两边进行导航。这不会对基础数据库结构产生任何影响。连接表tournament_catogories已经有了锦标赛和类别表的外键,可以编写SQL查询来获取与类别相关的锦标赛。

截屏2022-05-27 21.36.41.png

对于多对多的关系,我们可以选择任何一方作为所有者。该关系在所有者一方使用@JoinTable注解进行配置。在目标方,我们使用mappedBy属性来指定在拥有方映射关系的字段名。从数据库设计的角度来看,多对多的关系没有所有者。如果我们交换@JoinTablemappedBy,对表的结构不会有任何影响。

  1. 我们将首先在Category类中创建一个tournamentsList,以及gettersetter方法。
package io.datajek.databaserelationships.manytomany;

@Entity
public class Category {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private int id;
 
    @Column(unique = true)
    private String name;

    private List<Tournament> tournaments = new ArrayList<>();
    //...
}

mappedBy属性

  1. 在上面创建的tournaments字段上,使用@ManyToMany注解与mappedBy属性。这显示了用于映射锦标赛类中的关系的值。

    @ManyToMany(mappedBy= "playingCategories")
    private List<Tournament> tournaments = new ArrayList<>();
    

    截屏2022-05-27 21.39.09.png

  2. 我们还将使用级联属性来级联除REMOVE以外的所有操作,因为如果一个类别被删除,我们不希望删除所有相关的锦标赛。

    @ManyToMany(mappedBy= "playingCategories",
            cascade= {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH},
            fetch=FetchType.LAZY)
        private List<Tournament> tournaments = new ArrayList<>();
    
  3. 管理双向关系是应用程序的责任。当我们将一个类别添加到锦标赛中时,我们也必须将锦标赛添加到该类别中以保持双向关系。如果不这样做,可能会导致意外的JPA行为。

    我们将更新Tournament类中的addCategory方法,通过将锦标赛添加到类别中来建立双向关系。

    public void addCategory(Category category) {
       playingCategories.add(category);
       //建立双向关系
       category.getTournaments().add(this);
    }
    

    同样地,我们将更新Tournament类中的removeCategory方法,以便从两边删除关联。

    public void removeCategory(Category category) {
       if (playingCategories != null)
              playingCategories.remove(category);
       //更新双向关系
       category.getTournaments().remove(this);
    }
    

@JsonIgnoreProperties

  1. 当试图对双向关系进行反序列化时,JSON会陷入无限递归。在 "一对一双向关系 "一课中,我们已经看到了解决这个问题的两种方法。在这里,我们将看到另一种避免无限递归的方法。我们可以用@JsonIgnoreProperties来忽略我们想忽略的属性。这个注解可以在锦标赛和类别类的字段级使用。

    @JsonIgnoreProperties("tournaments")
    private List<Category> playingCategories = new ArrayList<>();
    
    @JsonIgnoreProperties("playingCategories")
    private List<Tournament> tournaments = new ArrayList<>();
    

<aside> 📢 在多对多的关系中,当涉及到表的结构时,没有所有者。这与一对多的关系不同,在一对多的关系中,多方总是包含一方的键的拥有方。

</aside>

此时所有代码:

6.zip

为了测试这个应用程序,我们将重现上一课的情景,增加两个锦标赛和五个类别。

要创建锦标赛条目,请向/tournaments发送POST请求,如下所示。

{
    "name": "Canadian Open",
    "location": "Toronto"
}
{
    "name": "US Open",
    "location": "New York City"
}