JPA 与同一实体的两个单向 @OneToMany 关系导致重复条目

Posted

技术标签:

【中文标题】JPA 与同一实体的两个单向 @OneToMany 关系导致重复条目【英文标题】:JPA two unidirectional @OneToMany relationships to the same entity resulting in duplicate entry 【发布时间】:2018-08-07 08:31:27 【问题描述】:

我目前正在从事一个学校项目,我们必须创建自己的“Twitter”应用程序,但我在域对象的持久性方面遇到了一些问题。

My Account 类(为便于阅读而简化):

@Entity
public class Account implements Serializable 

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
@Column(unique = true)
private String email;

@OneToMany
private final List<Account> following = new ArrayList<>();
@OneToMany(mappedBy = "tweetedBy", cascade = ALL)
private final List<Tweet> tweets = new ArrayList<>();

我的 Tweet 类(为便于阅读而简化):

@Entity
public class Tweet implements Serializable 

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
private String content;

@ManyToOne
private Account tweetedBy;

@OneToMany(cascade = CascadeType.PERSIST)
@JoinTable(name = "tweet_likes")
private final List<Account> likedBy = new ArrayList<>();

@OneToMany(cascade = CascadeType.PERSIST)
@JoinTable(name = "tweet_mentions")
private final List<Account> mentions = new ArrayList<>();

持久化代码(简化):

Account a1 = new Account("user1@gmail.com", "password1");
Account a2 = new Account("user2@gmail.com", "password2");
Account a3 = new Account("user3@gmail.com", "password3");

a1.addTweet("Sup mah dudes.");
a1.addTweet("yoyo");
a2.addTweet("Allo Allo #tweeting");
a2.addTweet("#testing yoyo");
a1.getTweets().get(0).addLike(a3);
a1.addFollowing(a3);

em.persist(a1);
em.persist(a2);
em.persist(a3);

我遇到的问题是 likedBymentions 没有正确持久化。正在生成链接器表并插入数据,但我在插入用户时不断收到重复条目错误。我相信我正确地建模了这种关系(单向 OneToMany),因为我不希望帐户跟踪其中提到的推文。

我尝试过的:

@JoinColumn 用于 likesmentions(导致重复插入) @JoinTable 用于 likesmentions(导致重复插入) 对于 likesmentions 仅适用于 @OneToMany(这不会导致错误,但会为这两个关系创建 1 个链接器表,其中任何一个都不能为空) @OneToMany for likes 然后@joinColumn for mentions where nullable = true (这会导致除非您喜欢,否则无法在推文中提及您,这是奇怪的行为) @OneToMany(cascade = CascadeType.MERGE)(导致重复插入)

Netbeans 输出重复插入错误:

Warning:   Local Exception Stack: 
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.4.qualifier): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry 'user6@gmail.com' for key 'EMAIL'
Error Code: 1062
Call: INSERT INTO ACCOUNT (AVATARPATH, BIO, EMAIL, ENCRYPTEDPASSWORD, LOCATION, USERNAME, USERROLE, WEBSITE) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
bind => [8 parameters bound]
Query: InsertObjectQuery(domain.Account@3c7f9d54)

我相信这个错误的发生是因为我的 JPA 实施流程如下:

    帐户已保留 推文保持不变(因为它在帐户内) 帐户保持不变(因为它在推文中)

我的期望:

1 个链接器表,其中一个 tweet_id (fk) 和一个 account_id (fk) 代表喜欢 1 个链接器表,其中一个 tweet_id (fk) 和一个 account_id (fk) 代表提及

如果有人可以帮助我进行注释或解释我做错了什么,将不胜感激。

如有任何帮助,请提前联系。

【问题讨论】:

【参考方案1】:

我解决了这个问题,原来是likesmentions 的注解。 @JoinTable(name = "") 已修复。我认为在重新部署之前不手动删除表导致我一开始没有将此视为解决方案。

对于有类似问题的任何人,这是我的解决方案:

@OneToMany(cascade = CascadeType.PERSIST)
@JoinTable(name = "tweet_likes") //<--CREATES SEPERATE TABLE
private final List<Account> likedBy = new ArrayList<>();
@OneToMany(cascade = CascadeType.PERSIST)
@JoinTable(name = "tweet_mentions") //<--CREATES SEPERATE TABLE
private final List<Account> mentions = new ArrayList<>();

另外,如果我重新启动整个项目(重新启动整个 glassfish/payara 服务器),我会收到重复条目错误,因为 EntityManager 会在删除和重新创建表之前尝试持久保存到数据库。因此,如果我只是在保持 payara 服务器在线的同时取消部署并重新部署我的项目,它仍然可以正常运行。我还没有找到解决这个问题的方法,但那是另一个问题。

【讨论】:

这对我不起作用。我粘贴了我的代码。看起来和你的很相似。

以上是关于JPA 与同一实体的两个单向 @OneToMany 关系导致重复条目的主要内容,如果未能解决你的问题,请参考以下文章

onetomany 单向与使用 jpa 的可连接设置

如何在 JPA 中定义单向 OneToMany 关系

JPA 单向一对多关联关系

弹簧数据 JPA。子实体的分页

为啥在看到 PersistentBag 的实体中使用 JPA 列出(@OneToMany)

单向 OneToMany JPA 仅向我显示第一个相关对象“n”次