【问题标题】:Jpa Specification cannot call contains functionJpa 规范不能调用包含函数
【发布时间】:2015-11-25 08:36:00
【问题描述】:

我有一个实体类

@Entity
@Table(name = "P_PERSON")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@Document(indexName="p_person")
public class P_person implements Serializable {

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

    @Size(max = 255)
    @Column(name = "first_name", length = 255)
    private String firstName;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getFirst_name() {
        return firstName;
    }

    public void setFirst_name(String first_name) {
        this.firstName = first_name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        P_person p_person = (P_person) o;

        if ( ! Objects.equals(id, p_person.id)) return false;

        return true;
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(id);
    }

    @Override
    public String toString() {
        return "P_person{" +
                "id=" + id +
                ", first_name='" + firstName + "'" +
                '}';
    }
}

和元模型

@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(P_person.class)
public abstract class P_person_ {
    public static volatile SingularAttribute<P_person, Long> id;
    public static volatile SingularAttribute<P_person, String> firstName;

}

存储库类

public interface P_personRepository extends JpaRepository<P_person,Long>, JpaSpecificationExecutor<P_person> {

}

和rest api函数

@RequestMapping(value = "/person_query",
    method = RequestMethod.GET)
@RolesAllowed(AuthoritiesConstants.USER)
@Timed
public ResponseEntity<List<P_person>> person_query(@RequestParam(value = "name") String name) throws URISyntaxException {
    Pageable pageable = PaginationUtil.generatePageRequest(offset, limit, direction, property);
    Page<P_person> page = p_personRepository.findAll(CustomSpecification.whereP_person_query(name), pageable);
    HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/person_query", offset, limit);
    return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK);

}

在 CustomSpecification 我有生成规范的函数

public static Specification<P_person> whereP_person_query(String name) {
    return new Specification<P_person>() {
        @Override
        public Predicate toPredicate(Root<P_person> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {

            return criteriaBuilder.like(root.get(P_person_.firstName), "%" + name + "%");

        }
    };
}

当我必须从 like 更改为在 MS-SQL 中使用 contains 函数时,我的问题就开始了

来自

select * from P_PERSON where first_name like N'%John%'

select * from P_PERSON where  CONTAINS(first_name, N'John')

我尝试了几种方法,但仍然失败

第一

public static Specification<P_person> whereP_person_query(String name) {
    return new Specification<P_person>() {
        @Override
        public Predicate toPredicate(Root<P_person> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {

            Expression<Boolean> contains = criteriaBuilder.function("CONTAINS", Boolean.class, root.get(P_person_.firstName), criteriaBuilder.literal(name));
            return criteriaBuilder.isTrue(contains);

        }
    };
}

这个生成sql:

select count(p_person0_.id) as col_0_0_ from P_PERSON p_person0_ where CONTAINS(p_person0_.first_name, ?)=1

“=”附近出现语法错误的数据库响应错误。

public static Specification<P_person> whereP_person_query(String name) {
    return new Specification<P_person>() {
        @Override
        public Predicate toPredicate(Root<P_person> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {

            Expression<Boolean> contains = criteriaBuilder.function("CONTAINS", Boolean.class, root.get(P_person_.firstName), criteriaBuilder.literal(name));
            return criteriaQuery.where(contains).getRestriction();

        }
    };
}

这个返回 antlr.NoViableAltException: 意外的 AST 节点:函数 (CONTAINS) 生成sql:

select generatedAlias0 from com.amlo.aers.domain.P_person as generatedAlias0
where function('CONTAINS', generatedAlias0.firstName, :param0) order by generatedAlias0.id asc

第三 我将返回类更改为 Integer 并希望它可以工作。

public static Specification<P_person> whereP_person_query(String name) {
    return new Specification<P_person>() {
        @Override
        public Predicate toPredicate(Root<P_person> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {

            Expression<Integer> contains = criteriaBuilder.function("CONTAINS", Integer.class, root.get(P_person_.firstName), criteriaBuilder.literal(name));
            return criteriaBuilder.greaterThan(contains, 0);

        }
    };
}

它生成sql:

select count(p_person0_.id) as col_0_0_ from P_PERSON p_person0_ where CONTAINS(p_person0_.first_name, ?)>0

返回错误 org.hibernate.engine.jdbc.spi.SqlExceptionHelper - '>' 附近的语法不正确。

第四次

public static Specification<P_person> whereP_person_query(String name) {
    return new Specification<P_person>() {
        @Override
        public Predicate toPredicate(Root<P_person> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {

            Predicate contains = (Predicate) criteriaBuilder.function("CONTAINS", Boolean.class, root.get(P_person_.firstName), criteriaBuilder.literal(name));
            return contains;

        }
    };
}

还是错误 java.lang.ClassCastException: org.hibernate.jpa.criteria.expression.function.ParameterizedFunctionExpression 不能转换为 javax.persistence.criteria.Predicate

第 5 次

public static Specification<P_person> whereP_person_query(String name) {
    return new Specification<P_person>() {
        @Override
        public Predicate toPredicate(Root<P_person> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {

            Expression<Boolean> contains = criteriaBuilder.function("CONTAINS", Boolean.class, root.get(P_person_.firstName), criteriaBuilder.literal(name));
            return criteriaBuilder.not(criteriaBuilder.not(contains));

        }
    };
}

仍然返回 antlr.NoViableAltException: 意外的 AST 节点:函数 (CONTAINS)

第六 在阅读了我找到的所有资源后。

public static Specification<P_person> whereP_person_query(String name) {
    return new Specification<P_person>() {
        @Override
        public Predicate toPredicate(Root<P_person> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {

            Expression<Boolean> contains = criteriaBuilder.function("CONTAINS", Boolean.class, root.get(P_person_.firstName), criteriaBuilder.literal(name));
            criteriaQuery.where(contains);
            return null;

        }
    };
}

仍然返回 antlr.NoViableAltException: 意外的 AST 节点:函数 (CONTAINS)

"whereP_person_query" 可以很容易地替换为原生查询。 但我的问题是有一些规范结合了许多标准。 所以我创建了“whereP_person_query”来证明这个项目中全文搜索的使用。

真的可以这样吗?

提前致谢。

【问题讨论】:

标签: jpa full-text-search contains


【解决方案1】:

来自本期https://hibernate.atlassian.net/browse/HHH-3992 我尝试应用它并且此代码有效。

public static Specification<P_person> whereP_person_query(String name) {
    return new Specification<P_person>() {
        @Override
        public Predicate toPredicate(Root<P_person> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
            return criteriaBuilder.and(criteriaBuilder.and(), criteriaBuilder.function("CONTAINS", Boolean.class, root.get(P_person_.firstName), criteriaBuilder.literal(name)));
        }
    };
}

但由于在一个 SQL 中执行多包含功能,最终我无法使用它。

所以我写了另一个使用 containstable 创建 sql 的类。

【讨论】:

    【解决方案2】:

    您的第一个解决方案可以工作,但您应该删除调用 criteriaBuilder.isTrue(contains) - 它会将与 true 的比较添加到 SQL 中,因此在生成的 SQL 查询结束时您会得到 = 1。

    解决方案是将 toPredicate 方法的返回类型从 Predicate 更改为 Expression:

        @Override
        public Expression toPredicate(Root<P_person> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
    
            Expression<Boolean> contains = criteriaBuilder.function("CONTAINS", Boolean.class, root.get(P_person_.firstName), criteriaBuilder.literal(name));
            return contains; // return
    
        }
    

    或者如果你绝对必须返回谓词(修改接口不是一个选项),你应该将表达式转换为谓词而不添加副作用 - 使用criteriaBuilder.add 方法而不是isTrue 应该没事:

        @Override
        public Predicate toPredicate(Root<P_person> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
    
            Expression<Boolean> contains = criteriaBuilder.function("CONTAINS", Boolean.class, root.get(P_person_.firstName), criteriaBuilder.literal(name));
            return criteriaBuilder.and(contains);
    
        }
    

    【讨论】:

    • 感谢您的回答,但它不起作用两个答案都无法编译这让我很好奇为什么从 Expression 扩展的 Predicate 不能转换为 Expression critiriaBuilder.and 必须发送两个表达式,例如这个谓词 and(Expression x, Expression y);它可以一个或多个只用于谓词谓词和(谓词...限制);
    • 当我尝试 'Expression contains = criteriaBuilder.function("CONTAINS", Boolean.class, root.get(P_person_.firstName), criteriaBuilder.literal(name)); return criteriaBuilder.and(criteriaBuilder.literal(Boolean.TRUE), contains);'它抛出此错误意外 AST 节点::在第 1 行,第 86 列附近 [从 P_person 中选择 generatedAlias0 作为 generatedAlias0 where ( :param0 ) and ( function('CONTAINS', generatedAlias0.firstName, :param1) )]
    • 抱歉格式不可读。我只知道它使用另一个降价
    • 自动转换不起作用,因为你需要从`Expression`中获取Predicate。它会朝相反的方向工作。
    • 从 API 中,我看不出为什么and 应该至少需要 2 个表达式,尽管它是有道理的。我找不到将Expression 转换为Predicate 的另一种方法。尝试为此提出另一个问题。
    猜你喜欢
    • 2019-05-25
    • 2021-11-27
    • 2012-04-25
    • 2019-04-29
    • 1970-01-01
    • 1970-01-01
    • 2018-12-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多