I have an entity:
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...
}
Converter which is used by emails
field in entity (typical implementation like 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();
}
}
}
(I store emails separated by semicolons in one column. StringListConverter
do that conversion.)
And Spring Data repository:
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);
}
}
I use Query by Example mechanism from Spring Data. When I have fields without @Convert
(like String name
) it works. But when I have field with @Convert
(AttributeConverter
) like List<String> emails
it causes 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
(message is weird because I've tried search with that list: ["abc@company.com", "def@company.com"]
, but in message is only one email)
I've tried to implement transform
in 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);
}
}
But is causes InvalidDataAccessApiUsageException
too, but with different message than previous one (there are two emails that I've set):
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