Spring Boot 3.4+JPA+PostgreSQL枚举更新报错求助
Spring Boot 3.4 + JPA + PostgreSQL 16 枚举类型更新报错排查与解决
报错信息
执行emailRepository.save(emailEntity)时触发SQL语法错误:
Caused by: org.hibernate.exception.SQLGrammarException: could not execute statement [ERROR: operator does not exist: character varying = bigint Hint: No operator matches the given name and argument types. You might need to add explicit type casts. Position: 355] [update email_records set active=?,created_date_in_ms=? ...]
相关代码与配置
定时任务代码
@Component @RequiredArgsConstructor public class EmailSenderBatchJob { @Async("...") @Scheduled(cron = "...") public void mailJob() { final int batchSize = 50; Slice<EmailEntity> notSendEmails = emailRepository.getEmailWithStatus(EmailStatus.SCHEDULED, PageRequest.of(0, batchSize)); for (EmailEntity emailEntity : notSendEmails.getContent()) { emailEntity.setEmailStatus(EmailStatus.SEND); emailEntity.setActive(false); emailRepository.save(emailEntity); // 此处触发异常 } } }
Repository定义
@Repository public interface EmailRepository extends JpaRepository<EmailEntity, String> { @Query("FROM EmailEntity ue WHERE ue.emailStatus = :emailStatus") Slice<EmailEntity> getEmailWithStatus(@Param("emailStatus") EmailStatus emailStatus, PageRequest pageRequest); }
数据表结构(Flyway脚本)
CREATE TABLE email_records ( email_status character varying(100) NOT NULL, id character varying(60) PRIMARY KEY NOT NULL );
实体类与转换器
public class EmailEntity { @NotNull @Column(name = "email_status") @Convert(converter = EmailStatusJPAConverter.class) private EmailStatus emailStatus; } @Converter(autoApply = true) public class EmailStatusJPAConverter implements AttributeConverter<EmailStatus, String> { @Override public String convertToDatabaseColumn(EmailStatus attribute) { return attribute.dbValue; } @Override public EmailStatus convertToEntityAttribute(String dbData) { return EmailStatus.fromDbValue(dbData); } } public enum EmailStatus { UNKNOWN("UNKNOWN", "UNKNOWN"), SCHEDULED("SCHEDULED", "SCHEDULED"), SEND("SEND", "SEND") ; public final String dbValue; public final String value; EmailStatus(String dbValue, String value) { this.dbValue = dbValue; this.value = value; } public static EmailStatus fromDbValue(String dbValue) { for (EmailStatus each : values()) { if (each.dbValue.equals(dbValue)) { return each; } } return EmailStatus.UNKNOWN; } }
问题原因
报错核心是Hibernate生成UPDATE语句时,错误地将EmailStatus枚举参数以**整数(ordinal值)**传入,而数据库中email_status是varchar类型,导致类型不匹配。
虽然配置了自定义AttributeConverter,但可能存在以下冲突或未生效的情况:
@Convert注解与@Enumerated注解混用(用户尝试过@Enumerated(String)但未彻底清理配置)- 自定义转换器未被Hibernate正确扫描或加载
- Spring Boot 3.4搭配的Hibernate版本对
autoApply=true的转换器存在兼容问题
解决方案
方案1:移除自定义转换器,改用原生枚举字符串映射
由于你的EmailStatus枚举的dbValue与枚举名称完全一致,可直接使用JPA原生的@Enumerated(EnumType.STRING)替代自定义转换器:
- 修改实体类字段注解:
public class EmailEntity { @NotNull @Column(name = "email_status") @Enumerated(EnumType.STRING) // 替换@Convert注解 private EmailStatus emailStatus; }
- 删除
EmailStatusJPAConverter类(或注释掉@Converter(autoApply = true)避免干扰)
方案2:修复自定义转换器的生效问题
如果必须保留自定义转换器,按以下步骤调整:
- 确保实体类的
@Convert注解未与@Enumerated共存,移除所有@Enumerated注解 - 关闭转换器的
autoApply=true,改为在实体类显式指定:
@Converter // 移除autoApply=true public class EmailStatusJPAConverter implements AttributeConverter<EmailStatus, String> { // ... 原有代码不变 }
- 检查Spring Boot组件扫描范围,确保
EmailStatusJPAConverter所在包被纳入扫描 - 若仍无效,可在
application.properties中添加Hibernate配置强制类型处理:
spring.jpa.properties.hibernate.type.preferred_instant_jdbc_type=VARCHAR spring.jpa.properties.hibernate.type.preferred_uuid_jdbc_type=VARCHAR
方案3:批量优化(可选)
循环调用save会生成多条UPDATE语句,可改用批量操作提升性能同时规避单条语句的参数绑定问题:
// 在EmailRepository中添加批量更新方法 @Modifying @Query("UPDATE EmailEntity ue SET ue.emailStatus = :newStatus, ue.active = false WHERE ue.id IN :ids") void batchUpdateStatusAndActive(@Param("newStatus") EmailStatus newStatus, @Param("ids") List<String> ids); // 在Job中调用 List<String> ids = notSendEmails.getContent().stream().map(EmailEntity::getId).toList(); emailRepository.batchUpdateStatusAndActive(EmailStatus.SEND, ids);
内容的提问来源于stack exchange,提问作者monstereo
相关产品推荐
相关产品推荐

