Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JPA two unidirectional @OneToMany relationships to the same entity resulting in duplicate entry

I'm currently working on a school project where we have to create our own 'Twitter' application and I'm having some trouble with the persistence of the domain objects.

My Account class (simplified for readability):

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

My Tweet class (simplified for readability):

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

The persist code (simplified):

Account a1 = new Account("[email protected]", "password1");
Account a2 = new Account("[email protected]", "password2");
Account a3 = new Account("[email protected]", "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);

The problem I'm having is that the likedBy and mentions aren't being persisted correctly. The linker tables are being generated and the data gets inserted but I keep getting a duplicate entry error on the insert of a user. I believe I modeled the relationship correctly (unidirectional OneToMany), because I don't want an Account to keep track of Tweets it was mentioned in.

What I have tried:

  • @JoinColumn for both likes and mentions (results in duplicate insert)
  • @JoinTable for both likes and mentions (results in duplicate insert)
  • Only @OneToMany for both likes and mentions (this does not result in an error but creates 1 linker table for both relationships where either cannot be null)
  • @OneToMany for likes and then @joinColumn for mentions where nullable = true (this results in the scenario where u cannot be mentioned in a tweet unless you like it, which is odd behaviour)
  • @OneToMany(cascade = CascadeType.MERGE) (results in duplicate insert)

Netbeans output of duplicate insert error:

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 '[email protected]' 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)

I believe this error occurs because of the folowing flow of my JPA imlementation:

  1. Account persisted
  2. Tweet persisted (because it is inside Account)
  3. Account persisted (because it is inside Tweet) <-- duplicate entry

What I expect:

  • 1 linker table with a tweet_id (fk) and an account_id (fk) representing likes
  • 1 linker table with a tweet_id (fk) and an account_id (fk) representing mentions

If someone could help me with the annotations or explain what else I'm doing wrong that would be very much appreciated.

Ty in advance for any help.

like image 534
Teun van der Wijst Avatar asked Feb 27 '18 16:02

Teun van der Wijst


People also ask

What is the default mechanism for representing one-to-many unidirectional relationships in JPA?

As straightforward as it might be in a relational database, when it comes to JPA, the one-to-many database association can be represented either through a @ManyToOne or a @OneToMany association since the OOP association can be either unidirectional or bidirectional.

How do you persist many to many relationships in JPA?

In JPA we use the @ManyToMany annotation to model many-to-many relationships. This type of relationship can be unidirectional or bidirectional: In a unidirectional relationship only one entity in the relationship points the other. In a bidirectional relationship both entities point to each other.

Is OneToMany unidirectional?

– @OneToMany annotation is for One-to-Many Unidirectional mapping. We use it together with @JoinColumn annotation that will create a column in child table storing the parent entity id ( tutorial_id ).

What is the difference between MappedBy and @JoinColumn?

The @JoinColumn annotation helps us specify the column we'll use for joining an entity association or element collection. On the other hand, the mappedBy attribute is used to define the referencing side (non-owning side) of the relationship.


1 Answers

I solved the problem, it turns out it was the annotations at the likes and mentions. @JoinTable(name = "") fixed it. I think not dropping the tables manually before redeploying caused that I did not see this as the solution at first.

for anyone with a similar problem, this is my solution:

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

Also, apearently if I restart my entire project (restart the entire glassfish/payara server) I get the duplicate entry error because the EntityManager tries to persist to the database before the tables have been dropped and recreated. So if I just undeploy and redeploy my project while keeping the payara server online, it persists just fine. I have not yet been able to find a solution to this problem but thats for another question.

like image 174
Teun van der Wijst Avatar answered Sep 28 '22 13:09

Teun van der Wijst