我是靠谱客的博主 任性灯泡,这篇文章主要介绍gorm gorm.db_动态建立GORM标准查询,现在分享给大家,希望可以做个参考。

gorm gorm.db

我最初使用HQL在spring-security-ui插件中编写了大多数查询,因为我发现它比标准查询更直观,但是HQL仅适用于Hibernate和关系数据库。 拉取请求将查询更新为使用标准查询,以允许该插件与NoSQL数据存储一起使用,但是一个查询与我使用的编程风格不匹配。 没什么大不了的,但是由于许多控制器代码基本上都是CRUD代码,并且与其他代码非常相似,因此我尝试使代码保持通用并将共享逻辑推入基类。

原始的HQL包括此

复制代码
1
hql.append " AND e.aclObjectIdentity.aclClass.id=:aclClass"

转换后的标准代码为

复制代码
1
2
3
4
5
aclObjectIdentity { aclClass { eq 'id', params.long('aclClass') } }

整个查询与此类似:

复制代码
1
2
3
4
5
6
7
8
9
10
11
def results = lookupClass().createCriteria().list(max: max, offset: offset) { // other standard criteria method calls if (params.aclClass) { aclObjectIdentity { aclClass { eq 'id', params.long('aclClass') } } } }

这让我开始思考创建一种通用表示两级投影和标准的方法。

如果我们恢复省略的可选括号,代码将变为

复制代码
1
2
3
4
5
aclObjectIdentity({ aclClass({ eq('id', params.long('aclClass')) }) })

因此,应该更加清楚这是方法调用序列; 调用aclObjectIdentity具有关闭的说法,然后aclClass具有关闭的说法,最后eqStringlong争论。 将闭包拆分为局部变量使它更清晰,首先是

复制代码
1
2
3
4
5
6
7
def aclClassClosure = { eq('id', params.long('aclClass')) } aclObjectIdentity({ aclClass(aclClassClosure) })

然后

复制代码
1
2
3
4
5
6
7
8
9
def aclClassClosure = { eq 'id', params.long('aclClass') } def aclObjectIdentityClosure = { aclClass(aclClassClosure) } aclObjectIdentity(aclObjectIdentityClosure)

为了更具体一点,可以说我们有三个域类。

Department

复制代码
1
2
3
class Department { String name }

Manager

复制代码
1
2
3
4
class Manager { String name Department department }

Employee

复制代码
1
2
3
4
class Employee { String name Manager manager }

我们创建一些实例:

复制代码
1
2
3
4
Department d = new Department(name: 'department1').save() Manager m = new Manager(name: 'manager1', department: d).save() Employee e1 = new Employee(name: 'employee1', manager: m).save() Employee e2 = new Employee(name: 'employee2', manager: m).save()

后来想运行查询:

复制代码
1
2
3
4
5
6
7
8
9
Employee.createCriteria().list(max: 10, offset: 0) { eq 'name', 'employee1' manager { department { eq 'name', 'department1' } } }

我的目标是仅使用一些辅助方法来表示此查询,并且不使用任何闭包(或尽可能少的闭包)。 像上面那样将其拆分

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def departmentClosure = { eq 'name', 'department1' } def managerClosure = { department(departmentClosure) } def criteriaClosure = { eq 'name', 'employee1' manager(managerClosure) } Employee.createCriteria().list([max: 10, offset: 0], criteriaClosure)

运行查询时,使用Hibernate时会把criteriaClosure的委托设置为HibernateCriteriaBuilder的实例,或者是MongoDB或您使用的任何其他GORM实现的类似构建器。 构建器已经为eq定义了方法, likebetween等,因此,当您在条件闭包中进行这些调用时,它们将在构建器上运行。

事实证明,如果将闭包拆分为多个闭包并以生成器作为每个闭包的委托进行调用,则其工作方式相同。 因此,这样的方法有效:

复制代码
1
2
3
4
5
6
7
8
def runCriteria(Class clazz, List<Closure> criterias, Map paginateParams) { clazz.createCriteria().list(paginateParams) { for (Closure criteria in criterias) { criteria.delegate = delegate criteria() } } }

这意味着我们可以拆分

复制代码
1
2
3
4
5
6
7
8
9
Employee.createCriteria().list(max: 10, offset: 0) { eq 'name', 'employee1' manager { department { eq 'name', 'department1' } } }

进入

复制代码
1
2
3
4
5
6
7
8
9
10
11
def closure1 = { eq 'name', 'employee1' } def closure2 = { manager { department { eq 'name', 'department1' } } }

并运行为

复制代码
1
runCriteria Employee, [closure1, closure2], [max: 10, offset: 0]

但是我们如何才能使该投影成为通用? 这是一个内部方法调用,包装在一个或多个闭包中,这些闭包向下投影到另一个域类。

我最终想要的是能够使用内部条件调用指定投影而不关闭:

复制代码
1
2
3
def projection = buildProjection('manager.department', 'eq', ['name', 'department1']) runCriteria Employee, [closure1, projection], [max: 10, offset: 0]

这是执行此操作的buildProjection方法:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Closure buildProjection(String path, String criterionMethod, List args) { def invoker = { String projectionName, Closure subcriteria -> delegate."$projectionName"(subcriteria) } def closure = { -> delegate."$criterionMethod"(args) } for (String projectionName in (path.split('\.').reverse())) { closure = invoker.clone().curry(projectionName, closure) } closure }

要了解其工作原理,请再次查看最里面的闭包:

复制代码
1
2
3
department { eq 'name', 'department1' }

实际上,这将作为对委托的方法调用被调用

复制代码
1
2
3
delegate.department({ eq 'name', 'department1' })

Groovy让我们使用GString动态调用方法,所以这与

复制代码
1
2
3
4
5
String methodName = 'department' delegate."$methodName"({ eq 'name', 'department1' })

因此,我们可以将嵌套的闭包表示为内部闭包,作为其包含的闭包的闭包参数调用,并作为其包含的闭包的闭包参数调用,依此类推,直到用完所有级别。

然后我们可以构建一个调用eq 'name', 'department1'的闭包(或带有参数的任何标准方法,这只是一个简化的示例),如下所示:

复制代码
1
2
3
def closure = { -> delegate."$criterionMethod"(args) }

因此,为了表示嵌套的闭包,请从“调用者”闭包开始:

复制代码
1
2
3
def invoker = { String projectionName, Closure subcriteria -> delegate."$projectionName"(subcriteria) }

并依次在每个嵌套级别对其进行克隆,并对其进行咖喱处理以嵌入投影名称及其内部闭包,因为条件构建器不希望使用任何闭包参数,从内而外进行工作:

复制代码
1
2
3
for (String projectionName in (path.split('\.').reverse())) { closure = invoker.clone().curry(projectionName, closure) }

因此,最后,我们可以将分解后的查询作为具有标准条件方法调用的一个或多个“核心”条件闭包,以及零个或多个派生的投影闭包来运行:

复制代码
1
2
3
4
5
6
7
def criteria = { eq 'name', 'employee1' } def projection = buildProjection('manager.department', 'eq', ['name', 'department1']) runCriteria Employee, [criteria, projection], [max: 10, offset: 0]

老实说,我怀疑这里有很多重用的潜力,但是通过这一工作帮助我更好地了解了GORM如何运行标准查询。 我将在下个月的Greach上讨论此主题以及其他一些GORM主题,因此,如果您觉得有趣,请务必查看该谈话的录音。

翻译自: https://www.javacodegeeks.com/2016/03/building-gorm-criteria-queries-dynamically.html

gorm gorm.db

最后

以上就是任性灯泡最近收集整理的关于gorm gorm.db_动态建立GORM标准查询的全部内容,更多相关gorm内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(50)

评论列表共有 0 条评论

立即
投稿
返回
顶部