Spring Boot中如何为JPA查询添加非实体声明的计算属性
解决JPA查询原实体全字段加计算列的问题
为什么子类扩展的方法行不通?
你用子类扩展实体并加@Entity的思路本身就不适合这个场景——JPA的实体继承是用来映射数据库表的继承关系的,而你的需求是临时添加计算列,不是要映射新的表结构。就算给子类加了@Entity,JPA会把它当成独立实体,要么找不到对应的表,要么实体扫描配置没覆盖到,自然会报Unknown Entity。
不用新建Repository的两种可行方案
方案1:JPQL构造函数查询(推荐,代码简洁)
直接在原实体类里新增一个包含计算列参数的构造函数,然后在原Repository中用@Query写JPQL语句,通过构造函数把原字段和计算列一起返回。
举个例子,假设你的原实体是Order:
@Entity @Table(name = "orders") public class Order { // 原实体的所有字段,比如: @Id private Long id; private String orderNo; private LocalDateTime createTime; private BigDecimal price; private Integer quantity; private Integer discount; // 比如折扣是整数,10代表10% // 新增包含计算列的构造函数 public Order(Long id, String orderNo, LocalDateTime createTime, BigDecimal price, Integer quantity, Integer discount, BigDecimal totalAmount, BigDecimal discountRate) { // 给原字段赋值 this.id = id; this.orderNo = orderNo; this.createTime = createTime; this.price = price; this.quantity = quantity; this.discount = discount; // 给计算列赋值 this.totalAmount = totalAmount; this.discountRate = discountRate; } // 必须保留JPA要求的无参构造函数 public Order() {} // 新增计算列的getter/setter,不用加@Column注解 private BigDecimal totalAmount; private BigDecimal discountRate; public BigDecimal getTotalAmount() { return totalAmount; } public void setTotalAmount(BigDecimal totalAmount) { this.totalAmount = totalAmount; } public BigDecimal getDiscountRate() { return discountRate; } public void setDiscountRate(BigDecimal discountRate) { this.discountRate = discountRate; } }
然后在原OrderRepository里新增查询方法:
public interface OrderRepository extends JpaRepository<Order, Long> { @Query("SELECT new com.yourpackage.entity.Order(" + "o.id, o.orderNo, o.createTime, o.price, o.quantity, o.discount, " + "(o.price * o.quantity) as totalAmount, " + "(o.discount / 100.0) as discountRate" + ") FROM Order o WHERE o.id = ?1") Order findOrderWithCalculations(Long id); }
注意:JPQL里的构造函数要写全类名,参数顺序必须和构造函数完全一致。
方案2:原生SQL + @SqlResultSetMapping(适合复杂计算场景)
如果计算逻辑用原生SQL更方便,就用这个方法:在原实体上定义结果集映射,指定原生SQL返回的列如何映射到实体的属性,包括新增的计算列。
还是以Order为例,先在实体类上加映射配置:
@Entity @Table(name = "orders") // 定义结果集映射 @SqlResultSetMapping( name = "OrderWithCalculationsMapping", classes = @ConstructorResult( targetClass = Order.class, columns = { @ColumnResult(name = "id", type = Long.class), @ColumnResult(name = "order_no", type = String.class), @ColumnResult(name = "create_time", type = LocalDateTime.class), @ColumnResult(name = "price", type = BigDecimal.class), @ColumnResult(name = "quantity", type = Integer.class), @ColumnResult(name = "discount", type = Integer.class), @ColumnResult(name = "total_amount", type = BigDecimal.class), @ColumnResult(name = "discount_rate", type = BigDecimal.class) } ) ) public class Order { // 原字段、构造函数、计算列属性和方案1一致,此处省略重复代码 }
然后在原Repository里写原生SQL查询,指定使用上面定义的映射:
public interface OrderRepository extends JpaRepository<Order, Long> { @Query(value = "SELECT " + "id, order_no, create_time, price, quantity, discount, " + "(price * quantity) as total_amount, " + "(discount / 100.0) as discount_rate " + "FROM orders WHERE id = ?1", nativeQuery = true, resultSetMapping = "OrderWithCalculationsMapping") Order findOrderWithCalculations(Long id); }
这两种方法都不用新建Repository,直接复用原有的,而且能返回包含所有原字段和计算列的实体对象,完美符合你的需求。
内容的提问来源于stack exchange,提问作者Flarosa
相关产品推荐
相关产品推荐

