【问题标题】:JPA/Hibernate + HQL/JPQL: select DTO with BigDecimal parameterJPA/Hibernate + HQL/JPQL:使用 BigDecimal 参数选择 DTO
【发布时间】:2012-09-26 09:06:07
【问题描述】:

我们使用 JPA 和 hibernate 作为实现。假设我有以下 DTO:

public class SupplierInfoDto{
   private String supplierName;
   private BigDecimal remainingFinances;

   public SupplierInfoDto(String supplierName, BigDecimal remainingFinances){
       this.supplierName = supplierName;
       this.remainingFinances = remainingFinances;
   }

   // getters / setters
}

我似乎无法休眠以正确找到此构造函数。我首先尝试了以下查询(模型比这更复杂,我最终需要获取一些聚合(不是直接在实体上),这就是为什么我要获取 DTO 而不是实体):

SELECT NEW com.company.dto.SupplierInfoDto(s.name, f.remaining)
FROM Supplier s INNER JOIN Finances f
WHERE s.id = :SupplierId

但是,我得到了 org.hibernate.hql.ast.QuerySyntaxException: Unable to locate appropriate constructor on class 异常。

我从中选择的 remaining 列在 MSSQL 中存储为浮点数(我知道,我知道钱永远不应该存储为浮点数,但这是一个现有系统,我不能只更改此数据类型)..

作为测试,我尝试了以下查询,但有与上述相同的异常:

SELECT NEW com.company.dto.SupplierInfoDto(s.name, NEW java.math.BigDecimal(10))
FROM Supplier s
WHERE s.id = :SupplierId

所以我的问题是:如何让 hibernate/JPA 为上述两个查询找到合适的构造函数?

更新:remaining 属性在 Finances 实体上属于 double 类型(不是我的决定)。

【问题讨论】:

  • 剩余的Finance字段/列是什么类型?
  • 你可以尝试包含一个默认构造函数吗?
  • @SatheeshKC 我可以,但我认为这行不通,因为休眠可能无法确定哪个构造函数参数属于哪个 getter/setter 对。如果我添加一个没有 BigDecimal 参数的构造函数,并从查询中删除该参数,它就可以正常工作。所以我认为这是一个类型问题。

标签: java hibernate jpa hql jpql


【解决方案1】:

试试这个:

CAST(f.remaining AS big_decimal)

根据https://docs.jboss.org/hibernate/orm/3.5/reference/en/html/queryhql.html

cast(... as ...),其中第二个参数是 Hibernate 类型 的名称,extract(... from ... ) 如果底层数据库支持 ANSI cast() 和 extract()

还有https://docs.jboss.org/hibernate/orm/3.5/api/org/hibernate/type/package-summary.html

BigDecimalType big_decimal:将 SQL NUMERIC 映射到 java.math.BigDecimal 的类型

【讨论】:

    【解决方案2】:

    为什么不使用 java.lang.Number 作为构造函数参数,并根据参数的 .floatValue() / doubleValue() 创建 BigDecimal 字段。

    【讨论】:

      【解决方案3】:

      我不确定为什么 BigDecimal ctor 没有被识别,但你可以重载你的构造函数

      如果你有

      public SupplierInfoDto(String s, Double d) {
         this(s, new BigDecimal(String.valueOf(d)));
      }
      
      public SupplierInfoDto(String s, BigDecimal bd) {
         //set fields
      }
      

      并不是说如果您使用 BigDecimal double 构造函数,数字是基于 double 的,因此仍然可能存在舍入错误。通常最好使用 BigDecimal string contstrctor

      例如

      new BigDecimal("0.1")
      

      更精确
      new BigDecimal(0.1d)
      

      This 文章解释了这一点

      【讨论】:

      • 是的,这应该可以。但正如您所指出的,舍入问题仍然存在,这是我试图避免的。我可以在 HQL 中转换/转换为另一种类型吗?
      • @MortenJacobsen 刚刚更新了答案以具有 String.valueOf(...) 这可能会有所帮助,尽管如果数据库中的源不好,你不能指望 java 在不应用一些规则的情况下会很好跨度>
      【解决方案4】:
      class named X  with a constructor that takes two parameters. The types of the parameters from the SELECT clause must match the signature defined in the class.
      
      Syntax for the SELECT clause:
      
      select_clause ::= SELECT [DISTINCT] select_expression
          {, select_expression}*
          select_expression ::=
          single_valued_path_expression |
          aggregate_expression |
          identification_variable |
          OBJECT(identification_variable) |
          constructor_expression
          constructor_expression ::=
          NEW constructor_name ( constructor_item {, constructor_item}* )
          constructor_item ::= single_valued_path_expression |
          aggregate_expression
          aggregate_expression ::=
          { AVG | MAX | MIN | SUM }
          ([DISTINCT] state_field_path_expression) |
          COUNT ([DISTINCT] identification_variable |
          state_field_path_expression |
          single_valued_association_path_expression)
      

      【讨论】:

      • 仅供参考,JPA 2.0 规范在第 4.8 节 SELECT 子句中讨论了这一点
      • 这没什么用。因为查询通过了,所以语法不是问题。我想知道为什么休眠不能正确拾取 BigDecimal 参数。
      猜你喜欢
      • 2010-09-16
      • 1970-01-01
      • 2020-07-18
      • 1970-01-01
      • 2021-09-05
      • 2023-01-10
      • 2013-07-13
      • 1970-01-01
      相关资源
      最近更新 更多