如何以类型安全的方式运行 Hibernate NativeQuery 而不是返回 Object[]
Posted
技术标签:
【中文标题】如何以类型安全的方式运行 Hibernate NativeQuery 而不是返回 Object[]【英文标题】:How to run a Hibernate NativeQuery in a type-safe manner instead of returning an Object[] 【发布时间】:2018-09-06 09:04:27 【问题描述】:我正在将应用程序从 Hibernate 4.x 迁移到 Hibernate 5.3.6。应用程序有这样的查询:
SQLQuery query = getSession().createSQLQuery("SELECT a.a, a.b, a.c FROM aTable");
由于 createSQLQuery 方法已被弃用,我首先将方法调用替换为 Hibernate Javadoc 中建议的替代方法,即使用 createNativeQuery:
NativeQuery query = getSession().createNativeQuery("SELECT a.a, a.b, a.c FROM aTable");
问题在于它会产生编译器警告“NativeQuery 是原始类型。对泛型类型 NativeQuery 的引用应该被参数化”。此外,我当然想从类型化查询中受益,因为它们是可用的。所以我将查询更改为
NativeQuery<Object[]> query = getSession().createNativeQuery("SELECT a.a, a.b, a.c FROM aTable", Object[].class);
现在的问题是执行查询
List<Object[]> retList = query.list();
产生错误
javax.persistence.PersistenceException: org.hibernate.MappingException:未知实体:[Ljava.lang.Object;
研究问题seems to indicate 在使用类型化的本机查询时无法使用非映射实体(这似乎是一个严重且不必要的限制,但我在这里离题了)。
问题:有没有办法使用 Hibernate 执行返回一个对象数组的本机 SQL 查询而不产生编译器警告同时实现类型安全?如果没有,是否有任何明智的选择?
【问题讨论】:
【参考方案1】:这种投影有更好的替代方案,而不是默认的Object[]
。
您可以使用 JPA javax.persistence.Tuple
结果集,因为 Hibernate ORM 5.2.11 适用于本机 SQL:
List<Tuple> postDTOs = entityManager
.createNativeQuery(
"SELECT " +
" p.id AS id, " +
" p.title AS title " +
"FROM Post p " +
"WHERE p.created_on > :fromTimestamp", Tuple.class)
.setParameter( "fromTimestamp", Timestamp.from(
LocalDateTime.of( 2016, 1, 1, 0, 0, 0 )
.toInstant( ZoneOffset.UTC ) ))
.getResultList();
您可以使用特定于 Hibernate 的 ResultTransformer
,它允许您构建非常复杂的 DTO 结构(例如图形):
List postDTOs = entityManager
.createNativeQuery(
"select " +
" p.id as \"id\", " +
" p.title as \"title\" " +
"from Post p " +
"where p.created_on > :fromTimestamp")
.setParameter( "fromTimestamp", Timestamp.from(
LocalDateTime.of( 2016, 1, 1, 0, 0, 0 ).toInstant( ZoneOffset.UTC ) ))
.unwrap( org.hibernate.query.NativeQuery.class )
.setResultTransformer( Transformers.aliasToBean( PostDTO.class ) )
.getResultList();
您也可以使用命名的原生查询:
List<PostDTO> postDTOs = entityManager
.createNamedQuery("PostDTO")
.setParameter( "fromTimestamp", Timestamp.from(
LocalDateTime.of( 2016, 1, 1, 0, 0, 0 )
.toInstant( ZoneOffset.UTC ) ))
.getResultList();
PostDTO
查询是一个命名的本机 SQL 查询,如下所示:
@NamedNativeQuery(
name = "PostDTO",
query =
"SELECT " +
" p.id AS id, " +
" p.title AS title " +
"FROM Post p " +
"WHERE p.created_on > :fromTimestamp",
resultSetMapping = "PostDTO"
)
@SqlResultSetMapping(
name = "PostDTO",
classes = @ConstructorResult(
targetClass = PostDTO.class,
columns =
@ColumnResult(name = "id"),
@ColumnResult(name = "title")
)
)
很酷,对吧?
【讨论】:
【参考方案2】:只需通过调用创建它
createNativeQuery("SELECT a.a, a.b, a.c FROM aTable");
它会默认返回一行Object[]
。
该警告与您的情况无关,因此请取消它。
【讨论】:
【参考方案3】:如果没有,是否有任何明智的选择?
由于您要求替代方案(可能包括不涉及 Hibernate 的替代方案),我认为在这里提及jOOQ 是公平的,您可以在其中编写您的查询:
Result<Record3<Integer, String, Long>> result =
ctx.select(A.A, A.B, A.C)
.from(A)
.fetch();
只用var
就可以避免表示泛型:
var result =
ctx.select(A.A, A.B, A.C)
.from(A)
.fetch();
或者,如果您想继续使用查询的字符串版本,您可以使用转换来降低类型安全性:
Result<?> result = ctx.fetch("SELECT a.a, a.b, a.c FROM aTable");
for (Record record : result)
int a = record.get(0, int.class);
String b = record.get(1, String.class);
long c = record.get(2, long.class);
或者,您使用 DTO / POJO
class POJO
int a;
String b;
long c;
List<POJO> list = ctx.fetch("SELECT a.a, a.b, a.c FROM aTable").into(POJO.class);
如果您的结果是实体,you can execute that query easily on an EntityManager
, using jOOQ and Hibernate together
(免责声明,我在 jOOQ 背后的公司工作)
【讨论】:
以上是关于如何以类型安全的方式运行 Hibernate NativeQuery 而不是返回 Object[]的主要内容,如果未能解决你的问题,请参考以下文章
如何以编程方式将“@Cascade”添加到 POJO 字段(Spring mvc + Hibernate)