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

@Formula注解致RepositoryItemWriter保存实体报错,需读取时填充字段

解决Spring Batch中@Formula导致的保存SQL异常问题

问题根源

你在TransactionFileEod实体上添加的@Formula("select get_data(cardNo)")会被JPA解析为持久化字段的一部分,在执行INSERT(保存)操作时,这个select表达式会被错误地嵌入到INSERT语句的列列表中,触发SQL语法错误。

可行解决方案

方案1:@Transient + ItemProcessor(读取阶段处理)

  • 将decryptedCardNo标记为@Transient,让JPA在保存时忽略该字段
  • 在Spring Batch的读取步骤后添加ItemProcessor,专门调用数据库函数填充解密卡号:
// 实体类修改
@Entity
public class TransactionFileEod {
    // 原有字段
    private String cardNo;

    @Transient
    private String decryptedCardNo;

    // getter/setter
}

// 自定义ItemProcessor
public class TransactionDecryptProcessor implements ItemProcessor<TransactionFileEod, TransactionFileEod> {
    @Autowired
    private EntityManager entityManager;

    @Override
    public TransactionFileEod process(TransactionFileEod item) throws Exception {
        String decryptedCard = (String) entityManager.createNativeQuery("select get_data(:cardNo)")
                .setParameter("cardNo", item.getCardNo())
                .getSingleResult();
        item.setDecryptedCardNo(decryptedCard);
        return item;
    }
}

// 读取步骤配置
@Bean
public Step readTransactionStep(StepBuilderFactory stepBuilderFactory, 
                                RepositoryItemReader<TransactionFileEod> reader) {
    return stepBuilderFactory.get("readTransactionStep")
            .<TransactionFileEod, TransactionFileEod>chunk(10)
            .reader(reader)
            .processor(new TransactionDecryptProcessor())
            .build();
}

方案2:@PostLoad注解(实体加载时自动填充)

  • 同样将decryptedCardNo标记为@Transient,添加@PostLoad注解的方法,在实体从数据库加载后自动调用函数填充:
@Entity
public class TransactionFileEod {
    private String cardNo;

    @Transient
    private String decryptedCardNo;

    @Autowired
    private TransactionFileEodRepository repository;

    @PostLoad
    public void loadDecryptedCard() {
        this.decryptedCardNo = repository.getDecryptedCardNo(this.cardNo);
    }

    // getter/setter
}

// 仓库接口添加自定义查询方法
public interface TransactionFileEodRepository extends JpaRepository<TransactionFileEod, Long> {
    @Query(value = "select get_data(:cardNo)", nativeQuery = true)
    String getDecryptedCardNo(@Param("cardNo") String cardNo);
}

方案3:拆分保存/读取实体(DTO分离)

  • 创建两个类:一个用于保存的TransactionFileEodEntity(仅包含需要持久化的字段),一个用于读取的TransactionFileEodDto(包含解密字段,用@Formula):
// 保存用实体
@Entity
@Table(name = "transaction_file_eod")
public class TransactionFileEodEntity {
    private String cardNo;
    // 其他需要保存的字段、getter/setter
}

// 读取用实体
@Entity
@Table(name = "transaction_file_eod")
public class TransactionFileEodDto {
    private String cardNo;

    @Formula("get_data(cardNo)") // 此处无需select,直接写函数调用
    private String decryptedCardNo;

    // getter/setter
}

// 读取步骤配置
@Bean
public RepositoryItemReader<TransactionFileEodDto> readDtoReader(TransactionFileEodDtoRepository repository) {
    RepositoryItemReader<TransactionFileEodDto> reader = new RepositoryItemReader<>();
    reader.setRepository(repository);
    reader.setMethodName("findAll");
    // 分页等其他配置
    return reader;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 04:40:34