将数组参数绑定到本机查询

时间:2016-10-13 12:26:58

标签: java sql postgresql hibernate jpa

我的表格 product_spec_entry 包含以下列:

  • product_spec_id
  • commodity_spec_id

一个 product_spec_id 可能是多个 commodity_spec_id ,例如:

|product_spec_id | commodity_spec_id|
|----------------|------------------|
|1683            |1681              |
|1692            |1693              |
|1692            |1681              |
|1692            |1687              |
|1692            |1864              |
|1860            |1681              |
|1868            |1681              |
|1868            |1864              |

我想让所有 product_spec_id 全部 commodity_spec_id 作为参数传递。

我写了下一个查询:

SELECT ps.product_spec_id, commodities
FROM (
       SELECT
         product_spec_id,
         array_agg(commodity_spec_id) AS commodities
       FROM system.product_spec_entry
       GROUP BY product_spec_id) ps
WHERE Cast(ARRAY [1681, 1864] as BIGINT[]) <@ Cast(ps.commodities as BIGINT[]);

工作正常,并返回预期结果:

  

product_spec_id = 1692,1868

我尝试将此查询用于JPA本机查询:

String query = "SELECT ps.product_spec_id " +
                "FROM ( " +
                "       SELECT " +
                "         product_spec_id, " +
                "         array_agg(commodity_spec_id) AS commodities " +
                "       FROM system.product_spec_entry " +
                "       GROUP BY product_spec_id) ps " +
                "WHERE CAST(ARRAY[:commoditySpecIds] AS BIGINT[]) <@ CAST(ps.commodities AS BIGINT[])";
List<Long> commoditySpecsIds = commoditySpecs.stream().map(Spec::getId).collect(Collectors.toList());

List<BigInteger> productSpecIds = em.createNativeQuery(query).setParameter("commoditySpecIds", commoditySpecsIds)
                .getResultList();

它不起作用,因为我获得了记录数组(ARRAY[(1692, 1868)])而不是bigint数组(ARRAY[1692, 1868]

如何将数组参数绑定到我的查询?可能是我可以使用更简单的查询。

3 个答案:

答案 0 :(得分:2)

从SQL中忽略array[...]

WHERE CAST(:commoditySpecIds AS BIGINT[])

然后将ID列表作为字符串传递,如下所示:

"{1,2,3,4}"

列表的默认toString()通常会返回类似于:"[1,2,3]"的内容,因此您可以执行以下操作:

String literal = commoditySpecsIds.toString();
literal = "{" + literal.substring(1,literal.length() - 1) + "};

然后将其传递给您的混淆层:

setParameter("commoditySpecIds", literal)

答案 1 :(得分:0)

您的案例的其他approcah是打开JPA提供程序会话并使用JPA提供程序API中的某些方法。

如果您正在使用休眠,则可以执行以下操作:

// unwrap hibenate session
final Session hibernateSession = em.unwrap(Session.class);

// create you SQL query in hibernate style
final SQLQuery sqlQuery = hibernateSession.createSQLQuery(sql);

然后使用hibernate API

设置参数
final Type customType = new CustomType(new ArrayTypeUser());
sqlQuery.setParameter("commoditySpecIds", value, customType);

其中“ArrayTypeUser”是将PostgresSQL数组类型映射到Java数组类型的自定义类型。

这不是最佳解决方案,但由于您已经在使用本机查询,因此对于此特定情况,最佳解决方案是跳过JPA标准API并使用JPA提供API。

答案 2 :(得分:0)

I'm exactly in the same situation. Hope @VladMihalcea can help us

Edit

I figure it out to do it with JPA. After reading the impementation of setParameter, i discovered something similar to UserType, the TypedParameterValue.

When you use

setParameter("commoditySpecIds", new TypedParameterValue(IntArrayType.INSTANCE, commoditySpecsIds))

Where IntArrayType.INSTANCE come from "hibernate-types" librairy provided by Vlad Mihalcea. Be carefull, the "commoditySpecsIds" must be an array, not a Collection.

Hope that helps