As says on To initialize or not initialize JPA relationship mappings? is good practice to initialize the relations on JPA mapping.
But into web architecture (Spring MVC), where the entities will be send as JSON, usually with a RESTfull API, the relations will be omitted on the serialization and it will be fetched to the server using its path (i.e employees/[id]/projects).
If we have:
class Employee {
#ManyToMany //Employee owner of relation
protected List<Project> projects=new ArrayList<Project>(0);
}
If from the client we want to update only the Employee name, the client send the JSON to the server (HTTP PUT), it is deserialized and employee.projects will be initialized as empty collection. When the EntityManager save, it will update the properties (right) and the projects (Employee is owner side) DELETING the existing projects.
A alternative is use the DIY merge pattern. "Instead of invoking EntityManager.merge we invoke EntityManager.find to find the existing entity and copy over the state ourselves". This workaround requires specific entity code on update.
What is the best way to solve it?
Related
I'm working on an enterprise application that uses Hibernate and EJB, and I'm utilizing Hibernate's Bytecode instrumentation to implement true lazy-loading of properties having bidirectional #OneToOne associations. I have a service method implemented using EJB, and as we all know, EJB uses RMI, which uses native Java serialization and deserialization to facilitate RPC invocations. The service method returns an entity with needed properties already fetched using JPQL, but since Hibernate doesn't set the fetched properties eagerly to their target fields, clients calling the service method end up receiving entities with properties having null values. Aside from calling the property getters manually before returning the entity, is there a way to tell Hibernate to set fetched property values automatically to their corresponding fields?
I'm using Hibernate 5.3.15 and JBoss EAP 7.2.8.
First of all, EJB doesn't necessarily use RMI, I guess what you mean is EJB remoting. There is no way that I know of to force field initialization except for initializing the fields through some means (access). One way to overcome this is to use DTOs that simply do not do any lazy initialization.
I think that this might be a bug in the serialization code of Hibernate for such bytecode enhanced proxies, so please create an issue in the issue tracker(https://hibernate.atlassian.net) with a test case(https://github.com/hibernate/hibernate-test-case-templates/blob/master/orm/hibernate-orm-5/src/test/java/org/hibernate/bugs/JPAUnitTestCase.java) that reproduces the issue.
If you want to take the DTO approach, I think this is a perfect use case for Blaze-Persistence Entity Views.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:
#EntityView(User.class)
public interface UserDto {
#IdMapping
Long getId();
String getName();
UserDetailsDto getDetails();
#EntityView(UserDetails.class)
interface UserDetailsDto {
#IdMapping
Long getId();
String getFirstname();
}
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
UserDto a = entityViewManager.find(entityManager, UserDto.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
Page<UserDto> findAll(Pageable pageable);
The best part is, it will only fetch the state that is actually necessary!
What is the best approach when developing (JSON-based) REST services in terms of Data objects.
Should I split database model (back-end) from the front-end model?
Is it a good practice to always keep the DB related JPA Entities up to a specific layer and then convert them into a set of DTOs for the front-end?
For example, 3 layer architecture:
Controller
Service
Repository
Should I confine the DB entities (annotated with JPA annotations) to Repository and Service layers
And then make the Controller only operate with another set of UI 'entities' (DTOs)?
This would require some kind of mapping between the 2 either automatic or 'manual'.
This allows for 'thin' front end entities.
For example in Backend we have the JPA annotations only and we have the owner as an Account reference:
#Entity
public class Job {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#ManyToOne(fetch = FetchType.LAZY)
private Account owner;
But in the front-end layer I would have Jackson specific annotations. I don't need the whole Account object, but only its id in the UI:
class Job {
long id;
String name;
long ownerId;
}
Update
After experimenting with "manual" mapping, the conclusion is that it quickly becomes a mess.
In my case, I wanted the Repository layer to return Entity (JPA) and the Service layer to do the mapping and return the Dto. So for getting data from the DB, this seems pretty affordable, there is only 1 mapping involved (from Entity to Dto). But, when it comes to creating / saving entities, the problem is bigger with composite objects. For example:
Let's say I POST a UserDto (which contains UserProfileDto as a composite object) from the API client to the Controller. Now the Service layer will accept UserDto, but it will have to find the UserProfileEntity corresponding to that UserProfileDto.
Also, Repository's .save() method returns the newly persisted Entity. Now this has to be mapped to Dto and then back to Entity if I want to use it further as part of another object (otherwise I will get the object references an unsaved transient instance - save the transient instance before flushing error).
For example, if I do:
repository.save(profileEntity) this will return a newly persisted ProfileEntity, but now I need to map it to ProfileDto in order to make it part of UserDto before mapping again UserDto to UserEntity and persisting.
Note: Dto here are objects I am planning to use as a response to the client (with JSON related annotations). These live in the Service and Controller layers, whereas Entity are JPA related objects that only live in the Repository and Service layers.
I'm testing spring data rest and I would like make a post on a relation entity.
For exemple :
I've two classes :
one two
----- -----
field field
#OneToOne
fieldTwo
how can I instantiate two ?
when I do post on /one
{
"field":"field",
"field2": {
"field":"field"
}
it doesn't create a field2
when I do post on /one/{idOne}/twos:
"field2": {
"field":"field"
}
it does nothing.
Does somebody have more informations ?
I didn't find any informations about this.
Thanks
Gegko
If I understood correctly you're trying to create records/entities with association using Spring Data Rest.
In Spring Data Rest when you POST for an entity, it won't create automatically the associated entity. instead, you will have to create each entity separately by yourself using rest.
If you want to create entities with association using REST, all you have to do is first create the not owning entity (the entity which doesn't hold the foreign key). when you do that you'll have its rest URL.
the second step is to take that URL and to put it as a foreign key when you try to save the second entity.
Here's an example:
POSTing a #OneToMany sub-resource association in Spring Data REST
My question is a follow up to this comment.
I'm mixing JPA and JAXB (MOXy) annotations on the same class, which works fine most of the time. As described in the linked thread, #XmlInverseReference prevents cycle exceptions when bidirectional relationships are marshalled. But in order to detect the cycle, MOXy has to inspect the back reference of the linked entity, which leads to extra SQL SELECTs if a lazy relation needs to be populated.
To illustrate the problem in detail, consider this made-up example:
#Entity
#Access( AccessType.FIELD )
#XmlRootElement
#XmlAccessorType( XmlAccessType.FIELD )
public class Phone {
#ManyToOne
#JoinColumn( name = "employeeID" )
#XmlElement( name = "employee" )
#XmlInverseReference( mappedBy = "phones" )
private Employee employee;
private String number;
[...]
}
#Entity
#Access( AccessType.FIELD )
#XmlRootElement
#XmlAccessorType( XmlAccessType.FIELD )
public class Employee {
#OneToMany( mappedBy = "employee" )
#XmlElementWrapper( name = "phones" )
#XmlElement( name = "phone" )
#XmlInverseReference( mappedBy = "employee" )
private List<Phone> phones;
private String name;
[...]
}
Now I'd run queries on Phones with a JAX-RS method like this (using an underlying EJB):
#Inject
private PhoneService phoneService;
#GET
#Path( "/phones" )
public List<Phone> getPhonesByNumber( #QueryParam( "number" ) String number ) {
List<Phone> result = phoneService.getPhonesByNumber( number );
return result;
}
What happens is this: The JPQL query within the PhoneService EJB triggers an SQL SELECT on the Phone table (filtered by the number), and if I use a JOIN FETCH query, I can get the associated Employee with the same single SELECT statement.
When the JAX-RS method returns, the JAXB marshalling kicks in, which leads to an additional SQL SELECT: this one selects all Phones whose employeeID points to the Employee who is associated with the originally requested Phones. So the lazy relationship from Employee to Phone is resolved now, presumably because MOXy must be able to determine if the original Phone is contained in the collection.
I've tried using JPA property access and JAXB field access for the phones field, as suggested in the other thread, to no avail. I've also tried nulling out the phones field in the linked Employee instance after retrieving the result from the EJB, i.e. when my entities are detached already, but this led to an immediate SQL SELECT again (it seems like EclipseLink will do this whenever any manipulation is done to an IndirectList?). The only workaround solution I could find is to use MOXy #XmlNamedObjectGraphs with a subgraph that excludes the phones field. But that's not practical, especially if the involved entities have many attributes.
As I may need to query in the other direction too, e.g. employees by name with their associated phones, I can't just mark phones as #XmlTransient.
Does anyone have an elegant solution to suppress those extra SQL statements?
From my experience the easiest way to accomplish what you are trying is to detach all the entity classes before you pass them to a presentation layer like a JAX-RS rest api. You can even use the #OneToMany(mappedBy = "employee", cascade = CascadeType.DETACH) and EntityManager.detach() to detach your phone class and subsequently detach your employee class or vice versa. This will ensure that during the marshaling of your entity, Jax-RS doesn't trigger any SELECT statements that you wouldn't normally want.
I always detach model entities before I pass them to the presentation layer so that they can interact with the model classes how they please without affecting performance or the database.
I collected some information about EclipseLink from these three threads. Important bits:
Detached Objects get the connection need to traverse the LAZY relationship from the EntityManagerFactory and will able able to use it as long as the EntityManagerFactory is open. The connection used in not the transactional one and when you want to use the entity in a transaction it will have to be properly merged.
This is a special feature of TopLink's implementation where the detached instances created from non-tx reads still have access in their proxies to retrieve additional dettached instances. If the object was detached through serialization this would not be possible.
If you would like TopLink Essentials to not process lazy relationships after the EM has closed I would recommend filing an enhancement request in GlassFish.
I couldn't find such an enhancement request though, let alone an implemented possibility to disable this feature (on a case-by-case basis).
There are five possible workarounds I could think of, each with its own drawbacks:
Just don't mix JAXB and JPA annotations on the same class: use a different set of additionatlly instantiated JAXB classes instead and perform explicit mapping between the two views. This could be a little expensive if lots of entities are returned from a query.
Like I mentioned in my question, use MOXy's (named) object graph feature to exclude (relationship) fields from being traversed.
Use a JAXB Marshaller.Listener to exclude all uninstantiated IndirectContainers.
Since serialization is supposed to break this EclipseLink feature for detached entities, serialize them before marshalling them. Seems awkward and even more expensive though.
This comes closest to emulating turning off the feature, but also looks hackish: access the wrapping IndirectContainer and its contained ValueHolderInterface and set them to null. Sample code:
(...)
import org.eclipse.persistence.indirection.IndirectContainer;
// entities must already be detached here, otherwise SQL UPDATEs will be triggered!
Employee e = phone.getEmployee();
IndirectContainer container = (IndirectContainer) e.getPhones();
container.setValueHolder( null );
e.setPhones( null );
We are migrating to spring & hibernate from struts1.x and JDBC.
Facing issue while adding multiple rows in jsp (like mapped properties in struts1.x) based on user preferences (Ex: Adding/deleting employees to Manager) in spring.
In service layer, getting Manager Object (as persistent object returned by hibernate )then displaying manager object in JSP. Employee row will be deleted by using java script/jquery from html form by user delete action, upon form submission spring does not delete in the respective employee from the list of employees of Manager object (model attribute). I have worked on some examples without hibernate where the object is being deleted from the list and works well but not in the hibernate prepared object.
ex: lets say Manager object have 3 employees as List when displaying on screen, when user will delete one employee row and submitted then spring needs to populate the Manager object with two employee objects as list since one employee is deleted by user in UI.
I suspect that, this behavior with PersistentBag implementation of Hibernate?
Anybody experienced this problem earlier? Any ideas would be greatly appreciate.
It might depends on how the pojo is declared..
Sure you are deleting a manager, but how is the manager connected to the employee?
If on the manager you have a Employee Object, you should have
#OneToOne(cascade=CascadeType.ALL, orphanRemoval=true, fetch=FetchType.EAGER)
#Fetch(FetchMode.SUBSELECT)
#JoinColumn(name="EMPLOYEE_ID", nullable = false)
private Employee employee;
This way, if you delete the manager object, it will cascade delete the Employee object...
Either your "cascade" is enforced via db, or it must be enforced via annotation.. if you're not doing either, of course nothing will happen...