首页 > 解决方案 > 如何以类型安全的方式运行 Hibernate NativeQuery 而不是返回 Object[]

问题描述

我正在将应用程序从 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;

研究这个问题似乎表明在使用类型化的本机查询时不可能使用非映射实体(这似乎是一个严重且不必要的限制,但我在这里离题了)。

问题:有什么方法可以执行原生 SQL 查询,使用 Hibernate 返回一个对象数组,而不会产生编译器警告,同时实现类型安全?如果没有,是否有任何明智的选择?

标签: javasqlhibernatejpaorm

解决方案


这种预测有更好的替代方案,而不是默认的Object[].

  1. 您可以使用 JPAjavax.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();
    
  2. 您可以使用 Hibernate-specific 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();
    
  3. 您还可以使用命名的本机查询:

     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")
             }
         )
     )
    

很酷,对吧?


推荐阅读