【问题标题】:JPA ArgumentException error when doing JOIN FETCH on EAGER shared relation oneToOne在 EAGER 共享关系 oneToOne 上执行 JOIN FETCH 时出现 JPA ArgumentException 错误
【发布时间】:2013-07-26 08:16:33
【问题描述】:

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

我有以下实体关系:

实体 A 扩展抽象实体 E。

抽象实体 E 与具有 EAGER 提取类型的实体 C 具有 oneToOne 双向关系。

实体 A 与实体 B 具有 oneToMany 关系。

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

实体 C 与抽象实体 E 具有反比关系

在执行一个简单的命名查询时,比如

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:如果我保持 oneToOne 与 LAZY 提取类型的关系并直接在 namedQuery 上添加 JOIN FETCH a.entityC,我会得到同样的错误

编辑 2:从 abstractEntity 类中删除与实体 C 的关系 oneToOne 并将此关系直接添加到 EntityA 和 EntityB 中(加上修改实体 C 以与 A 和 B 具有反向关系),仍然将此 oneToOne 关系保持为 EAGER => 没问题. 所以看起来问题出在将这个共享关系放到抽象实体类中,但为什么呢?

这是说明问题的代码示例。

实体 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;

   ...
}

道样本

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

提前感谢您的帮助。

【问题讨论】:

    标签: jpa join fetch openjpa


    【解决方案1】:

    我认为我找到了问题所在。如果我们参考Eager Fetching Considerations and Limitations上的文档,这是由于openJPA并不真正支持映射

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


    解决方案 1(简单的):

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

    优点:

    • 快速解决,对现有代码影响不大。

    缺点:

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

    方案二:修改映射

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

    优点:

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

    缺点:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-19
      • 1970-01-01
      • 2011-07-21
      相关资源
      最近更新 更多