首页 > 未分类 > 关于SpringDataJPA使用CriteriaBuilder生成WHERE子句(Predicate对象)时如何使用自定义值代替Expression参数

关于SpringDataJPA使用CriteriaBuilder生成WHERE子句(Predicate对象)时如何使用自定义值代替Expression参数

今天使用Specification 接口创建动态SQL时遇到了个坑。
关于SpringDataJPA如何优雅的定制高效率SQL,即使用Specification 接口创建SQL表示并执行 可以看: 这个
 
主要就是用CriteriaBuilder 创建 locate 语句遇到个问题。以此可以衍生到 CriteriaBuilder 所有用得上Expression入参的方法上。
关于Mysql的locate方法:  LOCATE(substr,str) :返回子串 substr 在字符串 str 中第一次出现的位置。如果子串 substr 在 str 中不存在,返回值为 0: 
CriteriaBuilder 在创建 locate 子句时,第一个参数是 LOCATE(substr,str) 中的 str,第二个才是 substr。完全颠倒了过来。很不符合逻辑。
下面是他的几个创建locate方法:

Expression<Integer> locate(Expression<String> x, Expression<String> pattern);
Expression<Integer> locate(Expression<String> x, String pattern);

Expression这玩意,一般是通过Root类的get方法来指定一个表字段的。
根据他提供的方法 我无法做到在一个自定义值中查找table字段是否包含在内,只能判断table字段是否包含我的自定义值。
因为他只有第二个参数接受一个String变量, 即表示传入的String参数是否是第一个参数x的子串(x表示一个表字段,通过root.get()得出)。
而我的需求则为需要在我提供的字符串中寻找一个表字段是否为我提供字符串的子串。
要是想以他提供的方法来实现我的需求,第一个参数是我需要自己设定自定义String值的。而我又无法设定,这就有点恶心了。
 
经过我不懈的查询。终于,什么方案都没查出来。 那没办法了,只能去看一下源码看看有没有什么收获。
我就好奇他这入参String到底经厉了一些什么, 翻了一下,结果还真有。
 
进入到 locate(Expression<String> x, String pattern) 方法的底层实现。
可以看到 他实际上是new 了一个LocateFunction, 传入了String入参pattern

@Override
public Expression<Integer> locate(Expression<String> string, String pattern) {
	return new LocateFunction( this, pattern, string );
}

 
然后,看一下所使用到的LocateFunction的构造方法:

public LocateFunction(CriteriaBuilderImpl criteriaBuilder, String pattern, Expression<String> string) {
	this(
		criteriaBuilder,
		new LiteralExpression<String>( criteriaBuilder, pattern ),
		string,
		null
	);
}

嗨,有发现了,他实例化了一个 LiteralExpression 类,传入了 CriteriaBuilder 的一个实现和String类型的pattern参数。
而LiteralExpression 类又实现了Expression接口。
我来一个如法炮制,创建一个LiteralExpression 对象,放入自己想要的参数:  new LiteralExpression<>(cb, title)  。
传的cb对象是Specification 接口的toSpecification 方法提供的CriteriaBuilder 对象。
然后使用这个locate方法:  将我初始化的LiteralExpression作为第一个参数,root.get()获取的Expression作为第二个参数。

Expression<Integer> locate(Expression<String> x, Expression<String> pattern);

然后debug断点调试一下。 emmmm 然后就报错了。
很疑惑,后面又试了一下别的东西,塞不进去,干脆给第一个参数传了个null,神tm就可以了。至于为什么,我也不甚了解。
 
贴上这段转换为sql语句后与我想象吻合的的 Predicate  。

//(type = 1 AND locate(text,titleExpr)>0)
LiteralExpression<String> titleExpr = new LiteralExpression<>(null, title);//titleExpr==自定义的title参数变量
Predicate titleIsMatch = cb.gt(cb.locate(titleExpr, root.get("text")), 0);
Predicate titlePredicate = cb.and(cb.equal(root.get("type"), 1), titleIsMatch);

 

           


CAPTCHAis initialing...
EA PLAYER &

历史记录 [ 注意:部分数据仅限于当前浏览器 ]清空

      00:00/00:00