Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid duplicates with JPA cascades?

Tags:

java

jpa

I have a Parent entity with a Child entity in a ManyToOne relationship:

@Entity class Parent {
  // ...
  @ManyToOne((cascade = {CascadeType.ALL})
  private Child child;
  // ...
}

The Child has an unique field:

@Entity class Child {
  // ...
  @Column(unique = true)
  private String name;
  // ...
}

When I need a new Child, I ask the ChildDAO first:

Child child = childDao.findByName(name);
if(child == null) {
  child = new Child(name);
}

Parent parent = new Parent();
parent.setChild(child);

The problem is, if I do like above twice (with the same name for the Child), and only persist the Parent at the end, I get a constraint exception. Which seems normal, since initially there was no child in the database with the specified name.

The problem is, I'm not sure what would be the best way to avoid this situation.

like image 684
Cos64 Avatar asked Nov 27 '11 19:11

Cos64


People also ask

Why does JPA return duplicate rows?

Issue with @Id column, If we check closely, @Id column value is same for all the rows. Hence hibernate/JPA not able to get different records, it just get 1st record with this @Id and return duplicate records of it. Solution - Use @IdClass with columns which result in unique row instead of duplicate row.

What is Spring Data JPA?

Spring Data JPA aims to significantly improve the implementation of data access layers by reducing the effort to the amount that's actually needed. As a developer you write your repository interfaces, including custom finder methods, and Spring will provide the implementation automatically.

How do I stop inserting duplicate records in MySQL?

Note − Use the INSERT IGNORE command rather than the INSERT command. If a record doesn't duplicate an existing record, then MySQL inserts it as usual. If the record is a duplicate, then the IGNORE keyword tells MySQL to discard it silently without generating an error.


1 Answers

You are creating two non-persistent instances of Child with new Child() twice then putting these in two different Parents. When you persist the Parent objects, each of the two new Child instances will be persisted/inserted via cascade, each with a different @Id. The unique constraint on the name then breaks. If you're doing CascadeType.ALL, then every time you do new Child() you may be getting a separate persistent object.

If you really wanted the two Child instances to be treated as a single persistent object with the same ID, you would need to persist it separately to associate with the persistence context/session. Subsequent calls to childDao.findByName would then flush the insert and return the new Child you just created, so you won't be doing new Child() twice.

like image 154
wrschneider Avatar answered Oct 14 '22 22:10

wrschneider