在EAGER共享关系oneToOne上执行JOIN FETCH时出现JPA ArgumentException错误

时间:2013-07-26 08:16:33

标签: jpa join fetch openjpa

我遇到了JOIN FETCH和EAGER关系的问题。

我有以下实体关系:

实体A扩展抽象实体E.

抽象实体E与实体C具有oneToOne双向关系, EAGER获取类型

实体A与实体B有一个ToMany关系。

实体B扩展抽象实体E(因此也与C具有oneToOne关系)

实体C与抽象实体E有反向关系

执行简单的namedQuery,如

SELECT a FROM A a WHERE a.key = :key

其中参数'key'是String类型,那么我没有问题。从检索到的实体A访问子实体B执行子请求,因为它应该是。

但是如果我在我的namedQuery中添加一个JOIN FETCH:

SELECT a FROM A a JOIN FETCH a.entitiesB WHERE a.key = :key

我获得了以下错误堆栈跟踪:

javax.ejb.EJBTransactionRolledbackException: The transaction has been marked rollback only because the bean encountered a non-application exception :javax.ejb.EJBTransactionRolledbackException : The transaction has been marked rollback only because the bean encountered a non-application exception :org.apache.openjpa.persistence.ArgumentException : The specified parameter of type "class org.apache.openjpa.util.LongId" is not a valid query parameter.
at org.apache.openejb.core.ivm.BaseEjbProxyHandler.convertException(BaseEjbProxyHandler.java:345)
at org.apache.openejb.core.ivm.BaseEjbProxyHandler.invoke(BaseEjbProxyHandler.java:283)
... 
Caused by: <openjpa-2.2.0-r422266:1244990 nonfatal user error> org.apache.openjpa.persistence.ArgumentException: The specified parameter of type "class org.apache.openjpa.util.LongId" is not a valid query parameter.
FailedObject: SELECT relation FROM Relation relation JOIN FETCH relation.accounts WHERE relation.key = :key [java.lang.String]
at org.apache.openjpa.jdbc.sql.DBDictionary.setUnknown(DBDictionary.java:1458)
at org.apache.openjpa.jdbc.sql.SQLBuffer.setParameters(SQLBuffer.java:544)
at org.apache.openjpa.jdbc.sql.SQLBuffer.prepareStatement(SQLBuffer.java:453)
at org.apache.openjpa.jdbc.sql.SQLBuffer.prepareStatement(SQLBuffer.java:429)
at org.apache.openjpa.jdbc.sql.SelectImpl.prepareStatement(SelectImpl.java:479)
... 

如果我将EAGER关系更改为LAZY,我不再有此错误。那么,问题是什么?

编辑1:如果我保持与LAZY获取类型的oneToOne关系并直接在namedQuery上添加JOIN FETCH a.entityC,我会得到同样的错误

编辑2:从abstractEntity类中删除与实体C的关系oneToOne,并将该关系直接添加到EntityA和EntityB中(加上修改实体C与A&amp; B具有反向关系),仍将这个oneToOne关系保持为EAGER =&gt;没问题。 所以看起来问题来自于将这种共享关系引入抽象实体类,但为什么呢?

这是代码示例说明问题。

实体A

@Entity
@Table(name = "TABLE_A")
@Access(AccessType.FIELD)
@NamedQueries({
   @NamedQuery(name = "findADetails", query = "SELECT a FROM A a WHERE a.customKey.key     = :customKey")})
public class A extends abstractEntity {

   @Embedded
   private CustomEmbeddedKey customKey;

   @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "entityA")
   private List<B> bEntities;

   ...
}

实体B

@Entity
@Table(name = "TABLE_B")
@Access(AccessType.FIELD)
public class B extends abstractEntity {

   @ManyToOne(fetch = FetchType.LAZY, targetEntity = A.class)
   @JoinColumn(name = "FK_A_ID")
   private A entityA;

   ...
}

抽象实体类

@Entity
@Access(AccessType.FIELD)
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@SequenceGenerator(name = "TOTO_ID_SEQ", sequenceName = "TOTO_ID_SEQ", initialValue = 1, allocationSize = 1)
public abstract class abstractEntity {

   @Id
   @Column(name = "ID")
   @GeneratedValue(generator = "TOTO_ID_SEQ", strategy = GenerationType.SEQUENCE)
   private Long id;

   @OneToOne(mappedBy = "...", fetch = FetchType.EAGER)
   private C anotherEntity;

   @OneToMany(mappedBy = "...", fetch = FetchType.LAZY)
   private List<D> anotherEntities;

   ...

}

嵌入式密钥

@Embeddable
@Access(AccessType.FIELD)
public class CustomEmbeddedKey {

   @Column(name = "...", length = ...)
   private String key;

   ...
}

Dao示例

TypedQuery<A> query = createNamedQuery("findADetails", A.class);
  query.setParameter("customKey", queryParam);
  List<A> aEntitiesFound = query.getResultList();

提前感谢您的帮助。

1 个答案:

答案 0 :(得分:1)

认为我发现了问题。这是由于我们参考Eager Fetching Considerations and Limitations

上的文档时openJPA并不真正支持的映射

所以最后我有两个解决方案:


解决方案1(简单)

  • 在抽象实体类
  • 上将获取类型EAGER更改为LAZY
  • 添加获取组并更新每个相关的namedQueries或代码

<强>优点:

  • 快速解决方案,对现有代码没有太大影响。

<强>缺点:

  • 必须调查新LAZY字段的使用情况+相关查询的更新,因为它现在将为每个getter延迟加载(如果不是来自更新的namedQueries)
  • 使用Junit和动态增强运行测试仍会在使用此类映射(抽象&amp; TABLE_PER_CLASS继承类型)在namedQueries上执行JOIN FETCH时抛出NPE

解决方案2:修改映射

  • 从抽象实体中删除继承的使用(实际上没有业务用法)并替换为@MappedSuperClass(以及删除@Entity和@Inheritance)
  • 添加界面并定义EAGER属性的getter / setter
  • 将此接口实现到抽象实体类中,以便子类必须实现getter / setter(以及相关的属性和映射)
  • 将反向关系的类型更改为这些EAGER对象的目标实体,从抽象类类型到新接口类型
  • 最后将EAGER属性的映射,getter和setter的实现从抽象实体类移动到所有子实体类。

<强>优点:

  • 没有更多的例外(由于旧的映射和动态类增强,也没有NPE)
  • 更强大的解决方案,因为有更多机会避免因旧映射样式的openJPA限制而导致的进一步异常

<强>缺点:

  • 更多修改
  • 由于接口而不是抽象类类型到目标实体类中,开发人员的可读性可能较低