【问题标题】:IllegalArgumentException thrown when a Path is created on a field with @Convert使用 @Convert 在字段上创建路径时引发 IllegalArgumentException
【发布时间】:2020-11-13 16:08:42
【问题描述】:

我正在尝试构建一个条件查询,并为每个传入过滤器一个谓词“和”连接,例如

CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> criteriaQuery = criteriaBuilder.createQuery(Long.class);
Root<T> entityRoot = criteriaQuery.from(SampleEntity.class);
// for each filterable field create a Predicate and included in as `criteriaQuery.where(...)`
criteriaQuery.select(criteriaBuilder.count(entityRoot));
entityManager.createQuery(criteriaQuery).setMaxResults(maxResult).getSingleResult();

对于每个这样的过滤器(参见上面代码示例中的注释行),首先创建一个路径。

Path<?> path = PathBuilder.buildFromFieldName(fieldName, rootEntity);

这将是方法:

public static <T> Path<?> buildFromFieldName(String fieldName, Root<T> entity) throws IllegalArgumentException {
    Path<?> path = entity;
    List<String> fieldNames = Arrays.asList(fieldName.split("\\."));
    for (String fieldNamePath : fieldNames) {
        path = path.get(fieldNamePath);
    }
    return path;
}

嵌套字段会出现,例如命名为myChild.mySet 给出以下示例。

@Entity
@Table(name = "T_SAMPLE")
public class SampleEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Embedded
    private SampleChildEntity myChild;
}

@Embeddable
public class SampleChildEntity {

    @Convert(converter = CommaSeparatedSetConverter.class, attributeName = "mySet")
    private Set<String> mySet = new HashSet<>();

}

鉴于该示例,当调用 criteriaBuilder.isMember(filterValue, path) 时会引发以下异常(“filterValue”将是要比较的值,例如搜索字符串)。

原因:在方法buildFromFieldName 中,首先从根实体(SampleEntity)创建路径。然后它的类型为org.hibernate.query.criteria.internal.path.RootImpl。在 for 循环中处理 fieldNamePath myChild 时,将重新创建 Path。然后它是 org.hibernate.query.criteria.internal.path.SingularAttributePath 类型,并且在处理最后一个 fieldNamePath mySet 时保持不变。只有当 @Convert 被删除时,类型才会更改为 org.hibernate.query.criteria.internal.path.PluralAttributePath,因为它是一个集合类型。

java.lang.IllegalArgumentException: unknown collection expression type 
        [org.hibernate.query.criteria.internal.path.SingularAttributePath]

    at org.hibernate.query.criteria.internal.CriteriaBuilderImpl.isMember(CriteriaBuilderImpl.java:1324)
    [... stack trace contains further local classes ...]

所以原因是SampleChildEntity 中的mySet 字段上的@Convert。 Hibernate 看到 Set&lt;String&gt; 类型,但不使用 PluralAttributePathSingularAttributePath 导致问题。如果没有 @Convert 它可以工作,但不能删除转换器。

有什么办法可以做到吗?路径可以创建不同吗?

【问题讨论】:

    标签: hibernate criteria-api


    【解决方案1】:

    通过例如使用转换器的持久属性@Convert 是一个基本属性,因此是 SingularAttribute,因此您看到 SingularAttributePath 是绝对正确的。假设您可以在这样的单数属性上使用复数谓词是错误的。 Hibernate 无法将其转换为 SQL,因为它不知道您的转换器做什么。我猜该列代表一个逗号分隔的列表?在这种情况下,您将使用例如一个类似的谓词来编写一个过滤器。像这样的:

    WHERE alias.myChild.mySet = :filterValue 
       OR alias.myChild.mySet LIKE CONCAT(:filterValue, ';%') 
       OR alias.myChild.mySet LIKE CONCAT(CONCAT('%;', :filterValue), ';%') 
       OR alias.myChild.mySet LIKE CONCAT('%;', :filterValue)
    

    将其转换为 JPA 标准应该很容易。

    【讨论】:

    • 是的,Hibernate 工作正常。是的,该字段将包含一个逗号分隔的列表。您的解决方案应该有效。我使用@Formula 发布了另一种可能性
    • 再次偶然发现同一个地方并尝试使用类似查询,但使用cb.like(path, ".*,null,.*") 得到java.lang.IllegalArgumentException: Parameter value [.*,abc,.*] did not match expected type [java.util.Set (n/a)]。似乎如果我将它视为实体管理器想要与集合进行比较的字符串,并且如果在标准构建器中使用集合查询,它甚至无法通过抱怨 SingularAttributePath 来构建查询。
    • 这是一个更好的方法:medium.com/the-soft-tradeoff/… 不幸的是,随着时间的推移,糟糕的设计会在大型系统中蔓延......
    【解决方案2】:

    解决方案/解决方法是在第二个字段上使用@Formula,该字段以字符串形式引用逗号分隔的字段。然后可以在该字符串-字段上进行查询。

    @Embeddable
    public class SampleChildEntity {
    
        // no getter/setter
        @Formula(value = "MY_SET")
        private String mySetCommaSeparated;
    
        @Column(name = "MY_SET")
        @Convert(converter = CommaSeparatedSetConverter.class, attributeName = "mySet")
        private Set<String> mySet = new HashSet<>();
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-12-08
      • 2019-09-25
      • 2018-12-04
      • 2017-12-12
      • 2010-11-14
      • 2020-07-28
      • 1970-01-01
      相关资源
      最近更新 更多