如何避免使用MOXy加载延迟双向关系? [英] How to avoid loading lazy bidirectional relationships with MOXy?

查看:72
本文介绍了如何避免使用MOXy加载延迟双向关系?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的问题是跟进这个评论。

我在同一个类上混合使用JPA和JAXB(MOXy)注释,这在大多数情况下都可以正常工作。如链接线程中所述, @XmlInverseReference 可防止在编组双向关系时出现循环异常。但是为了检测周期,MOXy必须检查链接实体的后向引用,如果需要填充延迟关系,则会导致额外的SQL SELECT。

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;

    [...]
}

现在我' d使用像这样的JAX-RS方法(使用底层EJB)在 Phone 上运行查询:

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

这是怎么回事:中的JPQL查询PhoneService EJB在 Phone 表上触发SQL SELECT(按数字过滤),如果我使用 JOIN FETCH 查询,我可以使用相同的单个SELECT语句获得关联的 Employee

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.

当JAX-RS方法返回时,JAXB编组开始,这导致一个额外的SQL SELECT:这个选择所有 Phone employeeID 指向与原始请求的电话相关联的 Employee 。所以现在解决了从 Employee Phone 的懒惰关系,大概是因为MOXy必须能够确定原来的电话包含在集合中。

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.

我尝试过使用JPA属性访问和JAXB字段访问 phones 字段,如其他帖子所示,无济于事。我还尝试在从EJB中检索结果后,在链接的 Employee 实例中清空 phones 字段,即当我的实体已经分离时,但这又导致了一个立即的SQL SELECT(看起来每当对 IndirectList 进行任何操作时,EclipseLink都会这样做)。我能找到的唯一解决方法是使用MOXy @XmlNamedObjectGraph s以及一个排除手机字段的子图。但这并不实际,特别是如果所涉及的实体具有许多属性。

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.

因为我可能还需要向另一个方向查询,例如员工的名字及其相关手机,我不能只将手机标记为 @XmlTransient

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.

有没有人有一个优雅的解决方案来压制那些额外的SQL语句?

Does anyone have an elegant solution to suppress those extra SQL statements?

推荐答案

我收集了有关EclipseLink的一些信息来自这些 三个 线程。重要位:

I collected some information about EclipseLink from these three threads. Important bits:


分离的对象获取连接需要遍历来自EntityManagerFactory的LAZY关系,并且只要能够使用它就能够使用它EntityManagerFactory已打开。在交易中使用的连接以及当您想在交易中使用实体时,必须正确合并。

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.


这是TopLink实现的一个特殊功能,其中从非tx读取创建的分离实例仍然可以在其代理中访问以检索其他dettached实例。如果通过序列化分离对象,则无法实现。

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.


如果您希望TopLink Essentials在EM关闭后不处理惰性关系,我建议您在GlassFish中提交增强请求。

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:


  1. 只是不要混用JAXB和同一类上的JPA注释:使用另一组另外添加的实例化JAXB类,并在两个视图之间执行显式映射。如果从查询返回大量实体,这可能会有点贵。

  1. 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.

就像我在我的问题中提到的那样,使用MOXy(命名)对象图功能排除(关系)字段。

Like I mentioned in my question, use MOXy's (named) object graph feature to exclude (relationship) fields from being traversed.

使用JAXB Marshaller.Listener排除所有未实例化的IndirectContainer。

Use a JAXB Marshaller.Listener to exclude all uninstantiated IndirectContainers.

由于序列化应该为分离的实体破坏此EclipseLink功能,因此在编组它们之前将它们序列化。看起来很尴尬,甚至更贵。

Since serialization is supposed to break this EclipseLink feature for detached entities, serialize them before marshalling them. Seems awkward and even more expensive though.

这最接近仿效关闭功能,但也看起来很黑:访问包装 IndirectContainer 及其包含的 ValueHolderInterface 并将它们设置为 null 。示例代码:

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

这篇关于如何避免使用MOXy加载延迟双向关系?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆