Hibernate:BigInteger 与 Long 来自本机查询以用于 JPQL 查询
Hibernate: BigInteger vs Long from native query to be used in JPQL query
在我们的 Java EE EJB 应用程序中,我们具有 class 的以下 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;
}
这些是与之相关的 classes:
@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;
}
此外,我们有一个由 TimerService
EJB 定期执行的计划作业。
首先,此作业必须了解是否有要执行的内容,因此它会执行如下所示的 native sql 查询 以从 T [=59= 中恢复 pk 列表] 根据几个条件:
List<Long> seqTs = (List<Long>)em.createNativeQuery("select SEQ_T from T").getResultList();
其中 em
是 EntityManager
的实例。查询显然不是那么简单但非常复杂,因为它派生自一些 JOIN
和其他 table 的子查询。
如果返回的列表不为空,则该作业可以完成其工作并执行此 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()
时抛出了这个异常:
Exception in thread "main" java.lang.IllegalArgumentException: Parameter value element [1] did not match expected type [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 个数据库,并且它由 ANT 在 3 个 JAR 中相应地构建:Oracle、PostgreSQL 和 SQL 服务器。
我可以假设每个数据库的 PK 值总是 BigInteger
吗?在 Oracle 中我们使用 Number(19)
,在 PostgreSQL 中我们使用 BigInt
...我不记得 SQL 服务器。
然后将此实体传递给 DRools,在应用规则后,此作业使用 EntityManager
来保存数据。这就是为什么我需要加载 JPA 实体,否则我会得到
Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist
否则我将不得不为 DRools 修改的每个事实再次调用 .find()
并通过调用其他人的 getter 来设置其属性。这仍然会导致 N+1 个查询。
更安全的方法是使用 Number
而不是 BigInteger
List<Long> seqTLong = new ArrayList<Long>();
for(Number seqNative : seqTs) {
seqTLong.add(seqNative.longValue());
}
在我们的 Java EE EJB 应用程序中,我们具有 class 的以下 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;
}
这些是与之相关的 classes:
@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;
}
此外,我们有一个由 TimerService
EJB 定期执行的计划作业。
首先,此作业必须了解是否有要执行的内容,因此它会执行如下所示的 native sql 查询 以从 T [=59= 中恢复 pk 列表] 根据几个条件:
List<Long> seqTs = (List<Long>)em.createNativeQuery("select SEQ_T from T").getResultList();
其中 em
是 EntityManager
的实例。查询显然不是那么简单但非常复杂,因为它派生自一些 JOIN
和其他 table 的子查询。
如果返回的列表不为空,则该作业可以完成其工作并执行此 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()
时抛出了这个异常:
Exception in thread "main" java.lang.IllegalArgumentException: Parameter value element [1] did not match expected type [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 个数据库,并且它由 ANT 在 3 个 JAR 中相应地构建:Oracle、PostgreSQL 和 SQL 服务器。
我可以假设每个数据库的 PK 值总是 BigInteger
吗?在 Oracle 中我们使用 Number(19)
,在 PostgreSQL 中我们使用 BigInt
...我不记得 SQL 服务器。
然后将此实体传递给 DRools,在应用规则后,此作业使用 EntityManager
来保存数据。这就是为什么我需要加载 JPA 实体,否则我会得到
Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist
否则我将不得不为 DRools 修改的每个事实再次调用 .find()
并通过调用其他人的 getter 来设置其属性。这仍然会导致 N+1 个查询。
更安全的方法是使用 Number
而不是 BigInteger
List<Long> seqTLong = new ArrayList<Long>();
for(Number seqNative : seqTs) {
seqTLong.add(seqNative.longValue());
}