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

如何使用CriteriaBuilder处理存储为varchar类型的MonthDay?

解决方案:用Criteria API查询varchar存储的MonthDay对象

问题根源

Hibernate对LocalDate有内置的JDBC类型映射,能自动将varchar列转换为SQL的date类型处理,因此as(LocalDate.class)能正常工作。但MonthDay没有默认的JDBC类型映射,Hibernate会将其视为可序列化对象,转换为Postgres的bytea类型,导致查询谓词失效。

可行方案

1. 自定义类型转换器(推荐,支持直接用MonthDay对象查询)

通过Hibernate的AttributeConverter实现MonthDay与数据库varchar字符串的双向转换,让Hibernate能正确识别列类型:

import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import java.time.MonthDay;

@Converter(autoApply = true)
public class MonthDayConverter implements AttributeConverter<MonthDay, String> {
    @Override
    public String convertToDatabaseColumn(MonthDay attribute) {
        return attribute != null ? attribute.toString() : null;
    }

    @Override
    public MonthDay convertToEntityAttribute(String dbData) {
        return dbData != null ? MonthDay.parse(dbData) : null;
    }
}
  • 若实体类中对应字段为MonthDay类型,添加@Convert(converter = MonthDayConverter.class)(或因autoApply=true自动生效)。
  • 查询时直接使用MonthDay对象,无需手动转换:
criteriaBuilder.between(root.get("my_varchar_column_that_contains_a_monthday"), startMonthDay, endMonthDay);

2. 字符串格式匹配比较(无需修改实体)

如果数据库中varchar存储的MonthDay格式与MonthDay.toString()输出的--MM-dd一致(比如--12-25),可直接将Java端的MonthDay转为字符串,用字符串比较:

String startMonthDayStr = startMonthDay.toString();
String endMonthDayStr = endMonthDay.toString();
Predicate predicate = criteriaBuilder.between(
    root.get("my_varchar_column_that_contains_a_monthday").as(String.class),
    startMonthDayStr,
    endMonthDayStr
);

这种方式依赖字符串的字典序与MonthDay的时间顺序一致,只要格式统一就能保证查询逻辑正确。

3. 数据库函数辅助转换(适合复杂场景)

利用Postgres的日期函数,将varchar列转换为可比较的月日数值,比如提取月份和日组合成整数(如1225代表12月25日):

// 提取月份并乘以100加上日期,转为整数比较
Expression<Integer> monthDayNum = criteriaBuilder.function(
    "to_number",
    Integer.class,
    criteriaBuilder.concat(
        criteriaBuilder.function("substring", String.class, root.get("my_varchar_column_that_contains_a_monthday"), criteriaBuilder.literal(3), criteriaBuilder.literal(2)),
        criteriaBuilder.function("substring", String.class, root.get("my_varchar_column_that_contains_a_monthday"), criteriaBuilder.literal(6), criteriaBuilder.literal(2))
    )
);
int startNum = startMonthDay.getMonthValue() * 100 + startMonthDay.getDayOfMonth();
int endNum = endMonthDay.getMonthValue() * 100 + endMonthDay.getDayOfMonth();
Predicate predicate = criteriaBuilder.between(monthDayNum, startNum, endNum);

总结

  • 可以使用实际的MonthDay对象处理,但需要通过自定义类型转换器让Hibernate正确映射类型。
  • 若不想修改实体或映射配置,基于统一格式的字符串比较是简单可行的临时方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 04:33:23