【问题标题】:How to Map a JPA create native query to projections如何将 JPA 创建本机查询映射到投影
【发布时间】:2020-11-05 13:14:55
【问题描述】:

我正在尝试使用 Spring Data JPA createNativeQuery 从 postgreSQL 数据库中获取计数。但是,查询返回的是 null 而不是实际值。

下面是 JPA createNativeQuery 语句:

Query q = entityManager.createNativeQuery("SELECT null AS id, 
count(case when (IS_QUERIED = false AND SUBMITTED = true) 
then 1 else null end) AS pending,  
count(case when ( (SUBMITTED = true)) then 1 else null end) AS submitted, 
count(*) AS totalApplications FROM ANNUAL_RETURNS ", AnnualReturn.class);
//Note: AnnualReturn is the name of the @Entity class

List <AnnualReturn> countList=q.getResultList();

return countList;

我需要帮助映射我的查询中的“已提交”、“待处理”和“totalApplications”实例,以返回如下结果。

预期结果是:

"data": {
        "totalApplications": 2000,
        "submitted": 560,
        "pending": 60,
    }

结果得到:

{
    "data": [
        null
    ]

我将不胜感激。

【问题讨论】:

  • 我想你并不真的想要一个类型化的查询,看起来提交是一个布尔字段,你想在提交时返回一个数字。如果没有类参数,您可能会得到一个带有值的 Object[] 列表
  • 我想将计数作为 Long 数据类型返回,其中提交的是“true”。因此,我希望创建一个映射/翻译层,因为单独的实体无法产生所需的结果。我将不胜感激。
  • SELECT null AS id 获取 null 作为 id 的任何原因?您可以更新它以获取实际值select id 或简单地执行select 0 as id 吗?

标签: java postgresql spring-boot spring-data-jpa


【解决方案1】:

我不知道为什么,但我相信 SELECT null AS id 导致获取空记录。

如果您不想获取 id,则可以将投影与自定义 RowMapper 或 DTO 投影一起使用。

见下文:

@Entity
@Data
@ToString
class A {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id;
    String name;
    int age;

    public A() {
    }
    public A(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

interface ARepo extends JpaRepositoryImplementation<A, Long> {
}

@Component
@RequiredArgsConstructor
class Init {
    final ARepo repo;
    final EntityManager em;

    @EventListener
    public void init(ContextRefreshedEvent evt) {
        repo.save(new A("XX", 5));
        repo.save(new A("ZZ", 6));
        repo.save(new A("AA", 11));
        repo.save(new A("AB", 12));

        Query q = em.createNativeQuery("select 0 as id, a.name as name, a.age as age  from A a where a.total > 10 ", A.class);

        System.out.println(q.getResultList()); //fetches AA and BB 
        //if you change the query to select null as id  it would return [ null, null]
          
    }
}

【讨论】:

  • 感谢您的回答。如果我只想要计数而不是实际名称怎么办? “选择 0 作为 id,count(a.name) 作为 nameCount,count(a.age) 作为 ageCount from A a where a.total > 10”,A.class)...我将如何映射 nameCount 和 ageCount,因为这些计数变量不在我的@Entity 类中?
  • docs.spring.io/spring-data/jpa/docs/current/reference/html/…。如果您可以在 spring data jpa 查询中表示您的查询,那么它会自动支持投影到接口、自定义 dto 或实体。如果要编写自己的自定义查询,则需要提供字段的手动映射。如果您的查询是复杂的 SQL 查询,那么我建议您使用 @JdbcTemplate 以便您可以轻松地提供 RowMapper 以将结果绑定到 DTO
【解决方案2】:

你不应该在这里使用类型化查询,试试类似

Query q = entityManager.createNativeQuery("SELECT " +
  " COALESCE(sum(case when (IS_QUERIED = false AND SUBMITTED = true) " +
  " then AMOUNT else 0 end),0) AS pending,  " +
  " COALESCE(sum(case when ( (SUBMITTED = true)) then 1 else 0 end),0) AS submitted, " +
  " COALESCE(sum(AMOUNT),0) AS totalApplications FROM ANNUAL_RETURNS ");

List <Object[]> countList=q.getResultList();

Object[] obj = countList.get(0);

for (Object value : obj) {
     System.out.println(value);
}
long pending = ((BigInteger)obj[0]).longValue();
long submitted = ((BigInteger)obj[1]).longValue();
long total = ((BigInteger)obj[2]).longValue();


return ...; // prepare the values to your convinience

编辑

将 null 更改为 0 以避免结果中可能出现 null 值

编辑

修复空表上的 NPE

【讨论】:

  • 它抛出了一个编译错误,说我必须将 obj 转换为 long。在为不同的值转换 long pending=(long) obj[0] 之后,它编译了,但是当我发送请求时,它在运行时抛出了一个空指针错误,从它到达第一行:long pending=(long)对象[0]
  • 您像我一样删除了“null AS id”吗?请记录 obj 并显示其内容
  • 当我删除列 id 时,它在运行时抛出错误它说::在这个 ResultSet 中找不到列名 id。这就是为什么我将那个子句添加到 select 语句中
  • 您是否也删除了 AnnualReturn.class?我认为没有其他理由需要名为“id”的列
  • 最初我没有删除 AnnualReturn.class,但是当我删除它时,应用程序在 long pending=(long) obj[0] 行中断并出现空指针异常
猜你喜欢
  • 2021-01-08
  • 1970-01-01
  • 1970-01-01
  • 2014-07-06
  • 2017-01-02
  • 2014-06-25
  • 1970-01-01
  • 2019-07-25
  • 2020-06-23
相关资源
最近更新 更多