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

Spring Data JPA原生查询枚举参数在H2与MariaDB下异常问题

带@Enumerated(EnumType.STRING)的原生查询枚举参数异常问题

问题现象

  • 基于Spring Boot 3.4.2开发,实体类枚举字段标注@Enumerated(EnumType.STRING)时,自定义原生SQL查询无法正确识别枚举参数,返回错误结果甚至抛出异常
  • Spring Data自动生成的findByStatus方法可正常工作,但自定义原生查询findByStatusSql存在以下问题:
    • H2数据库环境:查询FEEDBACK返回ACTIVE的实体,调整枚举值顺序后结果会变化;查询ACTIVE直接抛出DataIntegrityViolationException,提示值"0"不符合列的允许值
    • MariaDB数据库环境:查询ACTIVE返回空列表
  • 移除@Enumerated(EnumType.STRING)注解后,原生查询恢复正常

相关代码

实体类

@Data
@Entity
@Table(name = "order_")
@NoArgsConstructor
@AllArgsConstructor
public class Order {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    @Enumerated(EnumType.STRING)
    @Column(name="status", nullable = false)
    private OrderStatus status;
}

枚举类

public enum OrderStatus {
    ACTIVE,
    FEEDBACK,
    COMPLETED,
    FINISHED
}

Repository接口

public interface OrderRepository extends JpaRepository<Order, Long> {

    @Query(nativeQuery = true, value = "SELECT * FROM order_ s WHERE s.status = :status")
    List<Order> findByStatusSql(OrderStatus status);

    List<Order> findByStatus(OrderStatus status);
}

H2测试代码

@DataJpaTest
public class OrderRepositoryTest {

    @Autowired
    private OrderRepository repository;

    @Autowired
    TestEntityManager entityManager;

    @Test
    void test() {
        Order order = new Order(null, OrderStatus.FEEDBACK);
        entityManager.persist(order);
        order = new Order(null, OrderStatus.ACTIVE);
        entityManager.persist(order);

        List<Order> orders = repository.findByStatusSql(OrderStatus.FEEDBACK);
        // 1 - ACTIVE
        System.out.println(orders.size());
        orders = repository.findByStatusSql(OrderStatus.FINISHED);
        // 1 - FEEDBACK
        System.out.println(orders.size());
        orders = repository.findByStatus(OrderStatus.FEEDBACK);
        // 1 - FEEDBACK
        System.out.println(orders.size());
        orders = repository.findByStatusSql(OrderStatus.ACTIVE);
        // org.springframework.dao.DataIntegrityViolationException: JDBC exception executing SQL [SELECT * FROM order_ s WHERE s.status = ?] [Value not permitted for column "('ACTIVE', 'COMPLETED', 'FEEDBACK', 'FINISHED')": "0"; SQL statement:
        //SELECT * FROM order_ s WHERE s.status = ? [22030-232]] [n/a]; SQL [n/a]
    }
}

MariaDB配置

spring:
  datasource:
    url: jdbc:mariadb://localhost:3306/db?connectionTimeZone=SERVER&preserveInstants=true
    username: user
    password: pass
    driver-class-name: org.mariadb.jdbc.Driver
    connection-test-query: SELECT 1
  jpa:
    hibernate:
      ddl-auto: create-drop
    show-sql: true

原因分析

核心问题是原生查询不会自动应用实体类上的@Enumerated(EnumType.STRING)转换规则:

  • Spring Data自动生成的查询方法(如findByStatus)会读取实体的枚举注解,自动将枚举参数转换为对应的字符串值传入查询
  • 但原生查询默认会将枚举对象转换为其ordinal序号(从0开始的整数),而非字符串值
  • H2数据库中枚举类型列会校验值是否在允许的字符串列表中,传入序号"0"会触发校验异常;MariaDB中字符串列与数字"0"不匹配,因此返回空列表;调整枚举顺序会改变ordinal值,导致返回结果变化

解决思路

方案1:手动转换枚举为字符串参数

修改Repository方法的参数类型为String,调用时传入枚举的name()值:

@Query(nativeQuery = true, value = "SELECT * FROM order_ s WHERE s.status = :status")
List<Order> findByStatusSql(String status);

// 调用示例
repository.findByStatusSql(OrderStatus.FEEDBACK.name());

方案2:使用SPEL表达式自动转换

通过SPEL表达式在查询中直接获取枚举的字符串值,无需修改参数类型:

@Query(nativeQuery = true, value = "SELECT * FROM order_ s WHERE s.status = :#{#status.name()}")
List<Order> findByStatusSql(@Param("status") OrderStatus status);

方案3:自定义属性转换器(推荐)

实现AttributeConverter统一处理枚举与字符串的转换,替代@Enumerated注解,自动生成方法和原生查询都会应用该转换规则:

import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;

@Converter(autoApply = true)
public class OrderStatusConverter implements AttributeConverter<OrderStatus, String> {

    @Override
    public String convertToDatabaseColumn(OrderStatus status) {
        return status != null ? status.name() : null;
    }

    @Override
    public OrderStatus convertToEntityAttribute(String dbData) {
        return dbData != null ? OrderStatus.valueOf(dbData) : null;
    }
}

之后移除实体类中的@Enumerated(EnumType.STRING)注解即可。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 07:32:10