gorm gorm.db
我最初使用HQL在spring-security-ui插件中编写了大多数查询,因为我发现它比标准查询更直观,但是HQL仅适用于Hibernate和关系数据库。 拉取请求将查询更新为使用标准查询,以允许该插件与NoSQL数据存储一起使用,但是一个查询与我使用的编程风格不匹配。 没什么大不了的,但是由于许多控制器代码基本上都是CRUD代码,并且与其他代码非常相似,因此我尝试使代码保持通用并将共享逻辑推入基类。
原始的HQL包括此
1hql.append " AND e.aclObjectIdentity.aclClass.id=:aclClass"
转换后的标准代码为
1
2
3
4
5aclObjectIdentity { aclClass { eq 'id', params.long('aclClass') } }
整个查询与此类似:
1
2
3
4
5
6
7
8
9
10
11def 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
5aclObjectIdentity({ aclClass({ eq('id', params.long('aclClass')) }) })
因此,应该更加清楚这是方法调用序列; 调用aclObjectIdentity
具有关闭的说法,然后aclClass
具有关闭的说法,最后eq
与String
和long
争论。 将闭包拆分为局部变量使它更清晰,首先是
1
2
3
4
5
6
7def aclClassClosure = { eq('id', params.long('aclClass')) } aclObjectIdentity({ aclClass(aclClassClosure) })
然后
1
2
3
4
5
6
7
8
9def aclClassClosure = { eq 'id', params.long('aclClass') } def aclObjectIdentityClosure = { aclClass(aclClassClosure) } aclObjectIdentity(aclObjectIdentityClosure)
为了更具体一点,可以说我们有三个域类。
Department
:
1
2
3class Department { String name }
Manager
:
1
2
3
4class Manager { String name Department department }
和Employee
:
1
2
3
4class Employee { String name Manager manager }
我们创建一些实例:
1
2
3
4Department 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
9Employee.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
15def 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
定义了方法, like
, between
等,因此,当您在条件闭包中进行这些调用时,它们将在构建器上运行。
事实证明,如果将闭包拆分为多个闭包并以生成器作为每个闭包的委托进行调用,则其工作方式相同。 因此,这样的方法有效:
1
2
3
4
5
6
7
8def 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
9Employee.createCriteria().list(max: 10, offset: 0) { eq 'name', 'employee1' manager { department { eq 'name', 'department1' } } }
进入
1
2
3
4
5
6
7
8
9
10
11def closure1 = { eq 'name', 'employee1' } def closure2 = { manager { department { eq 'name', 'department1' } } }
并运行为
1runCriteria Employee, [closure1, closure2], [max: 10, offset: 0]
但是我们如何才能使该投影成为通用? 这是一个内部方法调用,包装在一个或多个闭包中,这些闭包向下投影到另一个域类。
我最终想要的是能够使用内部条件调用指定投影而不关闭:
1
2
3def 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
16Closure 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
3department { eq 'name', 'department1' }
实际上,这将作为对委托的方法调用被调用
1
2
3delegate.department({ eq 'name', 'department1' })
Groovy让我们使用GString
动态调用方法,所以这与
1
2
3
4
5String methodName = 'department' delegate."$methodName"({ eq 'name', 'department1' })
因此,我们可以将嵌套的闭包表示为内部闭包,作为其包含的闭包的闭包参数调用,并作为其包含的闭包的闭包参数调用,依此类推,直到用完所有级别。
然后我们可以构建一个调用eq 'name', 'department1'
的闭包(或带有参数的任何标准方法,这只是一个简化的示例),如下所示:
1
2
3def closure = { -> delegate."$criterionMethod"(args) }
因此,为了表示嵌套的闭包,请从“调用者”闭包开始:
1
2
3def invoker = { String projectionName, Closure subcriteria -> delegate."$projectionName"(subcriteria) }
并依次在每个嵌套级别对其进行克隆,并对其进行咖喱处理以嵌入投影名称及其内部闭包,因为条件构建器不希望使用任何闭包参数,从内而外进行工作:
1
2
3for (String projectionName in (path.split('\.').reverse())) { closure = invoker.clone().curry(projectionName, closure) }
因此,最后,我们可以将分解后的查询作为具有标准条件方法调用的一个或多个“核心”条件闭包,以及零个或多个派生的投影闭包来运行:
1
2
3
4
5
6
7def 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内容请搜索靠谱客的其他文章。
发表评论 取消回复