CeeTee
CeeTee

Reputation: 898

Spring Boot Data JPA @Version Returns 0 on Create but is 1 in the database

I have a project using spring-boot-starter-data-jpa version 2.2.6.RELEASE with an aOracle 12c database. I have a version field in my entity annotated with javax.persistance @Version annotation. When I save an entity for the first time, the query returns a version of 0, but in the database I can see it has been set to 1. It is like the transaction ends before the updated version is returned.

I've tried with both CrudRepository's save() method and JpaRepository's saveAndFlush() method, but neither work. Note, subsequent updates to the entity do return the correct version. This problem only happens when creating a new record.

When I use the EntityManager directly with saveAndRefresh(), it works as it should, but I'd like to avoid doing that if possible. Can someone please help?

UPDATE

Here are the entity, repository and service classes. I've tried with JpaRepository and saveAndFlush() too, but the outcome is the same:

@Getter
@Setter
@NoArgsConstructor
@ToString
@EqualsAndHashCode
@Entity
@Table(name = "TD_INCIDENTS")
public class Incident {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "INCIDENT_ID", nullable = false)
    private Long incidentId;

    @Column(name = "INCIDENT_STATUS")
    private Integer statusCode;

    @Version
    @Column(name = "INCIDENT_VER_NUM")
    private Long version;
}

@Repository
public interface IncidentRepository extends CrudRepository<Incident, Long> {
}

@Service
public class IncidentServiceImpl implements IncidentService {
    
    private final IncidentRepository incidentRepository;

    public IncidentServiceImpl(IncidentRepository incidentRepository) 
    {
        this.caseRepository = caseRepository;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Incident createIncident(String statusCode) {
        var newIncident = new Incident();
        newIncident.setStatusCode(1);
        // the line below returns an incident with version 0, but in db it is 1
        return incidentRepository.save(newIncident);
    }

    @Override
    @Transactional
    public Incident getIncident(Long incidentId) {
        return incidentRepository.findById(incidentId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Incident updateIncident(Long incidentId, Integer statusCode, Long version) {
        var incident = this.getIncident(incidentId);
        if (incident != null) {
            if (incident.getVersion().equals(version)) {
                incident.setStatusCode(statusCode);
                // the line below returns an incident with an updated version the same as in the db
                return incidentRepository.save(incident);
            }else {
                throw new OptimisticLockException("Expected versions do not match");
            }
        }
        return null;
    }
}

UPDATE

Here is the log output from the insert:

2020-07-26 13:54:09.675 DEBUG 9140 --- [nio-8080-exec-1] org.hibernate.SQL                        : 
    insert 
    into
        td_incidents
        (incident_id, incident_status, incident_ver_num) 
    values
        (default, ?, ?)
2020-07-26 13:54:09.701 TRACE 9140 --- [nio-8080-exec-1] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [INTEGER] - [1]
2020-07-26 13:54:09.703 TRACE 9140 --- [nio-8080-exec-1] o.h.type.descriptor.sql.BasicBinder      : binding parameter [2] as [BIGINT] - [0]

And here is the log output from the update. Note, I'm sending the version number as 1 in the request, as that's what it is in the db. If I send it as 0, the application will throw the OptimisticLockException

2020-07-26 13:56:29.346 DEBUG 9140 --- [nio-8080-exec-3] org.hibernate.SQL                        : 
    select
        incident0_.incident_id as incident_id1_0_0_,
        incident0_.incident_status as incident_status_2_0_0_,
        incident0_.incident_ver_num as incident_ver_num_3_0_0_ 
    from
        td_incidents incident0_ 
    where
        incident0_.incident_id_id=?
2020-07-26 13:56:29.347 TRACE 9140 --- [nio-8080-exec-3] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [BIGINT] - [1044]
2020-07-26 13:56:29.401 DEBUG 9140 --- [nio-8080-exec-3] org.hibernate.SQL                        : 
    update
        td_incidents 
    set
        incident_status=?,
        incident_ver_num=? 
    where
        incident_id=? 
        and incident_ver_num=?
2020-07-26 13:56:29.402 TRACE 9140 --- [nio-8080-exec-3] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [INTEGER] - [2]
2020-07-26 13:56:29.402 TRACE 9140 --- [nio-8080-exec-3] o.h.type.descriptor.sql.BasicBinder      : binding parameter [2] as [BIGINT] - [2]
2020-07-26 13:56:29.403 TRACE 9140 --- [nio-8080-exec-3] o.h.type.descriptor.sql.BasicBinder      : binding parameter [3] as [BIGINT] - [1044]
2020-07-26 13:56:29.404 TRACE 9140 --- [nio-8080-exec-3] o.h.type.descriptor.sql.BasicBinder      : binding parameter [4] as [BIGINT] - [1]

Upvotes: 1

Views: 3829

Answers (1)

  • INCIDENT_VER_NUM field should not have autoincrement column but you seems to have an autoincrecment column as it is managed and incremented by JPA

  • If you can't remove autocrement, try this option and it could work. But I have used this option so far other fields that are generated by database not for @Version

    @Version
    @Column(name = "INCIDENT_VER_NUM", insertable = false, updatable = false)
    @org.hibernate.annotations.Generated(value = GenerationTime.ALWAYS)
    private Long version;

Upvotes: 1

Related Questions