JPA规范搜索字符串包含的列表

时间:2019-09-27 08:53:05

标签: java spring hibernate jpa

我有以下实体:

@Entity
@Table(name = "my_entity");
public class MyEntity {
    // some fields
    @Column(name = "languages")
    @Convert(converter = StringToListConverter.class)
    private List<String> languages;
}

SQL表:

CREATE TABLE my_entity (
    id VARCHAR(255) PRIMARY KEY,
    // some fields
    languages VARCHAR(255) DEFAULT NULL
);

字段languages包含用逗号分隔的值列表EN,FR,NO

我的任务是选择包含某种语言的记录。例如,在本机SQL中,我想使用以下SQL:

SELECT * FROM my_entity e WHERE e.languages LIKE CONCAT('%', 'EN', '%');

我尝试使用“规范”来做到这一点:

Specification<MyEntity> specification = (root, query, cb) -> {
    final Path<Collection<String>> langs = root.get("languages");
    // also I tried root.joinList
    return cb.isMember("EN", langs);
};

repository.findAll(specification);

但是我收到以下错误:

unknown collection expression type [org.hibernate.jpa.criteria.path.SingularAttributePath]; nested exception is java.lang.IllegalArgumentException: unknown collection expression type [org.hibernate.jpa.criteria.path.SingularAttributePath]

StringToListConverter正在关注:

@Converter
public class StringToListConverter implements AttributeConverter<List<String>, String> {

@Override
public String convertToDatabaseColumn(List<String> list) {
    if(list==null)
        return null;
    return String.join(",", list);
}

@Override
public List<String> convertToEntityAttribute(String joined) {
    if(joined==null)
        return null;
    return new ArrayList<>(Arrays.asList(joined.split(",")));
}

}

存储库:

public class MyEntityRepository extends PagingAndSortingRepository<Address, String>, JpaSpecificationExecutor<MyEntity> {

}

如何解决此问题?

1 个答案:

答案 0 :(得分:0)

关于此问题,有一个很老的开放式休眠问题:https://hibernate.atlassian.net/browse/HHH-9991

遗憾的是,我发现的唯一解决方法是对实体中的语言使用辅助的只读映射,然后在规范中使用它。

@Entity
@Table(name = "my_entity");
public class MyEntity {
    // some fields
    @Column(name = "languages")
    @Convert(converter = StringToListConverter.class)
    private List<String> languages;

    @Column(name = "languages", insertable = false, updatable = false)
    private String languagesString; //No need for setter

}

条件:

Specification<MyEntity> specification = (root, query, cb) -> {
    final Path<String> path = root.get("languagesString");
    return cb.like(path, "%EN%");
};

repository.findAll(specification);