在您的情况下,我建议将@Inheritance 与单个表格一起用于所有类型的文章,而不是@MappedSuperclass:
@Data
@NoArgsConstructor
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "type")
public abstract class Article {
@Id
@GeneratedValue
private Integer id;
private String name;
@ManyToMany
private Set<Newspaper> newspapers;
@ManyToMany
private Set<Author> authors;
public Article(String name, Set<Newspaper> newspapers, Set<Author> authors) {
this.name = name;
this.newspapers = newspapers;
this.authors = authors;
}
@Override
public String toString() {
return getClass().getSimpleName() + "{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
所有类型的文章都将存储在单个表中,您可以通过在@DiscriminatorColumn 注释中设置的列type 确定它们的类型。
然后您将能够在Newspaper 实体中使用单个文章集:
@Data
@NoArgsConstructor
@Entity
public class Newspaper {
@Id
@GeneratedValue
private Integer id;
private String name;
@ManyToMany(mappedBy = "newspapers")
private Set<Author> authors;
@ManyToMany(mappedBy = "newspapers")
private Set<Article> articles;
public Newspaper(String name) {
this.name = name;
}
}
注意使用bi-directionalManyToMany时必须使用的参数mappedBy。
具体文章:
@NoArgsConstructor
@Entity
@DiscriminatorValue("FIRST")
public class FirstTypeArticle extends Article {
public FirstTypeArticle(String name, Set<Newspaper> newspapers, Set<Author> authors) {
super(name, newspapers, authors);
}
}
@NoArgsConstructor
@Entity
@DiscriminatorValue("SECOND")
public class SecondTypeArticle extends Article {
public SecondTypeArticle(String name, Set<Newspaper> newspapers, Set<Author> authors) {
super(name, newspapers, authors);
}
}
注意注解@DiscriminatorValue,它用于设置鉴别器列的值。
作者实体(也有一组文章):
@Data
@NoArgsConstructor
@Entity
public class Author {
@Id
@GeneratedValue
private Integer id;
private String name;
@ManyToMany
private Set<Newspaper> newspapers;
@ManyToMany(mappedBy = "authors")
private Set<Article> articles;
public Author(String name, Set<Newspaper> newspapers) {
this.name = name;
this.newspapers = newspapers;
}
}
对于我的 Spring Boot 2.1.1 演示项目(带有 H2 数据库)中的这些实体集,Hibernate 已创建以下表格,无需任何额外设置:
ARTICLE
ARTICLE_AUTHORS
ARTICLE_NEWSPAPERS
AUTHOR
AUTHOR_NEWSPAPERS
NEWSPAPER
存储库:
public interface ArticleRepo extends JpaRepository<Article, Integer> {
}
public interface AuthorRepo extends JpaRepository<Author, Integer> {
}
public interface NewspaperRepo extends JpaRepository<Newspaper, Integer> {
}
使用示例:
@RunWith(SpringRunner.class)
@DataJpaTest
public class ArticleRepoTest {
@Autowired private ArticleRepo articleRepo;
@Autowired private AuthorRepo authorRepo;
@Autowired private NewspaperRepo newspaperRepo;
private List<Article> articles;
@Before
public void setUp() throws Exception {
List<Newspaper> newspapers = newspaperRepo.saveAll(List.of(
new Newspaper("newspaper1"),
new Newspaper("newspaper2")
));
List<Author> authors = authorRepo.saveAll(List.of(
new Author("author1", new HashSet<>(newspapers)),
new Author("author2", new HashSet<>(newspapers))
));
articles = articleRepo.saveAll(List.of(
new FirstTypeArticle("first type article", new HashSet<>(newspapers), new HashSet<>(authors)),
new SecondTypeArticle("second type article", new HashSet<>(newspapers), new HashSet<>(authors))
));
}
@Test
public void findAll() {
List<Article> result = articleRepo.findAll();
result.forEach(System.out::println);
assertThat(result)
.hasSize(2)
.containsAll(articles);
}
}
更新
我个人不喜欢使用@Inheritance...
我还尝试避免使用 mappedBy 功能,因为我不需要双向寻址...
当然,您可以在Article 中使用@MappedSuperclass 而不是@Inheritance。您可以避免使用mappedBy 并使用unidirectional ManyToMany。
但在这种情况下,您将不得不保存独立的实体,例如 Author 和 Article 到 Newspaper (参见 cascade = CascadeType.MERGE 参数)。至于我,这很不方便(我试图用实用方法 addAuthors 和 addArticles 来中和它):
@Data
@NoArgsConstructor
@Entity
public class Newspaper {
@Id
@GeneratedValue
private Integer id;
private String name;
@ManyToMany(cascade = CascadeType.MERGE)
private Set<Author> authors = new HashSet<>();
@ManyToMany(cascade = CascadeType.MERGE)
private Set<FirstTypeArticle> firstTypeArticles = new HashSet<>();
@ManyToMany(cascade = CascadeType.MERGE)
private Set<SecondTypeArticle> secondTypeArticles = new HashSet<>();
public Newspaper(String name) {
this.name = name;
}
public Newspaper addAuthors(Author... authors) {
if (authors != null) {
this.authors.addAll(Set.of(authors));
}
return this;
}
public Newspaper addArticles(Article... articles) {
for (Article article : articles) {
if (article instanceof FirstTypeArticle) {
this.firstTypeArticles.add((FirstTypeArticle) article);
}
if (article instanceof SecondTypeArticle) {
this.secondTypeArticles.add((SecondTypeArticle) article);
}
}
return this;
}
}
@Data
@NoArgsConstructor
@Entity
public class Author {
@Id
@GeneratedValue
private Integer id;
private String name;
public Author(String name) {
this.name = name;
}
}
@Data
@NoArgsConstructor
@MappedSuperclass
public abstract class Article {
@Id
@GeneratedValue
private Integer id;
private String name;
@ManyToMany(cascade = CascadeType.MERGE)
private Set<Author> authors = new HashSet<>();
public Article(String name, Author... authors) {
this.name = name;
addAuthors(authors);
}
public void addAuthors(Author... authors) {
if (authors != null) {
this.authors.addAll(Set.of(authors));
}
}
@Override
public String toString() {
return getClass().getSimpleName() + "{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
@NoArgsConstructor
@Entity
public class FirstTypeArticle extends Article {
public FirstTypeArticle(String name, Author... authors) {
super(name, authors);
}
}
@NoArgsConstructor
@Entity
public class SecondTypeArticle extends Article {
public SecondTypeArticle(String name, Author... authors) {
super(name, authors);
}
}
然后你会得到以下自动生成的表格:
AUTHOR
FIRST_TYPE_ARTICLE
FIRST_TYPE_ARTICLE_AUTHORS
SECOND_TYPE_ARTICLE
SECOND_TYPE_ARTICLE_AUTHORS
NEWSPAPER
NEWSPAPER_AUTHORS
NEWSPAPER_FIRST_TYPE_ARTICLES
NEWSPAPER_SECOND_TYPE_ARTICLES
使用示例
添加报纸:
newspapers = newspaperRepo.saveAll(List.of(
new Newspaper("newspaper1"),
new Newspaper("newspaper2")
));
添加作者:
newspaperRepo.save(newspapers.get(0).addAuthors(
new Author("author1"),
new Author("author2")
));
获取作者:
authors = authorRepo.findAll();
添加文章:
newspaperRepo.save(newspapers.get(0).addArticles(
new FirstTypeArticle("article1", authors.get(0), authors.get(1)),
new SecondTypeArticle("article2", authors.get(1))
));