У меня есть сущность:
import javax.persistence.Convert;
@Entity(name = "my_entity")
public class MyEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@Convert(converter = StringListConverter.class)
private List<String> emails;
// next fields omitted...
}
Конвертер, который используется полем emails
в сущности (типичная реализация, например LocalDateTimeConverter
):
import javax.persistence.Converter;
@Converter
public class StringListConverter implements AttributeConverter<List<String>, String> {
private static final String SPLIT_CHAR = ";";
@Override
public String convertToDatabaseColumn(List<String> stringList) {
if (CollectionUtils.isNotEmpty(stringList)) {
return String.join(SPLIT_CHAR, stringList);
} else {
return null;
}
}
@Override
public List<String> convertToEntityAttribute(String string) {
if (StringUtils.isNotBlank(string)) {
return Arrays.asList(string.split(SPLIT_CHAR));
} else {
return Collections.emptyList();
}
}
}
(я храню электронные письма разделенные точкой с запятой в одном столбце. StringListConverter
выполните это преобразование.)
И хранилище данных Spring:
import org.springframework.data.domain.Example;
public interface MyRepository extends JpaRepository<MyEntity, Long> {
default List<MyEntity> findMatchingMyEntity(MyEntity myEntity) {
Example<MyEntity> example = Example.of(myEntity);
return findAll(example);
}
}
Я использую механизм Query by Example из Spring Data. Когда у меня есть поля без @Convert
(например, String name
), это работает. Но когда у меня есть поле с @Convert
(AttributeConverter
), например List<String> emails
, это вызывает InvalidDataAccessApiUsageException
.
org.springframework.dao.InvalidDataAccessApiUsageException: Parameter value [abc@company.com] did not match expected type [java.util.List (n/a)]; nested exception is java.lang.IllegalArgumentException: Parameter value [abc@company.com] did not match expected type [java.util.List (n/a)]
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:374) ~[spring-orm-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:257) ~[spring-orm-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:528) ~[spring-orm-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61) ~[spring-tx-5.2.1.RELEASE.jar:5.2.1.RELEASE]
...
Caused by: java.lang.IllegalArgumentException: Parameter value [abc@company.com] did not match expected type [java.util.List (n/a)]
at org.hibernate.query.spi.QueryParameterBindingValidator.validate(QueryParameterBindingValidator.java:54) ~[hibernate-core-5.4.8.Final.jar:5.4.8.Final]
at org.hibernate.query.spi.QueryParameterBindingValidator.validate(QueryParameterBindingValidator.java:27) ~[hibernate-core-5.4.8.Final.jar:5.4.8.Final]
at org.hibernate.query.internal.QueryParameterBindingImpl.validate(QueryParameterBindingImpl.java:90) ~[hibernate-core-5.4.8.Final.jar:5.4.8.Final]
... 146 common frames omitted
(сообщение странное, потому что я пробовал поиск по этому списку: ["abc@company.com", "def@company.com"]
, но в сообщении только одно электронное письмо)
Я пытался реализовать transform
в ExampleMatcher
:
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
public interface MyRepository extends JpaRepository<MyEntity, Long> {
default List<MyEntity> findMatchingMyEntity(MyEntity myEntity) {
ExampleMatcher matcher = ExampleMatcher.matching()
.withMatcher("emails",
match -> match.transform(emailsOptional -> {
if (emailsOptional.isPresent()) {
List<String> emails = (List<String>) emailsOptional.get();
return Optional.ofNullable(new StringListConverter().convertToDatabaseColumn(emails));
}
return emailsOptional;
}));
Example<MyEntity> example = Example.of(myEntity, matcher);
return findAll(example);
}
}
Но это тоже причины InvalidDataAccessApiUsageException
, но с сообщением, отличным от предыдущего (я установил два электронных письма):
org.springframework.dao.InvalidDataAccessApiUsageException: Parameter value [abc@company.com;def@company.com] did not match expected type [java.util.List (n/a)]; nested exception is java.lang.IllegalArgumentException: Parameter value [abc@company.com;def@company.com] did not match expected type [java.util.List (n/a)]
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:374) ~[spring-orm-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:257) ~[spring-orm-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:528) ~[spring-orm-5.2.1.RELEASE.jar:5.2.1.RELEASE]
Caused by: java.lang.IllegalArgumentException: Parameter value [abc@company.com;def@company.com] did not match expected type [java.util.List (n/a)]
at org.hibernate.query.spi.QueryParameterBindingValidator.validate(QueryParameterBindingValidator.java:54) ~[hibernate-core-5.4.8.Final.jar:5.4.8.Final]
at org.hibernate.query.spi.QueryParameterBindingValidator.validate(QueryParameterBindingValidator.java:27) ~[hibernate-core-5.4.8.Final.jar:5.4.8.Final]
... 146 common frames omitted