【问题标题】:JPA Native Query select and cast objectJPA Native Query 选择和转换对象
【发布时间】:2013-07-16 13:07:12
【问题描述】:

我有一个对象Admin,它扩展了User。默认情况下,这两个对象都在我的 Derby 数据库的表 User_ 中(包括来自 Admin 的字段)。通常我会像这样选择User

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root user= query.from(User.class);
Predicate predicateId = cb.equal(category.get("id"), id);
query.select(user).where(predicateId);
return em.createQuery(query).getSingleResult();

但是由于查询的复杂性,我使用的是这样的本机查询:

Query query = em.createNativeQuery("SELECT USER.* FROM USER_ AS USER WHERE ID = ?");
query.setParameter(1, id);
return (User) query.getSingleResult();

虽然这会引发强制转换异常。我认为这是由于 Admin 的任何字段造成的。

我的问题是,如何使用与第一个示例具有相同结果的本机查询选择 User(包括与 JPQL 查询返回的 @LOB@ManyToOne (等等)相同的值) )?

【问题讨论】:

  • 是什么让查询对于 JPQL 来说过于复杂?
  • @damo 这是一个缩短的例子,真正的查询包含许多子查询,SUMCOALESCENSECASE 等等。
  • 检查这个答案。它有完整的答案:stackoverflow.com/a/50365522/3073945

标签: java sql jpa jpql


【解决方案1】:

您可能想尝试以下方法之一:

  • 使用方法createNativeQuery(sqlString, resultClass)

    本机查询也可以使用EntityManager.createNativeQuery() API 动态定义。

    String sql = "SELECT USER.* FROM USER_ AS USER WHERE ID = ?";
    
    Query query = em.createNativeQuery(sql, User.class);
    query.setParameter(1, id);
    User user = (User) query.getSingleResult();
    
  • 使用注解@NamedNativeQuery

    本机查询通过@NamedNativeQuery@NamedNativeQueries 定义 注释,或&lt;named-native-query&gt; XML 元素。

    @NamedNativeQuery(
        name="complexQuery",
        query="SELECT USER.* FROM USER_ AS USER WHERE ID = ?",
        resultClass=User.class
    )
    public class User { ... }
    
    Query query = em.createNamedQuery("complexQuery", User.class);
    query.setParameter(1, id);
    User user = (User) query.getSingleResult();
    

您可以在优秀的开放书Java Persistence 中阅读更多内容(可在PDF 中找到)。

────────
注意:关于getSingleResult()的使用,请参阅Why you should never use getSingleResult() in JPA

【讨论】:

  • @PaulVargas 如果结果是整数而不是实体怎么办
  • @PaulVargas,我建议这样做: Integer val = (Integer) query.getSingleResult();但是,我通常建议不要使用 query.getSingleResult(),因为它会引发许多检查异常。如果您以列表的形式获取结果,则您可以不处理任何结果,也可以相应地处理多个结果。
  • 嘿,@乔。谢谢! -- 我继续使用query.getSingleResult(),因为 OP。 :)
【解决方案2】:

接受的答案不正确。

createNativeQuery 将始终返回 Query

public Query createNativeQuery(String sqlString, Class resultClass);

Query 上调用getResultList 返回List

List getResultList()

分配(或强制转换)List&lt;MyEntity&gt; 时,会产生未经检查的分配警告。

createQuery 将返回TypedQuery

public <T> TypedQuery<T> createQuery(String qlString, Class<T> resultClass);

TypedQuery 上调用getResultList 返回List&lt;X&gt;

List<X> getResultList();

这是正确输入的,不会给出警告。

对于createNativeQuery,使用ObjectMapper 似乎是摆脱警告的唯一方法。就个人而言,我选择隐藏警告,因为我认为这是图书馆的缺陷,而不是我应该担心的事情。

【讨论】:

    【解决方案3】:

    当您的本机查询基于联接时,在这种情况下,您可以将结果作为对象列表获取并进行处理。

    一个简单的例子。

    @Autowired
    EntityManager em;
    
        String nativeQuery = "select name,age from users where id=?";   
        Query query = em.createNativeQuery(nativeQuery);
        query.setParameter(1,id);
    
        List<Object[]> list = query.getResultList();
    
        for(Object[] q1 : list){
    
            String name = q1[0].toString();
            //..
            //do something more on 
         }
    

    【讨论】:

      【解决方案4】:

      请参考JPA : How to convert a native query result set to POJO class collection

      对于Postgres 9.4

      List<String> list = em.createNativeQuery("select cast(row_to_json(u) as text) from myschema.USER_ u WHERE ID = ?")
                         .setParameter(1, id).getResultList();
      
      User map = new ObjectMapper().readValue(list.get(0), User.class);
      

      【讨论】:

      • 哪种方式适合通过原生查询? 1) 使用 EntityManager 2) 使用 JpaRepository 接口
      【解决方案5】:

      首先创建一个模型POJO

      import javax.persistence.*;
      @Entity
      @Table(name = "sys_std_user")
      public class StdUser {
          @Id
          @GeneratedValue(strategy = GenerationType.AUTO)
          @Column(name = "class_id")
          public int classId;
          @Column(name = "user_name")
          public String userName;
          //getter,setter
      }
      

      控制器

      import com.example.demo.models.*;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.web.bind.annotation.GetMapping;
      import org.springframework.web.bind.annotation.RestController;
      import javax.persistence.EntityManager;
      import javax.persistence.EntityManagerFactory;
      import javax.persistence.PersistenceUnit;
      import java.util.List;
      
      @RestController
      public class HomeController {
          @PersistenceUnit
          private EntityManagerFactory emf;
      
          @GetMapping("/")
          public List<StdUser> actionIndex() {
              EntityManager em = emf.createEntityManager(); // Without parameter
              List<StdUser> arr_cust = (List<StdUser>)em
                      .createQuery("SELECT c FROM StdUser c")
                      .getResultList();
              return arr_cust;
          }
      
          @GetMapping("/paramter")
          public List actionJoin() {
              int id = 3;
              String userName = "Suresh Shrestha";
              EntityManager em = emf.createEntityManager(); // With parameter
              List arr_cust = em
                      .createQuery("SELECT c FROM StdUser c WHERE c.classId = :Id ANd c.userName = :UserName")
                      .setParameter("Id",id)
                      .setParameter("UserName",userName)
                      .getResultList();
              return arr_cust;
          }
      }
      

      【讨论】:

      • 在 REST 控制器中编写数据访问代码不是一个好习惯。将您的数据访问代码存储在 @Repository 类中。从 @Service 类调用您的存储库。从@RestController 访问@Service 类。
      猜你喜欢
      • 2020-01-06
      • 1970-01-01
      • 2016-08-18
      • 2013-03-24
      • 2019-02-19
      • 2022-11-18
      • 2014-03-21
      • 2014-09-02
      • 2014-10-14
      相关资源
      最近更新 更多