How to avoid loading lazy bidirectional relationships with MOXy?

My question is a comment on this .

I mix JPA and JAXB (MOXy) annotations in the same class, which works great in most cases. As described in the linked thread, @XmlInverseReference prevents circular exceptions when bidirectional relationships are sorted. But in order to detect the loop, MOXy must check the backlink of the related object, which leads to additional SQL SELECT if you need to fill in the lazy relation.

To illustrate the problem in detail, consider this 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 will run queries on Phone using this JAX-RS method (using basic 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: The JPQL query in PhoneService EJB starts SQL SELECT in the Phone table (filtered by number), and if I use the JOIN FETCH query, I can get the associated Employee with the same SELECT statement.

When the JAX-RS method returns, JAXB marshalling is triggered, resulting in an additional SQL SELECT: this selects all the Phone whose employeeID points to Employee , which is associated with the originally requested Phone s. So the lazy relationship from Employee to Phone is now resolved, apparently because MOXy should be able to determine if the original Phone in the collection.

I tried using access to JPA properties and access to JAXB fields for the phones field, as suggested in another thread, to no avail. I also tried to reset the phones field in the associated Employee instance after getting the result from EJB, that is, when my objects were already detached, but this again led to an immediate SQL SELECT (it seems that EclipseLink will do this whenever any manipulations are performed with IndirectList ?). The only workaround I could find was to use MOXy @XmlNamedObjectGraph with a subgraph that excludes the phones field. But this is impractical, especially if the objects involved have many attributes.

How may I need to execute the request in a different direction, for example. employees by name with their phones, I can’t just mark phones as @XmlTransient .

Does anyone have an elegant solution to suppress these extra SQL queries?

+1
source share
2 answers

I have gathered some information about EclipseLink from these three topics . Important bits:

Individual objects receive a connection to traverse the LAZY relationship from EntityManagerFactory and can use it while EntityManagerFactory is open. A connection that is not used in a transaction, but when you want to use an entity in a transaction, it must be correctly merged.

This is a special feature of the TopLink implementation where individual instances created from non-tx reads still have access to their proxies to retrieve additional dettached instances. If the object was detached via serialization, this would not be possible.

If you want TopLink Essentials not to handle lazy relationships after EM closes, I would recommend submitting an improvement request to GlassFish.

I could not find such an extension request, although, not to mention the realized opportunity to disable this function (in each case).

There are five possible workarounds that I could come up with, each with its own flaws:

  • Just don't mix JAXB and JPA annotations in the same class: instead, use a different set of additional JAXB instances and do explicit mapping between the two views. This can be a little expensive if many objects are returned from the request.

  • As I mentioned in my question, use the MOXy (named) object graph function to exclude fields (relationships) from passing.

  • Use the JAXB Marshaller.Listener to exclude all uninstalled IndirectContainers.

  • Since serialization should destroy this EclipseLink function for individual objects, serialize them before sorting them. It seems uncomfortable and even more expensive though.

  • This is the closest thing to emulating a function shutdown, but it also looks hacky: accessing the IndirectContainer and the contained ValueHolderInterface and null binding. Code example:

(...)

 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 ); 
+1
source

In my experience, the easiest way to accomplish what you are trying to do is to separate all entity classes before passing them to a presentation layer such as the JAX-RS rest api. You can even use @OneToMany(mappedBy = "employee", cascade = CascadeType.DETACH) and EntityManager.detach () to disconnect your phone class and then disconnect your employee class, or vice versa. This ensures that while marshaling your object, Jax-RS does not run any SELECT statements that you normally don't need.

I always separate model objects before passing them to the presentation layer so that they can interact with model classes as they like, without affecting performance or the database.

+1
source

Source: https://habr.com/ru/post/1485239/


All Articles