Spring Boot JPA实体建表排除指定字段,不用@Transient通过原生存储过程取值
Spring Boot JPA 实体字段适配方案:排除建表但保留结果集映射能力
核心需求场景
在Spring Boot JPA开发过程中,经常会遇到这类典型矛盾:
- 用
@Entity注解标记实体类映射数据库表时,希望Hibernate自动生成表结构的流程跳过某个特定字段 - 不能直接使用JPA提供的
@Transient注解——这个注解会让Hibernate把目标属性完全排除在持久化元数据外,不管是原生查询、还是存储过程返回的对应字段值,都无法映射到实体属性上 - 需要在JPA仓库接口调用原生SQL存储过程时,把存储过程返回的对应字段值正常映射到实体属性,完全不需要这个字段参与数据库表的实际创建
常见踩坑点
当前代码里的写法存在明显问题:
@Column(name = "no_of_cnt",insertable = false,updatable = false) private Integer noOfCnt;
这种写法虽然让该字段失去了插入、更新的权限,但是Hibernate在自动生成表结构的时候,依然会把no_of_cnt作为普通物理列创建到trn_cancelled_paid_amount表中,和不想在表里存这个字段的初衷完全矛盾。
如果直接给该字段加上@Transient注解,又会出现存储过程返回的no_of_cnt列值完全无法映射、拿到的对象对应属性始终为null的问题。
正确实现方案
直接使用Hibernate提供的@Formula注解替换原来的@Column配置,就能完美匹配所有需求。修改后的实体类核心代码如下:
import org.hibernate.annotations.Formula; @Data @Entity @Table(name = "trn_cancelled_paid_amount") public class TrnCancelledPaidAmount extends BaseModel { @Column(unique = true) private Long transactionId; @Column(unique = true) private String trackingId; private Double netAmount; private String orderId; // 替换原来的@Column注解,用Formula定义派生属性 @Formula("1 as no_of_cnt") private Integer noOfCnt; }
方案的适配逻辑说明
- 完全跳过表结构创建:
@Formula标记的属于动态派生属性,不是表的物理列,Hibernate在schema导出、自动建表的流程中,会直接跳过这类属性,不会在目标表里生成对应列 - 保留完整的结果集映射能力:该属性被纳入Hibernate的持久化元数据,不会像
@Transient标记的属性一样被直接忽略。当你调用原生SQL存储过程、返回的结果集包含no_of_cnt列时,Hibernate会自动匹配列名,把返回值映射到noOfCnt属性上 - 不会影响普通实体查询:
@Formula配置的常量表达式会被自动拼接到Hibernate生成的通用select语句中,因为用的是常量值,根本不依赖实际表的列结构,即使表里没有对应列,也不会触发SQL语法错误 - 原生查询的返回值优先级更高:当你使用原生SQL/存储过程查询时,只要结果集存在匹配的列名,Hibernate会优先读取返回集的列值,不会被Formula配置的常量值覆盖
对应的仓库接口代码不需要做任何调整,保持原来的原生存储过程调用写法即可:
public interface TrnCancelledPaidAmountRepo extends JpaRepository<TrnCancelledPaidAmount,Integer> { @Query(nativeQuery = true,value = "call cogent_fees_collection.proc_fees_cancelled( :#{#feesCancelledDataRequest.instId}, :#{#feesCancelledDataRequest.progTypeId}, :#{#feesCancelledDataRequest.progId}, :#{#feesCancelledDataRequest.sem}, :#{#feesCancelledDataRequest.termId}, :#{#feesCancelledDataRequest.feePayType}, :#{#feesCancelledDataRequest.isGrpBy})") List<TrnCancelledPaidAmount> callingProcFeesCancelled(@Param("feesCancelledDataRequest")FeesCancelledDataRequest feesCancelledDataRequest); }
注意:
@Formula表达式里的列别名必须和实体属性映射的数据库列名保持一致,也就是示例里的no_of_cnt,这样才能保证结果集列名匹配、值正常映射。
内容的提问来源于stack exchange,提问作者bhavya
相关产品推荐
相关产品推荐

