休眠:BigInteger vs Long 从本机查询中用于 JPQL 查询



在我们的Java EE EJB应用程序中,我们有以下类的JPA/Hibernate映射:

@Entity
@Table(name="T")
@TableGenerator( /* all annotation attributes */)
public class T {
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
@Column(name="SEQ_T", nullable = false)
private long seqT;
@OneToMany(
cascade = CascadeType.ALL, 
orphanRemoval = true,
mappedBy = "t",
fetch = FetchType.LAZY
)
private List<W> wu;
}

这些是与之相关的类:

@Entity
@Table(name="W")
@TableGenerator( /* all annotation attributes */)
public class W {
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
@Column(name="SEQ_W", nullable = false)
private long seqW;
@Column(name="SEQ_T", nullable = false)
private long seqT;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "SEQ_T", insertable = false, updatable = false)
private T t;
@OneToMany(
cascade = CascadeType.ALL, 
orphanRemoval = true,
mappedBy = "w",
fetch = FetchType.LAZY
)
private List<WA> wua;
}
@Entity
@Table(name="WA")
@TableGenerator( /* all annotation attributes */)
public class WA {
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
@Column(name="SEQ_W_A", nullable = false)
private long seqWA;
@Column(name="SEQ_W", nullable = false)
private long seqW;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "SEQ_W", insertable = false, updatable = false)
private W w;
}

此外,我们有一个由TimerServiceEJB 定期执行的计划作业。 首先,此作业必须了解是否有要执行的内容,以便执行如下所示的本机 sql 查询,以根据几个条件从 T 表中恢复 pk 列表:

List<Long> seqTs = (List<Long>)em.createNativeQuery("select SEQ_T from T").getResultList();

其中emEntityManager的实例。查询显然不是那么简单,但非常复杂,因为它派生自其他表的一些JOIN和子查询。 如果返回的列表不为空,则作业可以完成其工作,并执行此 JPQL 以加载它操作的实体:

String queryJPQL = "select wu from W wu JOIN FECTCH wu.wua where wu.seqT in :seqTs";
List<Workup> wus = em.createQuery(queryJPQL, W.class)
.setParameter("seqTs", seqTs)
.getResultList();

执行此查询是因为即使我们始终需要@OneToMany关系中的数据,如果我们将该关系设置为EAGER则执行 N+1 个查询。相反,JOIN FETCH执行唯一查询以恢复一种视图,然后通过 Hibernate 关联实体和关系。

好吧,问题是调用.setParameter()时会引发此异常:

线程"main"中的异常 java.lang.IllegalArgumentException: 参数值元素 [1] 与预期类型 [java.lang.Long (n/a(] 不匹配

阅读这里的许多帖子,并在 Eclipse 中设置断点,我发现不是从本机查询返回List<Long>,而是从List<BigInteger>返回(根据数据库中 PK 的本机类型(,没有任何ClassCastException或类似。为什么会这样? 所以,我想我应该在之前执行这样的事情:

List<Long> seqTLong = new ArrayList<Long>();
for(BigInteger seqNative : seqTs) 
seqTLong.add(seqNative.longValue());

并将其传递给查询。 无论如何,这是正确的解决方案吗?安全吗?这是因为我们的应用程序支持 3 DB,并且它是由 ANT 在 3 个 JAR 中相应地构建的:Oracle、PostgreSQL 和 SQL Server。 我可以假设每个数据库的 PK 值始终BigInteger吗?在Oracle中我们使用Number(19),在PostgreSQL中我们使用BigInt...我不记得SQL Server了。 然后将此实体传递给 DRools 应用规则后,此作业使用EntityManager来保存数据。这就是为什么我需要加载 JPA 实体,否则我会得到一个

原因:org.hibernate.PersistentObject异常:分离的实体传递到持久

否则我将不得不为 DRools 修改的每个事实再次调用.find(),并通过调用其他事实的 getter 来设置其属性。这仍然会导致 N+1 个查询。

更安全的方法是使用Number而不是BigInteger

List<Long> seqTLong = new ArrayList<Long>();
for(Number seqNative : seqTs) {
seqTLong.add(seqNative.longValue());
}

最新更新