Reputation: 83
i'm using JPA repository to save simple data objects to the database. To avoid duplicates i created a unique constraint on multiple fields. If now a duplicate according to the unique fields/constraint should be saved i want to catch the exception, log the object and the application should proceed and saves the next object. But here i always get this exception: "org.hibernate.AssertionFailure: null id in de.test.PeopleDBO entry (don't flush the Session after an exception occurs)".
In general i understand what hibernate is doing, but how i can revert the session or start a new session to proceed with saving of the next data objects. Please have a look to the code below:
PeopleDBO.java
@Entity
@Data
@Table(
name = "PEOPLE",
uniqueConstraints = {@UniqueConstraint(columnNames = {"firstname", "lastname"}})
public class PeopleDBO {
public PeopleDBO(String firstname, String lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstname;
private String lastname;
}
The Test:
public void should_save_people_and_ignore_constraint_violation(){
final List<PeopleDBO> peopleList = Arrays.asList(
new PeopleDBO("Georg","Smith"),
new PeopleDBO("Georg","Smith"),
new PeopleDBO("Paul","Smith")
);
peopleList.forEach(p -> {
try {
peopleRepository.save(p);
} catch (DataIntegrityViolationException e) {
log.error("Could not save due to constraint violation: {}",p);
}
}
Assertions.assertThat(peopleRepository.count()).isEqualTo(2);
}
The problem is, that with saving of the second people the unique constraint gets violated. The error log happens, and with the next call of peopleRepository.save() the mentioned exception above is thrown:
"org.hibernate.AssertionFailure: null id in de.test.PeopleDBO entry (don't flush the Session after an exception occurs)"
How i can avoid this behaviour? How i can clean the session or start a new session?
Thanks a lot in advance d.
--------- Edit / new idea ------ I just tried some things and have seen that i could implement a PeopleRepositoryImpl, like this:
@Service
public class PeopleRepositoryImpl {
final private PeopleRepository peopleRepository;
public PeopleRepositoryImpl(PeopleRepository peopleRepository) {
this.peopleRepository = peopleRepository;
}
@Transactional
public PeopleDBO save(PeopleDBO people){
return peopleRepository.save(people);
}
}
This is working pretty fine in my tests. ... what do you think?
Upvotes: 3
Views: 3385
Reputation: 5394
The reason is that all inserts occur in one transaction. As this transaction is atomic, it either succeeds entirely or fails, there is nothing in-between.
The most clean solution is to check if a People exists before trying to insert it:
public interface PeopleRespository {
boolean existsByLastnameAndFirstname(String lastname, String firstname);
}
and then:
if (!peopleRepository.existsByLastnameAndFirstname(p.getLastname, p.getFirstname)) {
peopleRepository.save(p);
}
An alternative is indeed to start a new transaction for each person. But I am not sure it will be more efficient, because there is an extra cost to create transaction.
Upvotes: 2