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

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,但可能存在以下冲突或未生效的情况:

  1. @Convert注解与@Enumerated注解混用(用户尝试过@Enumerated(String)但未彻底清理配置)
  2. 自定义转换器未被Hibernate正确扫描或加载
  3. Spring Boot 3.4搭配的Hibernate版本对autoApply=true的转换器存在兼容问题

解决方案

方案1:移除自定义转换器,改用原生枚举字符串映射

由于你的EmailStatus枚举的dbValue与枚举名称完全一致,可直接使用JPA原生的@Enumerated(EnumType.STRING)替代自定义转换器:

  1. 修改实体类字段注解:
public class EmailEntity {
  
    @NotNull
    @Column(name = "email_status")
    @Enumerated(EnumType.STRING) // 替换@Convert注解
    private EmailStatus emailStatus;
        
}
  1. 删除EmailStatusJPAConverter类(或注释掉@Converter(autoApply = true)避免干扰)

方案2:修复自定义转换器的生效问题

如果必须保留自定义转换器,按以下步骤调整:

  1. 确保实体类的@Convert注解未与@Enumerated共存,移除所有@Enumerated注解
  2. 关闭转换器的autoApply=true,改为在实体类显式指定:
@Converter // 移除autoApply=true
public class EmailStatusJPAConverter implements AttributeConverter<EmailStatus, String> {
    // ... 原有代码不变
}
  1. 检查Spring Boot组件扫描范围,确保EmailStatusJPAConverter所在包被纳入扫描
  2. 若仍无效,可在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 15:33:23