3

All,

We are using Hibernate 4.2.14.Final, Hibernate JPA API 2.0, and Spring Data JPA 1.7.0.RELEASE. Our transactions are managed via JTA Container-managed transactions.

I am seeing an issue where the version counter of a child entity is coming back incorrectly after a save and flush if there were no changes on the child entity. However, calling refresh on the entity "fixes" it.

To illustrate with a simple example, assume we have a DepartmentEntity and EmployeeEntity and a one-to-many relationship between Department and Employee.

We also have DTOs that mirror the relationship and are used since we did not want to expose out JPA-annotated Entities in our service API.

public class EmployeeEntity {
    ...
    @ManyToOne(cascade = {CascadeType.MERGE, CascadeType.REFRESH, CascadeType.PERSIST}, 
               optional = false)
    @JoinColumn(name = "DEPARTMENT_ID", nullable = false)
    private DepartmentEntity department;
    ...
}

I have a service method in a Spring-managed Bean:

@Transactional(readOnly = false, propagation = Propagation.REQUIRED)
public <EmployeeDTO> update(EmployeeDTO employee) {

    // Code here to retrieve the EmployeeEntity by Unique ID and copy props
    // from EmployeeDTO to EmployeeEntity, and also DepartmentDTO to 
    // DepartmentEntity.
    ... 

    // EmployeeRepository is a Spring Data JPA Repository 
    employeeEntity = employeeRepository.saveAndFlush(employeeEntity);

    // Convert EmployeeEntity back to EmployeeDTO and return it
    return newEmployeeDTOinstance;
}

If we start with: Employee v1, Department v1, and we only changed Department, what happens is after the saveAndFlush (checking via debugger), I am seeing Employee v2 and Department v2.

However, when the whole transaction is done and I check the database rows via PL/SQL Developer, Employee remained at Version 1, and Department is at Version 2.

The next call to update I pass in Employee v2 and Department v2 (since that's what JPA/Hibernate told me is the latest version). However, the database has Employee version 1, and I blow up with an Optimistic Lock Exception.

I am able to "make it work" by putting call to refresh after the saveAndFlush, as follows ...

// EmployeeRepository is a Spring Data JPA Repository 
employeeEntity = employeeRepository.saveAndFlush(employeeEntity);

// Refresh after flush in order to ensure version numbers are correct on Entities.
employeeRepository.refresh(employeeEntity);

In the scenario where Employee did not change but Department did, after the saveAndFlush, I see Employee v2, and then the call to refresh sets Employee back to v1.

My question is why? Is JPA/Hibernate assuming that by calling saveAndFlush on an Entity, that the version counter should be incremented, but then at the end of the transaction seeing that there are no changes and thus not actually changing the row in the database?

Has anyone ever come across this before? Any insight or where I could look?

I did read this Stackoverflow answer that alludes to a refresh after flush: https://stackoverflow.com/a/8137376/463196

However, it did not address version counters, and seemed to be more about refreshing changes made via database triggers which are not relevant since we are letting Hibernate manage the version counter.

Thank you very much in advance!

Community
  • 1
  • 1
Philip Tenn
  • 6,003
  • 8
  • 48
  • 85

0 Answers0