【问题标题】:JPA create @ManyToMany table name not by 'type_type' but by 'type_varname'JPA 不是通过“type_type”而是通过“type_varname”创建 @ManyToMany 表名
【发布时间】:2019-05-21 06:35:28
【问题描述】:

假设我们有两个(或更多)类,其中一个是@ManyToMany——引用其他类: (为了简化,这里省略了很多注释)

@Entity
class Newspaper {
    @Id long                    id;
    @ManyToMany Set<Author>     authors     = new HashSet<>();
    @ManyToMany Set<Article>    oldArticles = new HashSet<>();
    @ManyToMany Set<Article>    newArticles = new HashSet<>();
}

@Entity
class Article {
    @Id long id;
}

@Entity
class Author {
    @Id long id;
}

现在默认情况下,JPA 将创建 两个 表:

Newspaper_Author
Newspaper_Article

甚至将oldArticlesnewArticles 混合到同一个表中,产生有趣的结果;-)

现在可以通过在至少一个或所有成员变量上定义@JoinTable 轻松解决此问题:

    @Entity
class Newspaper {
    @Id long                                                            id;
    @ManyToMany Set<Author>                                             authors     = new HashSet<>();
    @ManyToMany Set<Article>                                            oldArticles = new HashSet<>();
    @ManyToMany @JoinTable(name = "Newspaper_newArticles") Set<Article> newArticles = new HashSet<>();
}

所以,终于要回答我的问题了: 当只有这样定义的类时

@Entity
class Newspaper {
    @Id long                    id;
    @ManyToMany Set<Author>     authors     = new HashSet<>();
    @ManyToMany Set<Article>    oldArticles = new HashSet<>();
    @ManyToMany Set<Article>    newArticles = new HashSet<>();
}

有什么方法可以让 JPA 自动创建表格

Newspaper_authors
Newspaper_oldArticles
Newspaper_newArticles

更新: ...让问题变得非常疯狂:

@Entity
class Newspaper {
    @Id long                    id;
    @ManyToMany Set<Author>     authors     = new HashSet<>();
    @ManyToMany Set<OldArticle> oldArticles = new HashSet<>();
    @ManyToMany Set<NewArticle> newArticles = new HashSet<>();
}

@MappedSuperclass
class Article {
    @Id long                id;
    @ManyToMany Set<Author> authors = new HashSet<>();
}

@Entity
class OldArticle extends Article { /* */ }

@Entity
class NewArticle extends Article { /* */ }

@Entity
class Author {
    @Id long id;
}

我怎么可能在这里定义Article.authors 的名称?

【问题讨论】:

  • 如果你想覆盖 JPA 规范中指定的默认表名,那么你直接设置它......为关系的所有实例。这就是所有要说的
  • 你到底在说什么? Now by default, JPA would create two tables:。不,它不会。我怎么可能定义Article.authors here? 的名称——你所做的有什么问题?您需要包含预期结果。
  • 好的,请告诉我...然后它会创建什么表?而且我并没有抱怨我的帖子有问题,我只是想知道是否有一种自动命名表格的方法,而无需我定义名称。

标签: hibernate jpa jakarta-ee eclipselink


【解决方案1】:

在您的情况下,我建议将@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时必须使用的参数ma​​ppedBy

具体文章:

@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。

但在这种情况下,您将不得不保存独立的实体,例如 AuthorArticleNewspaper (参见 cascade = CascadeType.MERGE 参数)。至于我,这很不方便(我试图用实用方法 addAuthorsaddArticles 来中和它):

@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))
));

【讨论】:

  • 这是一个很棒的答案。真的很详细很好解释。因此,我给了你赏金点。但是,答案并没有回答我的问题,而是完全相反。我个人不喜欢使用@Inheritance,因为我讨厌在一个表中有多个“类”。我只会在非常罕见的情况下使用它,即递归依赖,当它真的会加速查询时。顺便说一句,我也尝试避免使用mappedBy 功能,因为我既不需要双向寻址,也不想以这种方式解决基本的寻址问题(自动命名表)
  • 抱歉...仍然无法测试您的输入。很快就会检查它!
  • 好的。测试了它。结果(如预期):Tables_in_test_PT_local AUTHOR FIRSTTYPEARTICLE FIRSTTYPEARTICLE_AUTHOR NEWSPAPER NEWSPAPER_AUTHOR NEWSPAPER_FIRSTTYPEARTICLE NEWSPAPER_SECONDTYPEARTICLE SECONDTYPEARTICLE SECONDTYPEARTICLE_AUTHOR。所以不是你声称的那样。我在 MySQL 上使用 Payara/EclipseLink。在您的回答中,我没有看到任何表明表格命名的内容。 (除了mappedBy 标签我不能用于UNIDIRECTIONAL 关系)所以我的问题仍然存在:JPA 创建像type_type 这样的名称但我需要type_varname。尤其是与 MappedSuperClass 一起
猜你喜欢
  • 1970-01-01
  • 2020-10-24
  • 1970-01-01
  • 2014-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-14
  • 1970-01-01
相关资源
最近更新 更多