I am experimenting with the library OmniPersistence.
I have a problem using the class org.omnifaces.persistence.model.VersionedEntity
. In my code there is a simple entity class City.
@Entity
public class City extends VersionedEntity<Long> {
@Id
@GeneratedValue
private Long id;
private String postalCode;
private String name;
... (getter + setter)
}
There is a REST-Service that exposes the Entity for some client-applications. But every time I want to update an object a javax.persistence.OptimisticLockException
is thrown. The problem is that the version attribute is always null
. A look in the code of VersionedEntity
revealed that there is no setter method, but a comment
// No setter! JPA takes care of this.
I do understand the intention of the absence of the setter method but that is the reason for the exception.
Is my architecture so poor (exposing the entity class in a web-service) or is it maybe reasonable to add a setter method although JPA should handle the value/manipulation of the @Version
ed attribute?
Edit (as requested by the comment)
My update method is basically the one in OmniPersistence' BaseEntityService
. My service class looks like the following.
@Stateless
public class CityService extends BaseEntityService<Long, City> {
public Long count() {
return super.createLongQuery("select count(c) from City c").getSingleResult();
}
}
My controller is the REST endpoint.
@Path("city")
public class CityEndpoint {
@Inject
private CityService cityService;
@GET @Produces(MediaType.APPLICATION_JSON)
public Response getAll() {
List<City> cities = cityService.list();
return Response.ok(cities).build();
}
@GET @Produces(MediaType.APPLICATION_JSON)
@Path("{id}")
public Response get(@PathParam("id") Long id) {
return Response.ok(cityService.getById(id)).build();
}
@POST @Consumes(MediaType.APPLICATION_JSON)
public Response create(City city) {
cityService.persist(city);
return Response.created(URI.create(String.format("city/%s", Objects.toString(city.getId())))).build();
}
@POST @Consumes(MediaType.APPLICATION_JSON)
@Path("update")
public Response update(City city) {
System.out.println(city);
City updated = cityService.update(city);
return Response.ok(updated).build();
}
@GET
@Path("count")
public Response count() {
return Response.ok(cityService.count()).build();
}
}
The JPA specification document provides an important hint that you must not manipulate the @Version
attribute, see section 3.4.2, on page 90
An entity may access the state of its version field or property or export a method for use by the application to access the version, but must not modify the version value.
and
The version attribute is updated by the persistence provider runtime when the object is written to the database.
So the comment (”No setter! JPA takes care of this.“) you find in VersionedEntity
is absolutely reasonable. In essence, you should not change (or null
) the @Version
attribute from higher application levels.
In your case, it seems, you must compensate the ”lost“ (=nulled) version effect, eg by introducing a DTO for City
. Otherwise, you will always run into an OptimisticLockException
.