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.
Question
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 #Versioned 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.
Related
I'm playing with Quarkus and trying to build a CRUD REST application; I'm trying to get 2 endpoints returning 2 different views of the same entities. Here is an example on how I would have done in Spring + Jackson:
#Entity
public class Car{
public String model;
#ManyToOne( fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
public Owner owner;
// [...]
}
#Entity
public class Owner{
public String name;
// [...]
}
Here it is the important part: now if I were using Jackson I would have create a CarView class:
public class CarView {
public static class Public {};
public static class Private extends Public {};
}
And with that I would have annotated Car.model with #JsonView(CarView.Public.class) and Car.owner with #JsonView(CarView.Private.class) and then just annotate with the same annotations my methods in the REST controller in order to tell Jackson which view I want to use:
#RequestMapping("/car/{id}")
#JsonView(CarView.Public.class)
public Car getPublic(#PathVariable int id) { /*...*/ }
#RequestMapping("/car/private/{id}")
#JsonView(CarView.Private.class)
public Car getPrivate(#PathVariable int id) { /*...*/ }
Can I accomplish the same result using Quarkus & JSON-B?
Quarkus supports usage of JsonViews to manage the serialization/deserialization of request/response.
(Just to let you know, sadly it's not supported (yet) by smallry-openapi implementation, so even if the serialization would work, you'll still see the full model in swagger.)
An example of usage, taken from official guide https://quarkus.io/guides/resteasy-reactive#jsonview-support:
JAX-RS methods can be annotated with #JsonView in order to customize the serialization of the returned POJO, on a per method-basis. This is best explained with an example.
A typical use of #JsonView is to hide certain fields on certain methods. In that vein, let’s define two views:
public class Views {
public static class Public {
}
public static class Private extends Public {
}
}
Let’s assume we have the User POJO on which we want to hide some field during serialization. A simple example of this is:
public class User {
#JsonView(Views.Private.class)
public int id;
#JsonView(Views.Public.class)
public String name;
}
Depending on the JAX-RS method that returns this user, we might want to exclude the id field from serialization - for example you might want an insecure method to not expose this field. The way we can achieve that in RESTEasy Reactive is shown in the following example:
#JsonView(Views.Public.class)
#GET
#Path("/public")
public User userPublic() {
return testUser();
}
#JsonView(Views.Private.class)
#GET
#Path("/private")
public User userPrivate() {
return testUser();
}
When the result the userPublic method is serialized, the id field will not be contained in the response as the Public view does not include it. The result of userPrivate however will include the id as expected when serialized.
Have you checked #JsonbVisibility or "Jsonb adapter" part in
https://javaee.github.io/jsonb-spec/users-guide.html annotation from Jsonb? I am afraid maybe there isn't a solution in Jsonb yet like #JsonView in Jackson. Jsonb adapter is configuration at bean level(you choose the Jsonb instance when you (de)serialize), not at view level.
The problem is that one day we discovered that if we're saving an object in spring boot repository, another objects that are changed in the same method are also updated and persisted in the database.
The curiosity is massive to find out why does this actually happen. I created sample project using Spring Initializr and some template code to show the actual situation (tried to keep the number of dependencies as low as possible).
Using Spring boot version 1.5.11 (SNAPSHOT) and project has following dependencies:
dependencies {
compile('org.springframework.boot:spring-boot-starter-data-jpa')
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.mariadb.jdbc:mariadb-java-client:2.1.0')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
Now to the point:
Project has two entities, Pet:
#Entity
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id", scope = Pet.class)
public class Pet {
#Id
#GeneratedValue
private long id;
private String type;
public Pet() {}
public String getType() { return type; }
public void setType(String type) { this.type = type; }
}
and User:
#Entity
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id", scope = User.class)
public class User {
#Id
#GeneratedValue
private long id;
private String name;
public User() {}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
Both entities also have repositories, Pet:
#Repository
public interface PetRepository extends CrudRepository<Pet, Long> {
Pet findPetById(Long id);
}
User:
#Repository
public interface UserRepository extends CrudRepository<User, Long> {
User findUserById(Long id);
}
And one simple service where the magic actually happens ( I have pre-saved one Pet and one User object, with different name and type)
#Service
public class UserService {
#Autowired
UserRepository userRepository;
#Autowired
PetRepository petRepository;
public User changeUserAndPet() {
User user = userRepository.findUserById(1L);
Pet pet = petRepository.findPetById(1L);
user.setName("Kevin");
pet.setType("Cow");
userRepository.save(user);
return user;
}
}
Right after calling userRepository.save(user); the Pet object is also updated in the database with new type of 'Cow'. Why exactly does this happen if I only saved the User object? Is this intended to be like this?
There's also one simple controller and simple test endpoint to call the service method which most likely is not important to the question, but I'll still add it here for the sake of completeness.
#RestController
public class UserController {
#Autowired
UserService userService;
#RequestMapping(value = "/test", method = RequestMethod.GET)
public User changeUserAndPet() {
return userService.changeUserAndPet();
}
}
Any explanation / tips are appreciated and feel free to ask extra information / code in github.
The Spring Data repository is a wrapper around the JPA EntityManager. When an entity is loaded, you get the instance, but a copy of the object is stored inside the EntityManager. When your transaction commits, the EntityManager iterates all managed entities, and compares them to the version it returned to your code. If you have made any changes to your version, JPA calculates which updates should be performed in the database to reflect your changes.
Unless you know JPA quite well, it can be tricky to predict when calls are propagated to the database, since flush() is called internally. For instance every time you do a query JPA performs a pre-query flush, because any pending inserts must be send to the database, or the query would not find them.
If you defined a transaction using #Transactional on you method, then pet would be updated even if the user was not saved. When you don't have a transaction, the call to save must trigger the EntityManager to propagate your update to the database. It's a bit of a mystery to me why this happens. I Know that Spring creates the EntityManager inside OpenEntityManagerInViewInterceptor before the Controller is called, but since the transaction is not explicit, it must be created implicitly and there could potentially be multiple transactions.
I always encourage developers to use explicit transactions in Spring, and qualify them with readonly when appropriate.
That's how JPA and the EntityManager works. If you lookup an entity through the repository, it is attached to the EntityManager as managed entity. Any changes that you do to that object, are picked up when a flush is executed by the EntityManager. In fact, you wouldn't even need to call the save method on the repository in your case.
You can find more information about the lifecycle of JPA entities e.g. here: https://dzone.com/articles/jpa-entity-lifecycle
I am using spring boot, spring web and spring data for the following example.
I have one entity called Person and I already populated two Persons in the database:
Person entity
#Entity
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(unique = true, nullable = false)
private long id;
private String name;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Personne() {
}
public Personne(long id, String name) {
this.id = id;
this.name = name;
}}
PersonRepository
#Repository
public interface PersonRepository extends JpaRepository<Person, Long> {
}
PersonController
#RestController
public class PersonController {
#Autowired
private PersonRepository personRepo;
#RequestMapping(value = "/perss/{id}")
public Person getById(#PathVariable("id") long id) {
return personRepo.xxxx(id);
}}
Use case 1:
When I replace personRepo.xxxx(id) with personRepo.getOne(id) and tap localhost:8080/perss/1 i get Could not write JSON: No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) error in the browser due to the fact that getOne() method returns a proxy to Person that jackson somehow cannot convert.
Use case 2:
When I replace personRepo.xxxx(id) with personRepo.findOne(id) and tap localhost:8080/perss/1 I get the desired Person object in the correct JSON format (this one works fine).
Use case 3:
When I replace PersonController getById() method's code with the following one:
#RequestMapping(value = "/perss/{id}")
public Person getById(#PathVariable("id") long id) {
Person p1 = personRepo.findOne(id);
Person p2 = personRepo.getOne(id);
return p2;
}
And tap localhost:8080/perss/1 I get the wanted Person object in the correct JSON format.
Question:
Using getOne() got me an error, but using findOne() and getOne() together gave me good result.
How does the findOne() influence the getOne()'s behavior.
EDIT
Use Case 4
When I reverse the order of p1 and p2 i get an error.
#RequestMapping(value = "/perss/{id}")
public Person getById(#PathVariable("id") long id) {
Person p2 = personRepo.getOne(id);
Person p1 = personRepo.findOne(id);
return p2;
}
Try to return p1 and you probably get the same error.
#RequestMapping(value = "/perss/{id}")
public Person getById(#PathVariable("id") long id) {
Person p1 = personRepo.findOne(id);
Person p2 = personRepo.getOne(id);
return p1;
}
You didn't get any, because you didn't serialized p1 which is JavassistLazyInitializer proxy. You serialized p2 instead which was already fine.
This one also will be fine:
#RequestMapping(value = "/check/{id}")
public void getById(#PathVariable("id") long id) {
personRepo.getOne(id);
}
JSON-serialization occurs when the object converted to from POJO to JSON.
The error with serialization of beans that have lazy-init properties occurs because serialization happens before their full loading.
You can try to fix the error with findOne() doing the following options:
Set the property below to your application.properties file (as exception message suggests):
spring.jackson.serialization.fail-on-empty-beans=false
Annotate entity with lazy-init properties like:
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
So, answering the question:
How does the findOne() influence the getOne()'s behavior.
It doesn't. And also calls to repositories doesn't invoke JSON serialization process.
You are correct that the order of invocation does effect the result when using both findOne() and getOne().
Short Answer: Both methods will first lookup the ID in the persistence context and return the cached value if it is present. If there is nothing found in the persistence context, they will proceed to load their preferred result and cache it. The cached value will be found by the other method the next time it runs.
getOne(id) will load (and cache) a proxy if id is not in the persistence context.
findOne(id) will load (and cache) the naked entity if id is not in the persistence context.
Long Answer: I ran into the same problem and my project uses Hibernate 5.2.4.Final. The details of what is happening involves some Hibernate code. After debugging for a while I found that both findOne() and getOne() eventually call Hibernate's DefaultLoadEventListener.onLoad() method, but they call it with different loadType arguments:
getOne() eventually delegates to SessionImpl.IdentifierLoadAccessImpl<T>.doGetReference() which specifies the loadType of LoadEventListener.LOAD which is eventually passed down to DefaultLoadEventListener.proxyOrLoad(). LoadEventListener.LOAD does allow for the creation of a proxy.
findOne() eventually delegates to SessionImpl.IdentifierLoadAccessImpl<T>.doLoad() which specifies the loadType value of LoadEventListener.GET which is eventually passed down to DefaultLoadEventListener.proxyOrLoad(). LoadEventListener.GET does not allow creation of a proxy.
Set a breakpoint in DefaultLoadEventListener.proxyOrLoad() to verify that the LoadType options argument that is passed in has different values for its allowProxyCreation field depending on whether findOne() or getOne() is calling it.
You can see that if allowProxyCreation is true and there is no proxy, then proxyOrLoad() will return the result of createProxyIfNecessary(). In the case where only getOne() is used, this will result in returning a proxy.
If it happens that findOne() was called for the same entity type and ID before getOne(), then when the getOne() call makes its way into createProxyIfNecessary() it will return early because the entity will already be found in the persistence context. In that case calling getOne() will not result in creating a proxy.
If you call getOne() before findOne() then the proxy will be created and stored in the persistence context, and findOne() will also return the proxy because it will simply retrieve the cached proxy from the persistence context.
I have a very weird problem with Jersey REST services. I'm using:
Glassfish
sh4.0
EJB3.1
JDK1.7
Netbeans8.0
When I persist an object by GET request it normally store in database:
#Singleton
#Path("/person")
public class SampleRest {
#EJB
PersonFasade personFasade;
#GET
public Person getPerson1(
final #QueryParam("id") String id,
final #QueryParam("first") String first,
final #QueryParam("last") String last)
{
final Person person = this.personFasade.create(id, first, last);
return person;
}
}
But when I'm using POST:
...
#POST
public Person getPerson3(Person person) {
this.personFasade.create(person);
return person;
}
...
Everything works but Object does not store in database (without any error)!!
I'm pretty sure that is the parameter, you should use the #Consumes annotation to pass a Person object as JSON/XML parameter or use the same #QueryParam annotations to describe the object to persist.
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Person getPerson3(Person person) {
this.personFasade.create(person);
return person;
}
I found the problem! It was because I forgot to put beans.xml into WEB-INF folder!!! I don't know how it is possible to such things happen by this mistake!! After I did it works. I switch to Wildfly, seems it's more stable, with very good useful logs and error message.
I have been looking into this issue for hours now, probably simple but I don't get it anymore:
I have an entity (Param) which is rendered to json via jax-rs. The entity references another entity (Step).
When writing / reading json, I dont want to see the whole step-entity but merely its id, so I use this code :
#Entity
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Param implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
long id;
#Column(name = "KEYNAME")
String key;
String value;
#XmlIDREF
Step step;
}
Works perfectly for marshalling.
So any GET-request shows me something the following:
{id: 1,
key: "a",
value: "b",
step: 53
}
But when I post some param to the server, it cant map back the numeric id to a step-entity.
I need to provide the unmarshaller with a custom IDResolver. But how can I configure the unmarshaller???? The Jax-RS servlet is doing the marshalling for me. My code looks like that:
#Path("param")
public class ParamRepresentation {
/**
* Retrieves representation of an instance of ParamRepresentation
* #return an instance of Param
*/
#GET
#Produces("application/json")
#Path("{ID}")
public Param getJson(#PathParam("ID") long id) {
return (Param) ctr.find(id, Param.class);
}
#PUT
#Path("{ID}")
#Consumes("application/json")
#Produces("application/json")
public SuccessMessage updateStep(#PathParam("ID") long id, Param p) {
ctr.update(p);
ParamSuccessMessage sm = new ParamSuccessMessage();
sm.setSuccess(true);
sm.setParam(p);
return sm;
}
}
so how can i configure the unmarshaller ?????
I think you've misunderstood the purpose of IDREF in XML schemas. It's there to allow you to refer to another element that is marked as an ID (i.e., with an #XmlID annotation in JAXB) in the same document. You can't use it to refer to an ID elsewhere in the world; for that you'd use a URI (possibly with a fragment identifier part). To do those in JAXB, you use:
#XmlElement // You might be able to omit this part; it's here for clarity
#XmlSchemaType(name = "anyURI")
public URI exampleField;
You then need to work out whether the URI refers to something you know (i.e., resolve the URI and see if it points into yourself) and deal with the fragment identifier. Or do the more common trick of just using a string and don't worry about trying to magically hook everything up in the binding layer. (That works rather well in practice.)
I've done a similar thing using Jersey and xml representations. I used an xml adapter to symmetrically map between the complete child element and the partial (just id) element.
I would annotate the Step entity in your Param entity as follows:
//javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter
#XmlJavaTypeAdapter(PartialStepEntityAdapter.class)
Step step
You would then need to define both the partial Step entity and the Adapter. The PartialStep would be identical to your original step class, but with just the id field.
The PartialStepEntityAdapter would map a Step to a PartialStep when marshalling and a PartialStep to a Step when unmarshalling:
//javax.xml.bind.annotation.adapters.XmlAdapter
public class PartialStepEntityAdapter extends XmlAdapter<PartialStep, Step> {
#Override
public Step unmarshal(PartialStep partialStep) throws Exception {
Step step = new Step();
step.setId(partialStep.getId());
return step;
}
#Override
public PartialStep marshal(Step step) throws Exception {
PartialStep partialStep= new PartialStep();
partialStep.setId(step.getId());
return partialStep;
}
}
Hope that's some help.