我在Hibernate6(迁移到Spring Boot3)上遇到了一个问题.我有一个主实体类BaseEntity,它的status属性是类CustomStatus.在Hibernate/JPA查询中,我喜欢使用这种语法select * from BaseEntity where status = 'ARCHIVED'select * from BaseEntity where status NOT IN ('ARCHIVED', 'CANCELLED').这可以很好地处理原生查询,但我喜欢使用JPA来调用服务.

编译是可以的,但在运行时,我在查询执行时收到一个错误:

Can't compare test expression of type [BasicSqmPathSource(status : CustomStatus)] with element of type [basicType@4(java.lang.String,12)].


最初(Hibernate5/Spring Boot2.7),引用AbstractSingleColumnStandardBasicType/AbstractTypeDescriptor@Type做得很好.


BaseEntity

@Table(name="base_entity")
public class BaseEntity {
    @Id
    private Integer id;

    @JavaType(CustomStatusJavaType.class)
    @JdbcTypeCode(SqlTypes.VARCHAR)
    private CustomStatus status;
}

CustomStatus个个

public class CustomStatus implements Serializable {
    private final String name;

    public CustomStatus (final String name) { this.name = name;}
}

CustomStatusJavaType

public class CustomStatusJavaType extends AbstractClassJavaType<CustomStatus> {

    public static final CustomStatusJavaType INSTANCE = new CustomStatusJavaType ();

    public CustomStatusJavaType () {
        super(CustomStatus.class);
    }

    @Override
    public String toString(CustomStatus value) {
        if (value == null) return null;
        return value.getName();
    }

    @Override
    public CustomStatus fromString(CharSequence str) {
        if (str == null || str.length() == 0) return null;
        return new CustomStatus(str.toString());
    }

    @Override
    public <X> X unwrap(CustomStatus value, Class<X> type, WrapperOptions options) {
        if (value == null) return null;
        return StringJavaType.INSTANCE.unwrap(
                value == null ? null : value.getName(),
                type,
                options
        );
    }

    @Override
    public <X> CustomStatus wrap(X value, WrapperOptions options) {
        if (value == null) return null;
        return new CustomStatus (StringJavaType.INSTANCE.wrap( value, options));
    }
}

UPDATE with AttributeConverter

CustomStatus个个

@Converter
public class CustomStatusAttributeConverter implements AttributeConverter<CustomStatus, String> {

    @Override
    public String convertToDatabaseColumn(CustomStatus status) {
        return (null == status? null : status.getName());
    }

    @Override
    public CustomStatus convertToEntityAttribute(String s) {
        return (s == null || s.isEmpty() ? null : new CustomStatus(s));
    }
}
public class BaseEntity {
    @Id
    private Integer id;

    @Convert(converter = CustomStatusAttributeConverter.class)
    private CustomStatus status;
}

遗憾的是,我得到了同样的错误……如果我编写原生查询,但不使用JPA查询,它就会起作用.

推荐答案

我想问题出在你的问题上,select * from BaseEntity where status = 'ARCHIVED'/select * from BaseEntity where status NOT IN ('ARCHIVED', 'CANCELLED').

更准确地说,Hibernate在您的查询中预期的不是String,而是CustomStatus.

如果不想使用原生查询,可以try 在查询中使用占位符:

@Query("select * from BaseEntity where status = :status")
List<BaseEntity> getBaseEntitiesWithStatus(CustomStatus status);
default List<BaseEntity> getArchivedBaseEntities(){
    return getBaseEntitiesWithStatus(new CustomStatus("ARCHIVED"));
}

@Query("select * from BaseEntity where status NOT IN :disallowed")
List<BaseEntity> getBaseEntitiesWhereStatusNotIn(List<CustomStatus> disallowed);
default List<BaseEntity> getActiveBaseEntities(){
    return getBaseEntitiesWhereStatusNotIn(List.of(
        new CustomStatus("ARCHIVED"),
        new CustomStatus("CANCELLED")
    ));
}

您也可以try 以不同的方式表示您的CustomStatus,例如,在1:N关系中作为它自己的实体,或者通过嵌入它:

public class BaseEntity {
    @Id
    private Integer id;

    @Embedded
    private CustomStatus status;
    //methods
} 
@Embeddable
public class CustomStatus implements Serializable {
    private final String name;

    public CustomStatus (final String name) { this.name = name;}
    //methods
}

有了这个,您可能会得到一个类似select * from BaseEntity where status.name = 'ARCHIVED'的查询

然而,这可能需要将name设为非final.

Java相关问答推荐

查找最大子数组的和

Java自定义ThreadPool—暂停任务提交并取消当前排队任务

为什么一个Test的instance?& gt;在构造函数中接受非空对象?

JPanel透支重叠的JComcoBox

Java流传输一个列表并创建单个对象

按属性值从流中筛选出重复项

如何让JavaFx应用程序识别依赖项?

Javadoc在方法摘要中省略方法

使用Jolt将字段转换为列表

try 将JSON字符串响应从API转换为映射字符串、对象>;时出错

有没有可能在时间范围内得到多种解决方案?

为什么Collectors.toList()不能保证易变性

为什么StandardOpenOption.CREATE不能通过Ubuntu在中小企业上运行?

本机方法(JNI)总是编译的吗?

如何在右击时 Select 新行?

如何利用OpenTelemeter将初始值(零)输出到普罗米修斯

在Java中比较同一多维数组的两个不同的字符串元素

简化每个元素本身都是 map 列表的列表

java 11上出现DateTimeParseException,但java 8上没有

如何在java中从以百分比表示的经过时间和结束日期中找到开始日期