我是靠谱客的博主 想人陪自行车,最近开发中收集的这篇文章主要介绍在 JPA 和Hibernate中映射多对多关系的 6 种方法,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

有几种方法可以通过使用@ManyToMany、@OneToMany和@ManyToOne来映射 JPA 和Hibernate中的多对多关系,包括

  • 使用单个主键、@OneToMany和@ManyToOne连接实体单向和双向映射

  • 使用复合主键、@OneToMany和@ManyToOne连接实体单向和双向映射

  • 不带连接实体的单向和双向映射,带@ManyToMany

本指南将向您展示如何绘制地图以及每种方法的优缺点

考虑出版商和书籍之间的关系。一个出版商可以出版许多书籍,一本书可以由多个出版商出版

1) 使用单个主键和@ManyToOne连接实体单向映射

连接的实体将使用单个@Id和 2 个@ManyToOne进行定义。父实体上没有子集合关联映射

@Entity
public class BookPublisher {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "book_id")
private Book book;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "publisher_id")
private Publisher publisher;
@Column(name = "published_date")
private Date publishedDate;
...
}
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
...
}
@Entity
public class Publisher {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
...
}

“获取类型”用于@ManyToOne,而不是默认的“获取类型”。.

@JoinColumn指定外键列。它是可选的,默认名称为关联字段名称及其主键列名称的下划线字符串连接

优点和缺点

  • 单主键映射比复合主键更简单

  • 避免@OneToMany的潜在性能问题

  • 父实体无法快速导航或级联 CRUD 操作到子集合。但是,您可以通过 JPQL 查询手动执行此操作

动手教程

  • 具有联接实体和单个主键的多对多单向映射

2) 使用单个主键、@ManyToOne和@OneToMany连接实体双向映射

除了像上面这样定义连接实体的单向映射之外,我们还将定义子集合与父实体上@OneToMany的关联。

@Entity
public class BookPublisher {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "book_id")
private Book book;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "publisher_id")
private Publisher publisher;
@Column(name = "published_date")
private Date publishedDate;
...
}
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
@OneToMany(mappedBy = "book", cascade = CascadeType.ALL)
private Set<BookPublisher> bookPublishers = new HashSet<>();
...
}
@Entity
public class Publisher {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
@OneToMany(mappedBy = "publisher", cascade = CascadeType.ALL)
private Set<BookPublisher> bookPublishers = new HashSet<>();
...
}

@JoinColumn指定外键列。对于此映射,默认名称为关联字段名称及其主键列名称的下划线字符串连接是可选的

相反,需要为双向映射指定@OneToMany上的 mapBy 属性。如果映射 By 不存在,JPA 和休眠将自动创建冗余联接表,如下所示book_book_publishers

+--------------------+---------+------+-----+---------+-------+
| Field
| Type
| Null | Key | Default | Extra |
+--------------------+---------+------+-----+---------+-------+
| book_id
| int(11) | NO
| PRI | NULL
|
|
| book_publishers_id | int(11) | NO
| PRI | NULL
|
|
+--------------------+---------+------+-----+---------+-------+

优点和缺点

  • 单主键映射比复合主键更简单

  • 关系的双方可以快速访问和级联 CRUD 操作

  • @OneToMany可能会导致大型子集合出现性能问题

动手教程

  • 弹簧启动 JPA 和休眠双向多到多额外列映射与连接实体和单个主键

3) 使用复合主键、@ManyToOne和@OneToMany连接实体单向和双向映射

复合主键将由自定义类定义,该类使用@Embeddable进行批注,并实现可序列化。还应定义其等式和哈希码方法

@Embeddable
public class BookPublisherId implements Serializable {
@Column(name = "book_id")
private Integer bookId;
@Column(name = "publisher_id")
private Integer publisherId;
...
}

连接实体将使用 1 个用 @EmbeddedId 标记@Embeddable对象字段和 2 个用@MapsId标记的@ManyToOne来定义,这些字段指向@Embeddable类中定义的关键字段

@Entity
public class BookPublisher {
@EmbeddedId
private BookPublisherId id;
@ManyToOne
@MapsId("bookId")
@JoinColumn(name = "book_id")
private Book book;
@ManyToOne
@MapsId("publisherId")
@JoinColumn(name = "publisher_id")
private Publisher publisher;
@Column(name = "published_date")
private Date publishedDate;
public BookPublisher(Book book, Publisher publisher, Date publishedDate) {
this.id = new BookPublisherId(book.getId(), publisher.getId());
this.book = book;
this.publisher = publisher;
this.publishedDate = publishedDate;
}
...
}
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
...
}
@Entity
public class Publisher {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
...
}

要在此映射中保留联接的实体,需要手动填充@EmbededId值,因为 Hibernate 无法通过反射设置该值,否则,您将在控制台中收到以下错误

Caused by: org.hibernate.PropertyAccessException: Could not set field value by reflection

在 BookPubler 的上述实体映射中,我们通过其构造函数进行了配置。

@Entity
public class BookPublisher {
...
public BookPublisher(Book book, Publisher publisher, Date publishedDate) {
this.id = new BookPublisherId(book.getId(), publisher.getId());
...
}
...
}
这对于单向映射来说已经足够了。在双向映射的情况下,如果需要快速导航并将 CRUD 操作级联到子集合关联,请将@OneToMany放在父实体上,如上述方法 2

优点和缺点

  • 比单个主键映射更复杂

  • 具有@OneToMany的双向映射可能会导致大型子集合关联的性能问题

动手教程

  • 弹簧启动 JPA 和休眠双向多到多额外列映射与连接实体和复合主键

4) 无连接实体单向和双向映射与@ManyToMany

此映射不会使用任何联接的实体。@ManyToMany将放置在一侧进行单向映射,或放置在两侧进行双向映射

@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "book_publisher",
joinColumns = @JoinColumn(name = "book_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "publisher_id", referencedColumnName = "id"))
private Set<Publisher> publishers = new HashSet<>();
...
}
@Entity
public class Publisher {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@ManyToMany(mappedBy = "publishers")
private Set<Book> books = new HashSet<>();
...
}

优点和缺点

  • 通过双向映射,关系的双方可以快速访问和级联 CRUD 操作

  • 最简单的多对多映射。但是,它仅适用于没有额外列的联接表,并且与@OneToMany一样,@ManyToMany也可能导致大型子集合的性能问题

动手教程

  • 弹簧启动JPA和休眠多对多,没有额外的列映射与@ManyToMany

最后

以上就是想人陪自行车为你收集整理的在 JPA 和Hibernate中映射多对多关系的 6 种方法的全部内容,希望文章能够帮你解决在 JPA 和Hibernate中映射多对多关系的 6 种方法所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(52)

评论列表共有 0 条评论

立即
投稿
返回
顶部