@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
相关产品推荐
相关产品推荐

