当我尝试使用Specification
在我的数据库中使用Spring @Repository
按其姓名搜索枚举时,我收到以下异常:
Caused by: java.lang.IllegalArgumentException: Parameter value [HELLO] did not match expected type [application.springEnum.Hello (n/a)]
但是在数据库中,枚举保存为VARCHAR(255)
,为什么我可以使用String
搜索枚举,为什么需要使用枚举类型?
DTO课程
@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class DTO {
@Id
private String id;
@Enumerated(EnumType.STRING)
private Hello helloEnum; // My Enum
}
数据库连接器
@Repository
public interface Connector extends JpaRepository<DTO, String>, JpaSpecificationExecutor<DTO> {
}
简化版
@Component
public class Starter {
@Autowired
private Connector connector;
@PostConstruct
public void init(){
// Create DTO entity
DTO dto = DTO.builder()
.id(UUID.randomUUID().toString())
.helloEnum(Hello.HELLO)
.build();
// Save the entity in the db
connector.save(dto);
// Search by the name, here I get the excpetion
List<DTO> result = connector.findAll((root, query, cb) ->
cb.equal(root.get("helloEnum"), "HELLO")
);
}
}
我很感激您的解释。
答案 0 :(得分:1)
您正在尝试比较Enum
和String
。
尝试这种方式:
List<DTO> result = connector.findAll((root, query, cb) ->
cb.equal(root.get("helloEnum"), Hello.HELLO);
我会尽力解释为什么会这样。 Hibernate使用ResultSet
从数据库中将Class
提取到Reflection
签名。
观察堆栈跟踪,你会看到类似的东西:
org.hibernate.query.spi.QueryParameterBindingValidator.validate(QueryParameterBindingValidator.java:54) 〜[hibernate-core-5.2.16.Final.jar:5.2.16.Final] at org.hibernate.query.spi.QueryParameterBindingValidator.validate(QueryParameterBindingValidator.java:27) 〜[hibernate-core-5.2.16.Final.jar:5.2.16.Final] at org.hibernate.query.internal.QueryParameterBindingImpl.validate(QueryParameterBindingImpl.java:90) 〜[hibernate-core-5.2.16.Final.jar:5.2.16.Final] at org.hibernate.query.internal.QueryParameterBindingImpl.setBindValue(QueryParameterBindingImpl.java:55) 〜[hibernate-core-5.2.16.Final.jar:5.2.16.Final] at org.hibernate.query.internal.AbstractProducedQuery.setParameter(AbstractProducedQuery.java:486) 〜[hibernate-core-5.2.16.Final.jar:5.2.16.Final] at org.hibernate.query.internal.AbstractProducedQuery.setParameter(AbstractProducedQuery.java:104) 〜[冬眠核-5.2.16.Final.jar:5.2.16.Final]
在设置参数之前,Hibernate会执行一系列验证。
以下是初始化Exception
的根本原因的最后一种方法:
public <P> void validate(Type paramType, Object bind, TemporalType temporalType) {
if ( bind == null || paramType == null ) {
// nothing we can check
return;
}
final Class parameterType = paramType.getReturnedClass();
if ( parameterType == null ) {
// nothing we can check
return;
}
if ( Collection.class.isInstance( bind ) && !Collection.class.isAssignableFrom( parameterType ) ) {
// we have a collection passed in where we are expecting a non-collection.
// NOTE : this can happen in Hibernate's notion of "parameter list" binding
// NOTE2 : the case of a collection value and an expected collection (if that can even happen)
// will fall through to the main check.
validateCollectionValuedParameterBinding( parameterType, (Collection) bind, temporalType );
}
else if ( bind.getClass().isArray() ) {
validateArrayValuedParameterBinding( parameterType, bind, temporalType );
}
else {
if ( !isValidBindValue( parameterType, bind, temporalType ) ) {
throw new IllegalArgumentException(
String.format(
"Parameter value [%s] did not match expected type [%s (%s)]",
bind,
parameterType.getName(),
extractName( temporalType )
)
);
}
}
}
包含大量支票的方法private static boolean isValidBindValue(Class expectedType, Object value, TemporalType temporalType)
返回false
因为您的预期类型为class com.whatever.Hello
,要检查的值为HELLO
什么是String
,但Enum
类型与String
不相容!
如果您在搜索条件中添加了适当的Enum
,则验证将会通过,因为private static boolean isValidBindValue(Class expectedType, Object value, TemporalType temporalType)
包含isInstance
检查将通过:
else if ( expectedType.isInstance( value ) ) {
return true;
}
在所有检查完成后,Hibernate从ResultSet
中提取值并构建List
,在这种特殊情况下,使用反射提取List
的元素。
答案 1 :(得分:-1)
public class Main {
enum Hello {
HELLO
}
public static void main(String[] args) {
Hello hello = Hello.HELLO;
System.out.println(hello.toString().equals("HELLO")); //true
System.out.println("HELLO".equals(hello.toString())); //true
System.out.println(hello.toString() == "HELLO"); //true
System.out.println(hello.equals("HELLO")); //false
System.out.println("HELLO".equals(hello)); //false
// System.out.println(hello == "HELLO"); //incompatible types
}
}
不完全确定,但equal()参数必须属于同一类型。
root.get("helloEnum")
是Hello
的实例"HELLO"
是String的实例。
这里没有自动toString()强制转换枚举。 只是为了确保尝试:
// Search by the name, here I get the excpetion
List<DTO> result = connector.findAll((root, query, cb) ->
cb.equal(root.get("helloEnum").toString(), "HELLO")
);