Reputation: 366
I have the following entities:
@Entity
@Data
@Table(name = "Parents")
public class Parent {
@Id
@GeneratedValue
Long id;
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "child")
private Child child;
}
And
@Entity
@Data
@Table(name = "Childs")
public class Child {
@Id
String number;
}
For both entities I have a standard CrudRepository. At startup of the app I try to populate the database with this code:
@Component
public class TestFiller implements ApplicationListener<ContextRefreshedEvent> {
@Autowired
ParentRepository parentRepository;
@Autowired
ChildRepository childRepository;
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
for (int i = 0; i < 100; i++) {
Parent parent = new Parent();
String childId = "CHILD" + Math.round(Math.random() * 10);
Optional<Child> childOpt = childRepository.findById(childId);
if (childOpt.isPresent()) {
parent.setChild(childOpt.get());
} else {
Child child = new Child();
child.setNumber(childId);
parent.setChild(child);
}
parentRepository.save(parent);
}
}
}
This is the ChildRepository:
@Repository
public interface ChildRepository extends JpaRepository<Child, String> {
}
But for some inexplicable reason this fails when a child is already present in the database:
Caused by: org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "childs_pkey"
Detail: Key (number)=(CHILD6) already exists.
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2182) ~[postgresql-9.4-1206-jdbc42.jar:9.4]
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1911) ~[postgresql-9.4-1206-jdbc42.jar:9.4]
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:173) ~[postgresql-9.4-1206-jdbc42.jar:9.4]
at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:645) ~[postgresql-9.4-1206-jdbc42.jar:9.4]
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:495) ~[postgresql-9.4-1206-jdbc42.jar:9.4]
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeUpdate(AbstractJdbc2Statement.java:441) ~[postgresql-9.4-1206-jdbc42.jar:9.4]
It almost looks like the entity returned by the findById method is not managed. Why is Hibernate/JPA trying to persist the existing entity?
Edit: as per request I added the ChildRepository
Upvotes: 2
Views: 3153
Reputation: 366
Ok, to answer my own question:
it is caused by the CascadeType @ManyToOne(cascade = CascadeType.ALL)
on the Child
property of the Parent
. Removing that fixes it.
Apparently JPA cascades the PERSIST
operation to the Child as well, even though it could have easily known that the Child is already a managed and persisted entity.
Upvotes: 2
Reputation: 129
As per the java documentation
This is a value-based class; use of identity-sensitive operations (including reference equality (==), identity hash code, or synchronization) on instances of Optional may have unpredictable results and should be avoided.
so first try to get the object from the result and check if it is the expected value :-
if (childOpt.isPresent()) {
Child checkObj=childOpt.get():
parent.setChild(checkObj);
}
Upvotes: 0
Reputation: 86
I think this is because of your String ID. Can you please try with findOne() method it may help you.
String childId = "CHILD" + Math.round(Math.random() * 10);
Child child = new Child();
child.setNumber(childId);
Child childOpt = childRepository.findOne(child);
Upvotes: 0