You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用@Transient注解后无法获取原生查询生成的theDate字段值

解决JPA原生SQL查询中@Transient字段返回NULL的问题

问题根源

@Transient注解的作用是告诉JPA该字段不属于数据库表的列,不会参与实体与数据库的映射过程——所以即使你的原生SQL返回了theDate列,JPA也会自动忽略这个字段的赋值,导致调用getTheDate()时返回NULL。

三种可行解决方案

1. 用@Column(insertable=false, updatable=false)替代@Transient

这是最简单的方案:既保留“不写入数据库”的需求,又让JPA识别该字段为查询结果的映射列。

修改实体类的theDate字段:

@Getter
@Setter
@ToString
@Entity
@Table(name = "amortization_schedules")
public class AmortizationSchedule {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    // 替换@Transient,设置insertable和updatable为false,避免写入数据库
    @Column(name = "theDate", insertable = false, updatable = false)
    private Date theDate;

    // 其他字段省略
}
  • 原理:insertable=false和updatable=false会让JPA在执行INSERT/UPDATE操作时忽略该字段,同时保留查询结果的映射逻辑。
  • 注意:@Column的name属性必须和原生SQL返回的列名完全一致(这里是theDate)。

2. 使用DTO/接口投影分离查询结果

如果不想修改原实体类,可以创建专门的DTO或投影接口来接收查询结果:

方式A:DTO构造函数投影

先定义包含所有查询字段的DTO:

@Getter
@Setter
public class AmortizationScheduleDTO {
    private Date theDate;
    private Long id;
    // 复制原实体类中需要的其他字段,顺序要和SQL查询列的顺序一致
    private BigDecimal interestPayment;
    private BigDecimal interestRate;
    // ... 其他字段
}

然后修改Repository方法(注意nativeQuery要设为false,改用JPQL):

@Query(value = "SELECT new com.yourpackage.AmortizationScheduleDTO(" +
        "tab.theDate, asl.id, asl.interest_payment, asl.interest_rate, ...) " + // 按DTO构造函数顺序罗列所有列
        "FROM GENERATE_SERIES(" +
        "   (SELECT MIN(ams.date) FROM AmortizationSchedule ams)," +
        "   (SELECT MAX(ams.date) + INTERVAL '1' MONTH FROM AmortizationSchedule ams)," +
        "   '1 MONTH'" +
        ") AS tab(theDate) " +
        "FULL JOIN AmortizationSchedule asl ON FUNCTION('to_char', tab.theDate, 'yyyy-mm') = FUNCTION('to_char', asl.date, 'yyyy-mm')")
List<AmortizationScheduleDTO> findAllByDate();

方式B:接口投影(更适合原生SQL)

定义一个投影接口,方法名与查询返回的列名对应:

public interface AmortizationScheduleProjection {
    Date getTheDate();
    Long getId();
    BigDecimal getInterestPayment(); // 对应SQL中的interest_payment,JPA会自动处理下划线转驼峰
    BigDecimal getInterestRate();
    // ... 其他字段对应的get方法
}

修改Repository方法:

@Query(value = "SELECT " +
        "  theDate, asl.id, asl.interest_payment, asl.interest_rate, ... " + // 原SQL不变
        "FROM ...", nativeQuery = true)
List<AmortizationScheduleProjection> findAllByDate();
  • 原理:JPA会动态生成代理类实现该接口,自动将查询结果映射到接口方法的返回值。

3. 用@SqlResultSetMapping自定义结果映射

如果必须使用原实体类且不想修改字段注解,可以通过结果集映射告诉JPA如何处理theDate字段:

在实体类上添加映射定义:

@SqlResultSetMapping(
    name = "AmortizationScheduleWithTheDate",
    entities = @EntityResult(
        entityClass = AmortizationSchedule.class,
        fields = {
            @FieldResult(name = "id", column = "id"),
            @FieldResult(name = "theDate", column = "theDate"),
            // 必须逐一映射所有需要的字段,name是实体属性名,column是SQL返回的列名
            @FieldResult(name = "interestPayment", column = "interest_payment"),
            @FieldResult(name = "interestRate", column = "interest_rate"),
            // ... 其他所有字段
        }
    )
)
@Getter
@Setter
@ToString
@Entity
@Table(name = "amortization_schedules")
public class AmortizationSchedule {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    @Transient
    private Date theDate;

    // 其他字段省略
}

然后在Repository的@Query中指定该映射:

@Query(value = "SELECT ...", nativeQuery = true, resultSetMapping = "AmortizationScheduleWithTheDate")
List<AmortizationSchedule> findAllByDate();
  • 原理:通过自定义映射规则,强制JPA将SQL返回的theDate列赋值给实体的theDate属性,即使它被标记为@Transient。

内容的提问来源于stack exchange,提问作者Alexander

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 20:05:26