gpt4 book ai didi

hibernate - JPQL 和元组列表作为 SELECT IN 语句的参数

转载 作者:行者123 更新时间:2023-12-05 01:41:01 24 4
gpt4 key购买 nike

给定以下表格布局:

CREATE TABLE things (
id BIGINT PRIMARY KEY NOT NULL,
foo BIGINT NOT NULL,
bar BIGINT NOT NULL
);

实体类 (Kotlin):

@Entity
@Table(name = "things")
class Thing(
val foo: Long,
val bar: Long
) : AbstractPersistable<Long>()

还有一个存储库:

interface ThingRepository : JpaRepository<Thing, Long> {
@Query("SELECT t FROM Thing t WHERE t.foo IN ?1")
fun selectByFoos(foos: Iterable<Long>): Iterable<Thing>

@Query("SELECT t FROM Thing t WHERE (t.foo, t.bar) IN ((1, 2), (3, 4))")
fun selectByFoosAndBarsFixed(): Iterable<Thing>

@Query("SELECT t FROM Thing t WHERE (t.foo, t.bar) IN ?1")
fun selectByFoosAndBars(foosAndBars: Iterable<Pair<Long, Long>>): Iterable<Thing>

以下两个调用工作正常:

repo.selectByFoos(listOf(1L, 3L))
repo.selectByFoosAndBarsFixed()

但是这个没有:

repo.selectByFoosAndBars(listOf(Pair(1L, 2L), Pair(3L, 4L)))

它抛出:

org.springframework.dao.DataIntegrityViolationException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.DataException: could not extract ResultSet

Caused by: org.h2.jdbc.JdbcSQLException: Data conversion error converting "aced00057372000b6b6f746c696e2e50616972fa1b06813de78f780200024c000566697273747400124c6a6176612f6c616e672f4f626a6563743b4c00067365636f6e6471007e000178707372000e6a6176612e6c616e672e4c6f6e673b8be490cc8f23df0200014a000576616c7565787200106a6176612e6c616e672e4e756d62657286ac951d0b94e08b020000787000000000000000017371007e00030000000000000002"; SQL statement:
/* SELECT t FROM Thing t WHERE (t.foo, t.bar) IN ?1 */ select thing0_.id as id1_0_, thing0_.bar as bar2_0_, thing0_.foo as foo3_0_ from things thing0_ where (thing0_.foo , thing0_.bar) in (? , ?) [22018-197]

Caused by: java.lang.NumberFormatException: For input string: "aced00057372000b6b6f746c696e2e50616972fa1b06813de78f780200024c000566697273747400124c6a6176612f6c616e672f4f626a6563743b4c00067365636f6e6471007e000178707372000e6a6176612e6c616e672e4c6f6e673b8be490cc8f23df0200014a000576616c7565787200106a6176612e6c616e672e4e756d62657286ac951d0b94e08b020000787000000000000000017371007e00030000000000000002"

我想作为参数传递的列表元素没有正确插入到查询中。我该如何纠正这个问题?

当然,我可以像这样手动构建查询:

@Repository
class SecondThingRepository(private val entityManager: EntityManager) {
fun selectByFoosAndBars(foosAndBars: Iterable<Pair<Long, Long>>): Iterable<Thing> {
val pairsRepr = foosAndBars.joinToString(prefix = "(", postfix = ")") { "(${it.first}, '${it.second}')" }
val query: TypedQuery<Thing> = entityManager.createQuery("SELECT t FROM Thing t WHERE (t.foo, t.bar) IN $pairsRepr", Thing::class.java)
return query.resultList
}
}

不过这个好像note很好。

最佳答案

首先,提醒一句:并非所有数据库都支持 (t.foo, t.bar) IN ((1, 2), (3, 4)) 语法。使用它会使您的应用程序不可移植。

我假设 ListPair 的数量可以是任意的(如果不是,有一个更简单的解决方案涉及修改 IN 表达式到例如 IN (?1, ?2, ?3) 并更新查询方法以接受 List 类型的三个参数。我想那不是什么不过,您要的是)。

问题是 Hibernate 不知道如何将 Pair 类映射到数据库类型。似乎集合元素的类型解析逻辑与“外部”类型的解析逻辑不同,所以 listOf(listOf(1L, 2L), listOf(3L, 4L)) 不会也不行。

解决方案(请注意,这有点 hack)是引入一个 Hibernate 的 UserType 能够映射 Pair 对象并使用这个新创建的 PairType 用于 List 的元素。

首先,将以下类添加到您的项目中:

/* It is absolutely crucial that this class extend Pair. If the Pair class you're using
happens to be final, you will have to implement a Pair class yourself.
For an explanation of why this is required, have a look at SessionFactory.resolveParameterBindType()
and TypeResolver.heuristicType() methods */
public class PairType extends Pair<Long, Long> implements UserType {

public PairType(Long first, Long second) {
super(first, second);
}

public PairType() {
super(null, null);
}

@Override
public int[] sqlTypes() {
return new int[] {Types.ARRAY};
}

@Override
public Class returnedClass() {
return Pair.class;
}

@Override
public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session)
throws HibernateException, SQLException {
if (Objects.isNull(value)) {
st.setNull(index, Types.ARRAY);
} else {
final Pair pair = (Pair) value;
st.setArray(index, new Array() {


@Override
public Object getArray() throws SQLException {
// TODO Auto-generated method stub
return new Object[] {pair.getFirst(), pair.getSecond()};
}

...
//you can leave the rest of the autogenerated method stubs as they are

});
}
}

@Override
public Object deepCopy(Object value) throws HibernateException {
if (Objects.isNull(value)) {
return null;
}
return Pair.of(((Pair) value).getFirst(), ((Pair) value).getSecond());
}

@Override
public boolean isMutable() {
return false;
}

...
//you can leave the rest of the autogenerated method stubs as they are here as well

}

然后,将您的方法签名修改为:

selectByFoosAndBars(foosAndBars: Iterable<PairType>): Iterable<Thing>

注意:上述解决方案对我来说开箱即用,适用于 H2 数据库。你的旅费可能会改变。

关于hibernate - JPQL 和元组列表作为 SELECT IN 语句的参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55317347/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com