Jpa 规范无法调用包含函数
Jpa Specification cannot call contains function
我有一个实体class
@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;
}
存储库class
public interface P_personRepository extends JpaRepository<P_person,Long>, JpaSpecificationExecutor<P_person> {
}
然后休息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 + "%");
}
};
}
当我必须从喜欢更改为使用 contains MS 中的函数时,我的问题就开始了-SQL
来自
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();
}
};
}
这个 return 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
第三
我将 return class 更改为 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
哪个 return 错误 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
第五
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));
}
};
}
仍然return 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;
}
};
}
仍然return antlr.NoViableAltException: 意外的 AST 节点: 函数 (CONTAINS)
"whereP_person_query" 可以轻松替换为本机查询。
但我的问题是有一些规范结合了很多标准。
所以我创建 "whereP_person_query" 来证明全文搜索在这个项目中的使用。
真的可以这样做吗?
提前致谢。
您的第一个解决方案可行,但您应该删除调用 criteriaBuilder.isTrue(contains)
- 它将与 true 的比较添加到 SQL,因此您在生成的 SQL 末尾得到 = 1 ]查询。
解决方案 是将 toPredicate
方法的 return 类型从 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
}
或者如果你绝对必须 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);
}
来自本期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。
所以我写了另一个 class 使用 containstable 创建 sql。
我有一个实体class
@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;
}
存储库class
public interface P_personRepository extends JpaRepository<P_person,Long>, JpaSpecificationExecutor<P_person> {
}
然后休息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 + "%");
}
};
}
当我必须从喜欢更改为使用 contains MS 中的函数时,我的问题就开始了-SQL
来自
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();
}
};
}
这个 return 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
第三 我将 return class 更改为 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
哪个 return 错误 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
第五
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));
}
};
}
仍然return 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;
}
};
}
仍然return antlr.NoViableAltException: 意外的 AST 节点: 函数 (CONTAINS)
"whereP_person_query" 可以轻松替换为本机查询。 但我的问题是有一些规范结合了很多标准。 所以我创建 "whereP_person_query" 来证明全文搜索在这个项目中的使用。
真的可以这样做吗?
提前致谢。
您的第一个解决方案可行,但您应该删除调用 criteriaBuilder.isTrue(contains)
- 它将与 true 的比较添加到 SQL,因此您在生成的 SQL 末尾得到 = 1 ]查询。
解决方案 是将 toPredicate
方法的 return 类型从 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
}
或者如果你绝对必须 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);
}
来自本期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。
所以我写了另一个 class 使用 containstable 创建 sql。