Reputation: 898
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
Reputation: 8203
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