java动态构建数据库复杂查询教程

网友投稿 240 2022-09-12


java动态构建数据库复杂查询教程

有的时候,你需要动态构建一个比较复杂的查询条件,传入数据库中进行查询。而条件本身可能来自前端请求或者配置文件。那么这个时候,表达式树,就可以帮助到你。

Where当中可以传入固定的条件

以下是一个简单的单元测试用例。接下来,我们将这个测试用例改的面目全非。

[Test]

public void Normal()

{

var re = Enumerable.Range(0, 10).AsQueryable() // 0-9

.Where(x => x >= 1 && x < 5).ToList(); // 1 2 3 4

var expectation = Enumerable.Range(1, 4); // 1 2 3 4

re.Should().BeEquivalentTo(expectation);

}

Queryable中的Where就是一种表达式树

由于是 Queryable 的关系,所以Where当中的其实是一个表达式,那么我们把它单独定义出来,顺便水一下文章的长度。

[Test]

public void Expression00()

{

Expression> filter = x => x >= 1 && x < 5;

var re = Enumerable.Range(0, 10).AsQueryable()

.Where(filter).ToList();

var expectation = Enumerable.Range(1, 4);

re.Should().BeEquivalentTo(expectation);

}

表达式可以通过Lambda隐式转换

Expression 右侧是一个 Lambda ,所以可以捕获上下文中的变量。

这样你便可以把 minValue 和 maxValue 单独定义出来。

于是乎你可以从其他地方来获取 minValue 和 maxValue 来改变 filter。

[Test]

public void Expression01()

{

var minValue = 1;

var maxValue = 5;

Expression> filter = x => x >= minValue && x < maxValue;

var re = Enumerable.Range(0, 10).AsQueryable()

.Where(filter).ToList();

var expectation = Enumerable.Range(1, 4);

re.Should().BeEquivalentTo(expectation);

}

可以使用方法创建表达式

那既然这样,我们也可以使用一个方法来创建 Expression。

这个方法,实际上就可以认为是这个 Expression 的工厂方法。

[Test]

public void Expression02()

{

var filter = CreateFilter(1, 5);

var re = Enumerable.Range(0, 10).AsQueryable()

.Where(filter).ToList();

var expectation = Enumerable.Range(1, 4);

re.Should().BeEquivalentTo(expectation);

Expression> CreateFilter(int minValue, int maxValue)

{

return x => x >= minValue && x < maxValue;

}

}

通过Func可以更加灵活的组合条件

那可以使用 minValue 和 maxValue 作为参数来制作工厂方法,那么用委托当然也可以。

于是,我们可以把左边和右边分别定义成两个 Func,从而由外部来http://决定左右具体的比较方式。

[Test]

public void Expression03()

{

var filter = CreateFilter(x => x >= 1, x => x < 5);

var re = Enumerable.Range(0, 10).AsQueryable()

.Where(filter).ToList();

var expectation = Enumerable.Range(1, 4);

re.Should().BeEquivalentTo(expectation);

Expression> CreateFilter(Func leftFunc, Func rightFunc)

{

return x => leftFunc.Invoke(x) && rightFunc.Invoke(x);

}

}

也可以手动构建表达式

实际上,左右两个不仅仅是两个Func,其实也可以直接是两个表达式。

不过稍微有点不同的是,表达式的合并需要用 Expression 类型中的相关方法创建。

我们可以发现,调用的地方这次其实没有任何改变,因为 Lambda 既可以隐式转换为 Func 也可以隐式转换为 Expression。

每个方法的意思可以从注释中看出。

[Test]

public void Expression04()

{

var filter = CreateFilter(x => x >= 1, x => x < 5);

var re = Enumerable.Range(0, 10).AsQueryable()

.Where(filter).ToList();

var expectation = Enumerable.Range(1, 4);

re.Should().BeEquivalentTo(expectation);

Expression> CreateFilter(Expression> leftFunc,

Expression> rightFunc)

{

// x

var pExp = Expression.Parameter(typeof(int), "x");

// (a => leftFunc(a))(x)

var leftExp = Expression.Invoke(leftFunc, pExp);

// (a => rightFunc(a))(x)

var rightExp = Expression.Invoke(rightFunc, pExp);

// (a => leftFunc(a))(x) && (a => rightFunc(a))(x)

var bodyExp = Expression.AndAlso(leftExp, rightExp);

// x => (a => leftFunc(a))(x) && (a => rightFunc(a))(x)

var resultExp = Expression.Lambda>(bodyExp, pExp);

return resultExp;

}

}

引入表达式的解构

使其更加简单

但是,上面的方法,其实可以再优化一下。避免对左右表达式的直接调用。

使用一个叫做 Unwrap 的方法,可以将 Lambda Expression 解构成只包含 Body 部分的表达式。

这是一个自定义的扩展方法,你可以通过ObjectVisitor来引入这个方法。

[Test]

public void Expression05()

{

var filter = CreateFilter(x => x >= 1, x => x < 5);

var re = Enumerable.Range(0, 10).AsQueryable()

.Where(filter).ToList();

var expectation = Enumerable.Range(1, 4);

re.Should().BeEquivalentTo(expectation);

Expression> CreateFilter(Expression> leftFunc,

Expression> rightFunc)

{

// x

var pExp = Expression.Parameter(typeof(int), "x");

// leftFunc(x)

var leftExp = leftFunc.Unwrap(pExp);

// rightFunc(x)

var rightExp = rightFunc.Unwrap(pExp);

// leftFunc(x) && rightFunc(x)

var bodyExp = Expression.AndAlso(leftExp, rightExp);

// x => leftFunc(x) && rightFunc(x)

var resultExp = Expression.Lambda>(bodyExp, pExp);

return resultExp;

}

}

可以拼接更多的表达式

我们可以再优化以下,把 CreateFilter 方法扩展为支持多个子表达式和可自定义子表达式的连接方式。

于是,我们就可以得到一个 JoinSubFilters 方法。

[Test]

public void Expression06()

{

var filter = JoinSubFilters(Expression.AndAlso, x => x >= 1, x => x < 5);

var re = Enumerable.Range(0, 10).AsQueryable()

.Where(filter).ToList();

var expectation = Enumerable.Range(1, 4);

re.Should().BeEquivalentTo(expectation);

Expression> JoinSubFilters(Func expJoiner,

params Expression>[] subFilters)

{

// x

var pExp = Expression.Parameter(typeof(int), "x");

var result = subFilters[0];

foreach (var sub in subFilters[1..])

{

var leftExp = result.Unwrap(pExp);

var rightExp = sub.Unwrap(pExp);

var bodyExp = expJoiner(leftExp, rightExp);

result = Expression.Lambda>(bodyExp, pExp);

}

return result;

}

}

使用工厂方法来代替固定的子表达式

有了前面的经验,我们知道。其实x => x >= 1这个表达式可以通过一个工厂方法来建。

所以,我们使用一个 CreateMinValueFilte

r 来创建这个表达式。

[Test]

public void Expression07()

{

var filter = JoinSubFilters(Expression.AndAlso,

CreateMinValueFilter(1),

x => x < 5);

var re = Enumerable.Range(0, 10).AsQueryable()

.Where(filter).ToList();

var expectation = Enumerable.Range(1, 4);

re.Should().BeEquivalentTo(expectation);

Expression> CreateMinValueFilter(int minValue)

{

return x => x >= minValue;

}

Expression> JoinSubFilters(Func expJoiner,

params Expression>[] subFilters)

{

// x

var pExp = Expression.Parameter(typeof(int), "x");

var result = subFilters[0];

foreach (var sub in subFilters[1..])

{

var leftExp = result.Unwrap(pExp);

var rightExp = sub.Unwrap(pExp);

var bodyExp = expJoiner(leftExp, rightExp);

result = Expression.Lambda>(bodyExp, pExp);

}

return result;

}

}

工厂方法内部也可以使用Expression手动创建

当然,可以只使用 Expression 相关的方法来创建x => x >= 1。

[Test]

public void Expression08()

{

var filter = JoinSubFilters(Expression.AndAlso,

CreateMinValueFilter(1),

x => x < 5);

var re = Enumerable.Range(0, 10).AsQueryable()

.Where(filter).ToList();

var expectation = Enumerable.Range(1, 4);

re.Should().BeEquivalentTo(expectation);

Expression> CreateMinValueFilter(int minValue)

{

// x

var pExp = Expression.Parameter(typeof(int), "x");

// minValue

var rightExp = Expression.Constant(minValue);

// x >= minValue

var bodyExp = Expression.GreaterThanOrEqual(pExp, rightExp);

var result = Expression.Lambda>(bodyExp, pExp);

return result;

}

Expression> JoinSubFilters(Func expJoiner,

params Expression>[] subFilters)

{

// x

var pExp = Expression.Parameter(typeof(int), "x");

var result = subFilters[0];

foreach (var sub in subFilters[1..])

{

var leftExp = result.Unwrap(pExp);

var rightExp = sub.Unwrap(pExp);

var bodyExp = expJoiner(leftExp, rightExp);

result = Expression.Lambda>(bodyExp, pExp);

}

return result;

}

}

同理,子表达式都可以如此创建

那既然都用了 Expression 来创建子表达式了,那就干脆再做一点点改进,把x => x < 5也做成从工厂方法获取。

[Test]

public void Expression09()

{

var filter = JoinSubFilters(Expression.AndAlso,

CreateValueCompareFilter(Expression.GreaterThanOrEqual, 1),

CreateValueCompareFilter(Expression.LessThan, 5));

var re = Enumerable.Range(0, 10).AsQueryable()

.Where(filter).ToList();

var expectation = Enumerable.Range(1, 4);

re.Should().BeEquivalentTo(expectation);

Expression> CreateValueCompareFilter(Func comparerFunc,

int rightValue)

{

var pExp = Expression.Parameter(typeof(int), "x");

var rightExp = Expression.Constant(rightValue);

var bodyExp = comparerFunc(pExp, rightExp);

var result = Expression.Lambda>(bodyExp, pExp);

return result;

}

Expression> JoinSubFilters(Func expJoiner,

params Expression>[] subFilters)

{

// x

var pExp = Expression.Parameter(typeof(int), "x");

var result = subFilters[0];

foreach (var sub in subFilters[1..])

{

var leftExp = result.Unwrap(pExp);

var rightExp = sub.Unwrap(pExp);

var bodyExp = expJoiner(leftExp, rightExp);

result = Expression.Lambda>(bodyExp, pExp);

}

return result;

}

}

加入一点点配置,就完成了

最后,我们在把子表达式的创建通过一点点小技巧。通过外部参数来决定。就基本完成了一个多 And 的值比较查询条件的动态构建。

[Test]

public void Expression10()

{

var config = new Dictionary

{

{ ">=", 1 },

{ "<", 5 }

};

var subFilters = config.Select(x => CreateValueCompareFilter(MapConfig(x.Key), x.Value)).ToArray();

var filter = JoinSubFilters(Expression.AndAlso, subFilters);

var re = Enumerable.Range(0, 10).AsQueryable()

.Where(filter).ToList();

var expectation = Enumerable.Range(1, 4);

re.Should().BeEquivalentTo(expectation);

Func MapConfig(string op)

{

return op switch

{

">=" => Expression.GreaterThanOrEqual,

"<" => Expression.LessThan,

_ => throw new ArgumentOutOfRangeException(nameof(op))

};

}

Expression> CreateValueCompareFilter(Func comparerFunc,

int rightValue)

{

var pExp = Expression.Parameter(typeof(int), "x");

var rightExp = Expression.Constant(rightValue);

var bodyExp = comparerFunc(pExp, rightExp);

var result = Expression.Lambda>(bodyExp, pExp);

return result;

}

Expression> JoinSubFilters(Func expJoiner,

params Expression>[] subFilters)

{

// x

var pExp = Expression.Parameter(typeof(int), "x");

var result = subFilters[0];

foreach (var sub in subFilters[1..])

{

var leftExp = result.Unwrap(pExp);

var rightExp = sub.Unwrap(pExp);

var bodyExp = expJoiner(leftExp, rightExp);

result = Expression.Lambda>(bodyExp, pExp);

}

return result;

}

}

以上就是java动态构建数据库复杂查询实现示例的详细内容,更多关于动态构建数据库复杂查询的资料请关注我们其它相关文章!


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:DHCP中继设置(dhcp中继模式)
下一篇:CCNA(ccna自学难吗)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~