How to avoid loading lazy bidirectional relationships with MOXy? - java

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 );

Related

Does FetchType.LAZY fetch anything in list or it's empty

Let's say i have a association as ,
class Department{
......
#OneToMany(fetch = FetchType.LAZY)
private List<Employee> employees;
}
Now when i fetch Department, will it fetch anything in employees list or will it be completely empty.
Will identifiers for employee object be loaded in list like say i have employee object attributes as below:-
Employee{
id
name
doj
....
}
Lazy object like {
id -> 111
name -> null
doj -> null
}
Now when i initialize employee object or access it's properties using getters, then the object will be loaded from database using id as an identifier...??
Instead of the real collection class (e.g. ArrayList) a different List implementation is injected into your field (PersistentList). Depending on the calls on that collection and the lazy strategy it will do different things:
In case lazy="lazy" the call to any of the collections methods would get the collection fully loaded
If lazy="extra", then calls to some functions would trigger SQL without loading the collection. E.g. list.size() would trigger select count.... While getting the 1st element would select only that element. This may be suitable for large collections. Note, that this behaviour may also depend on the collection type - unordered collections will load all elements anyway.
Lazy fetch type, Hibernate won’t load the relationships for that particular object instance.
FetchType.LAZY = Doesn’t load the relationships unless explicitly “asked for” via getters
FetchType.EAGER = Loads ALL relationships
In your case It won't load Employee List from database unless you explicitly fire query for it, Because you have set fetch type ( fetch = FetchType.LAZY ). If fetch type was ( fetch = FetchType.EAGER ) then It would explicitly fire a select query for Employee list. In that object you would get all employee property eg name, doj.
the object will be loaded from database using id as an identifier...??
Department{
#OneToMany(fetch = FetchType.EAGER,,mappedBy = "department")
private List<Employee> employees;
}
In Emplooyee.... You need to mapped it by reference of department object.
eg:
Employee{
// Reference of department.
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "departmentid", nullable = false)
private Department department;
}
This way it will become bidirectional. now Hibernate will fire query by reference (id in native sql) .
If you want to be loaded lazily the set Fetch mode FetchType.LAZY in both mapping....
In your case until you explicitly access Department.employees (through a getter or any other means) It will not load the Employee entities. There will be a Proxy for this. It will be initialized once at the first call to access this employees collection.
How Lazy Loading Works in Hibernate
The simplest way that Hibernate can apply lazy load behavior upon your entities and associations is by providing a proxy implementation of them. Hibernate intercepts calls to the entity by substituting a proxy for it derived from the entity’s class. Where the requested information is missing, it will be loaded from the database before control is ceded to the parent entity’s implementation.
Please note that when the association is represented as a collection class, then a wrapper (essentially a proxy for the collection, rather than for the entities that it contains) is created and substituted for the original collection. When you access this collection proxy then what you get inside returned proxy collection are not proxy entities; rather they are actual entities. You need not to put much pressure on understanding this concept because on runtime it hardly matters.
Please refer this for more information:
http://howtodoinjava.com/hibernate/lazy-loading-in-hibernate/
Also enable hibernate.sql.show=true so that you can see what queries are being fired when you are trying to fetch the collections.

JPA lazy fetch list invokes SELECT queries in setter

There is a method which returns entity from database by JPA. This entity has list for other entities, fetch type is LAZY. When I want to add object to this list I got exception:
Caused by: Exception [EclipseLink-7242] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.ValidationException
Exception Description: An attempt was made to traverse a relationship using indirection that had a null Session. This often occurs when an entity with an uninstantiated LAZY relationship is serialized and that lazy relationship is traversed after serialization. To avoid this issue, instantiate the LAZY relationship prior to serialization.
So in order to overcome this I can initialize this list by doing .size() on it. The thing is I don't really need these objects to be fetched from database so I would like to do something like this:
fetchedEntity.setMyLazyFetchList(new ArrayList<>()); which works fine. I can further access my list, but the problem is as following: set method invokes the same select queries as fetchedEntity.getMyLazyFetchList().size() does. These queries are useless as I set value to a new list, so why are they invoked?
Method fetching entity
public Competitor findAndInitializeEmptyGroups(Integer idCompetitor) {
Competitor entity = em.find(Competitor.class, idCompetitor);
System.out.println("Before set ");
entity.setGroupCompetitorList(new ArrayList<>());
System.out.print("After set lazy list size ");
System.out.print(entity.getGroupCompetitorList().size());
return entity;
}
Lazy fetch list field in entity (Competitor)
#OneToMany(mappedBy = "idCompetitor")
private List<GroupCompetitor> groupCompetitorList = new ArrayList<>();
Second end relationship field (GroupCompetitor)
#JoinColumn(name = "id_competitor", referencedColumnName = "id_competitor")
#ManyToOne(optional = false)
private Competitor idCompetitor;
What logs say:
Info: Before set
Fine: SELECT id_group_competitor, id_competitor, id_group_details FROM group_competitor WHERE (id_competitor = ?)
bind => [43]
Fine: SELECT id_group_details, end_date, start_date, version, id_competition, id_group_name FROM group_details WHERE (id_group_details = ?)
bind => [241]
...
many more SELECTs
Info: After set lazy list size
Info: 0
After replacing line
entity.setGroupCompetitorList(new ArrayList<>());
with
entity.getGroupCompetitorList().size();
And logs (they are the same except the list now consists of fetched entities):
Info: Before set
Fine: SELECT id_group_competitor, id_competitor, id_group_details FROM group_competitor WHERE (id_competitor = ?)
bind => [43]
Fine: SELECT id_group_details, end_date, start_date, version, id_competition, id_group_name FROM group_details WHERE (id_group_details = ?)
bind => [241]
...
many more SELECTs
Info: After set lazy list size
Info: 44
So my question is: why SELECT queries are invoked when I do entity.setGroupCompetitorList(new ArrayList<>());? I don't want them for the performance reasons. Is there any way to eliminate this issue or what exactly causes this behavior?
Using:
EclipseLink JPA 2.1
GlassFish 4.1
Java 8
You can't not fetch a list that is a member of an entity if you want to add an element and have the JPA provider persist it. The JPA provider has to track the owner, the ownee, and handle any cascading (which I don't see you have defined but I doubt there's a different code path for each combination of cascading options). The simplest way would be to have the list in memory and then decide what operation to perform on the DB at commit/flush time.
I believe the cause of your original exception about traversing a LAZY is due to accessing outside on a managed context. Once you return from an EJB method, the entity you're returning becomes detached. You have to reattach it to another EntityManager or make sure all the lazy relationships you're about to use have been loaded before you leave the method. Calling fetchedEntity.getMyLazyFetchList().size() would be an example of that and works fine in a single entity case. If you want to force the load of a LAZY in a list of entities I suggest you read up on LEFT JOIN FETCH clauses. I'm assuming here that your findAndInitializeEmptyGroups() method is in an EJB, judging by what looks to me like an injected EntitManager em in that method, and that the methods will get the default #TransactionAttribute(REQUIRED) treatment since I don't see any annotations to the contrary.
Now, let's go back to your original problem:
I want to add object to this list
The problem you're trying to solve is to add an element to a list without fetching the entire list. You're using a mappedBy attribute, meaning you've created a bidirectional relationship. If getGroupCompetitorList() returns an unordered list (a 'bag' in Hibernate speak) then you don't have to load the list at all. Try something like this:
Change GroupCompetitor's Integer idCompetitor to a #ManyToOne(fetch=FetchType.LAZY) Competitor competitor. Adjust the getters and setters accordingly.
Change Competitor's groupCompetitorList mappedBy to competitor. Add get/set methods.
Then you can add to the list from the child side with a method like this in the EJB:
public void addNewGroupCompetitorToCompetitor(Competitor comp, GroupCompetitor gComp) {
gComp.setCompetitor(comp);
em.persist(gComp);
em.flush();
}
The next time you fetch the Competitor again it and traverse entity.getGroupCompetitorList() (while managed by an EntityManager) it should have the new GroupCompetitor you've added. This kind of thing gets more complicated depending if comp is a new entity that has not been persisted, but that's the basic idea. It might need some adjustment to work correctly with EclipseLink, but I do the same kind of operation with Hibernate as the JPA provider and it works.

How to maintain bi-directional relationships with Spring Data REST and JPA?

Working with Spring Data REST, if you have a OneToMany or ManyToOne relationship, the PUT operation returns 200 on the "non-owning" entity but does not actually persist the joined resource.
Example Entities:
#Entity(name = 'author')
#ToString
class AuthorEntity implements Author {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id
String fullName
#ManyToMany(mappedBy = 'authors')
Set<BookEntity> books
}
#Entity(name = 'book')
#EqualsAndHashCode
class BookEntity implements Book {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id
#Column(nullable = false)
String title
#Column(nullable = false)
String isbn
#Column(nullable = false)
String publisher
#ManyToMany(fetch = FetchType.LAZY, cascade = [CascadeType.ALL])
Set<AuthorEntity> authors
}
If you back them with a PagingAndSortingRepository, you can GET a Book, follow the authors link on the book and do a PUT with the URI of a author to associate with. You cannot go the other way.
If you do a GET on an Author and do a PUT on its books link, the response returns 200, but the relationship is never persisted.
Is this the expected behavior?
tl;dr
The key to that is not so much anything in Spring Data REST - as you can easily get it to work in your scenario - but making sure that your model keeps both ends of the association in sync.
The problem
The problem you see here arises from the fact that Spring Data REST basically modifies the books property of your AuthorEntity. That itself doesn't reflect this update in the authors property of the BookEntity. This has to be worked around manually, which is not a constraint that Spring Data REST makes up but the way that JPA works in general. You will be able to reproduce the erroneous behavior by simply invoking setters manually and trying to persist the result.
How to solve this?
If removing the bi-directional association is not an option (see below on why I'd recommend this) the only way to make this work is to make sure changes to the association are reflected on both sides. Usually people take care of this by manually adding the author to the BookEntity when a book is added:
class AuthorEntity {
void add(BookEntity book) {
this.books.add(book);
if (!book.getAuthors().contains(this)) {
book.add(this);
}
}
}
The additional if clause would've to be added on the BookEntity side as well if you want to make sure that changes from the other side are propagated, too. The if is basically required as otherwise the two methods would constantly call themselves.
Spring Data REST, by default uses field access so that theres actually no method that you can put this logic into. One option would be to switch to property access and put the logic into the setters. Another option is to use a method annotated with #PreUpdate/#PrePersist that iterates over the entities and makes sure the modifications are reflected on both sides.
Removing the root cause of the issue
As you can see, this adds quite a lot of complexity to the domain model. As I joked on Twitter yesterday:
#1 rule of bi-directional associations: don't use them… :)
It usually simplifies the matter if you try not to use bi-directional relationship whenever possible and rather fall back to a repository to obtain all the entities that make up the backside of the association.
A good heuristics to determine which side to cut is to think about which side of the association is really core and crucial to the domain you're modeling. In your case I'd argue that it's perfectly fine for an author to exist with no books written by her. On the flip side, a book without an author doesn't make too much sense at all. So I'd keep the authors property in BookEntity but introduce the following method on the BookRepository:
interface BookRepository extends Repository<Book, Long> {
List<Book> findByAuthor(Author author);
}
Yes, that requires all clients that previously could just have invoked author.getBooks() to now work with a repository. But on the positive side you've removed all the cruft from your domain objects and created a clear dependency direction from book to author along the way. Books depend on authors but not the other way round.
I faced a similar problem, while sending my POJO(containing bi-directional mapping #OneToMany and #ManyToOne) as JSON via REST api, the data was persisted in both the parent and child entities but the foreign key relation was not established. This happens because bidirectional associations need to be manually maintained.
JPA provides an annotation #PrePersist which can be used to make sure that the method annotated with it is executed before the entity is persisted. Since, JPA first inserts the parent entity to the database followed by the child entity, I included a method annotated with #PrePersist which would iterate through the list of child entities and manually set the parent entity to it.
In your case it would be something like this:
class AuthorEntitiy {
#PrePersist
public void populateBooks {
for(BookEntity book : books)
book.addToAuthorList(this);
}
}
class BookEntity {
#PrePersist
public void populateAuthors {
for(AuthorEntity author : authors)
author.addToBookList(this);
}
}
After this you might get an infinite recursion error, to avoid that annotate your parent class with #JsonManagedReference and your child class with #JsonBackReference. This solution worked for me, hopefully it will work for you too.
This link has a very good tutorial on how you can navigate the recursion problem:Bidirectional Relationships
I was able to use #JsonManagedReference and #JsonBackReference and it worked like a charm
I believe one can also utilize #RepositoryEventHandler by adding a #BeforeLinkSave handler to cross link the bidirectional relation between entities. This seems to be working for me.
#Component
#RepositoryEventHandler
public class BiDirectionalLinkHandler {
#HandleBeforeLinkSave
public void crossLink(Author author, Collection<Books> books) {
for (Book b : books) {
b.setAuthor(author);
}
}
}
Note: #HandlBeforeLinkSave is called based on the first parameter, if you have multiple relations in your equivalent of an Author class, the second param should be Object and you will need to test within the method for the different relation types.

Mapping one DB column to two seperate fields using JPA

I'm developing a code generator that have to generate JPA entities from database meta-model files. These model are from home-brewed modeling system which are being used to generate models other than JPA entities.
In these models some fields are mapping back to same database column. But it seems like JPA does not like that very much. When I try to run generated code I get
Exception [EclipseLink-48] (Eclipse Persistence Services - 2.6.0.v20140809-296a69f): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: Multiple writable mappings exist for the field [FACT_INVENT_TRANS_HIST_DM.TRANSACTION_ID]. Only one may be defined as writable, all others must be specified read-only.
Mapping: org.eclipse.persistence.mappings.DirectToFieldMapping[TransactionIdKey-->FACT_INVENT_TRANS_HIST_DM.TRANSACTION_ID]
Descriptor: RelationalDescriptor(InventTransHistFactDM --> [DatabaseTable(FACT_INVENT_TRANS_HIST_DM)])
As I can't change the models only option left is to make one of those fields read-only. And the JPA entities being generated are only used to read data from database it will not used for writing data. Is there a way to mark some fields as read only or tell EclipseLink that these entities are read only so it does not have to worry about the multiple writable mapping.
I tried using EclipseLink's #ReadOnly annotation in all entities but it did not help this issue.
There is no #ReadOnly in JPA.
There are however attributes "insertable"/"updatable" that you can set against a field via #Column to effectively do the same.
The question may be almost 6 years old, but it's still being found today, so I'd like to address another option:
public class Foobar {
#OneToOne
#JoinColumn(name="SELF_COLUMN_FOO", referencedColumnName = "FOREIGN_COLUMN_TO_JOIN")
public Foo foo;
#OneToOne
#JoinColumn(name="SELF_COLUMN_BAR", referencedColumnName = "FOREIGN_COLUMN_TO_JOIN")
public Bar bar;
}
This can be used where SELF_COLUMN is obviously the relevant column in the Foobar table, and FOREIGN_COLUMN_TO_JOIN would be single key in the other table you wish to join.
This will be useful where you want to have two (or more) attributes in a single class, but only one column to join on the foreign DB table. For example: An Employee may have a home phone number, cell number, and a work phone number. All are mapped to different attributes in the class, but on the database there's a single table of phone numbers and id's, and an identifier column, say VARCHAR(1) with 'H' or 'W' or 'C'. The real example would then be...
Tables:
PHONENUMBERS
PHONENUMBER_ID,
ACTUAL_NUMBER
EMPLOYEE
ID
HOMENUMBER VARCHAR(12),
CELLNUMBER VARCHAR(12),
WORKNUMBER VARCHAR(12)
public class Employee {
#OneToOne
#JoinColumn(name="HOMENUMBER", referencedColumnName = "PHONENUMBER_ID")
public Phone homeNum;
#OneToOne
#JoinColumn(name="CELLNUMBER", referencedColumnName = "PHONENUMBER_ID")
public Phone cellNum;
#OneToOne
#JoinColumn(name="WORKNUMBER", referencedColumnName = "PHONENUMBER_ID")
public Phone workNum;
}
As you can see, this would require multiple columns on the Entity's table, but allows you to reference a foreign key multiple times without throwing the 'Multiple writable mappings exist...' that you showed above. Not a perfect solve, but helpful for those encountering the same problem.

Efficiently determining the IDs of entities referenced via OneToMany relationship

Let's say I have a Hibernate entity that declares a OneToMany relationship to a different entity:
#Entity
public class SomeEntity {
#OneToMany(fetch = FetchType.LAZY)
private List<OtherEntity> otherEntities = new LinkedList<OtherEntity>();
[...]
}
When mapping SomeEntity to the corresponding DTO, all I need are the IDs that identify OtherEntity as primary key (i.e., I am not actually interested in OtherEntity instances).
Does Hibernate support this pattern, i.e., only retrieving the IDs of entities referenced via a OneToMany relationship?
I cannot influence how SomeEntity is retrieved (i.e., I have an existing SomeEntity instance retrieved within te scope of the current Hibernate session), but let's assume that lazy loading has not yet taken place, so just retrieving the child objects' IDs (rather than the complete objects) would actually yield a performance benefit.
Well, if you only need the entities' ids and you want to be economical about it, when you get those entities from the database you should state in your query that you only want to get the ids of each entry, using projections, something like :
SELECT Entity.id as entity FROM Entity WHERE ...
This will return an array of objects of the same type as Entity's id field type.
You can try obtaining the primary key without accessing the entity itself (without otherEntities.get(0).getId()). To do this you can use the PersistenceUnitUtil class:
PersistenceUnitUtil#getIdentifier(yourEntity)
The PersistenceUnitUtil can be obtained from the EntityManagerFactory. So it could be something like:
EntityManager em = ...
PersistenceUnitUtil = em.getEntityManagerFactory().getPersistenceUnitUtil();
Unfortunately, I'm not aware if this will prevent the entity loading from occuring. However, just accessing the otherEntities collection or even obtaining references to each entity will not make the instance to be loaded; you need to invoke a method on the fetched entity in order to be sure it will be loaded.
You also might consider creating a #NamedQuery and return only the OtherEntity ID's.
HTH!
From hibernate reference, section 2.2.2.1.
http://docs.jboss.org/hibernate/annotations/3.5/reference/en/html/entity.html#entity-mapping-property
Declare your columns as lazy initialized:
#Basic(fetch = FetchType.LAZY)
private String getYourProperty() {
}
You also need to disable proxies for your entity class and byte instrument it. There is an example here:
Making a OneToOne-relation lazy
You can use the below HQL as told in the documentation to establish this.
session.createQuery(select new OtherEntity(oe.id) OtherEntity oe
where oe.parentSomeEntity.someId = :someId).list();//also set someId.
Add a constructor in OtherEntity to set the id also there should be a mapping to SomeEntity in OtherEntity.
This HQL will give you a List<OtherEntity> with only id set in the bean.

Categories

Resources