gorm gorm.db_动态建立GORM标准查询

gorm gorm.db

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

原始的HQL包括此

hql.append " AND e.aclObjectIdentity.aclClass.id=:aclClass"

转换后的标准代码为

aclObjectIdentity {
   aclClass {
      eq 'id', params.long('aclClass')
   }
}

整个查询与此类似:

def results = lookupClass().createCriteria().list(max: max, offset: offset) {
   // other standard criteria method calls

   if (params.aclClass) {
      aclObjectIdentity {
         aclClass {
            eq 'id', params.long('aclClass')
         }
      }
   }
}

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

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

aclObjectIdentity({
   aclClass({
      eq('id', params.long('aclClass'))
   })
})

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

def aclClassClosure = {
   eq('id', params.long('aclClass'))
}

aclObjectIdentity({
   aclClass(aclClassClosure)
})

然后

def aclClassClosure = {
   eq 'id', params.long('aclClass')
}

def aclObjectIdentityClosure = {
   aclClass(aclClassClosure)
}

aclObjectIdentity(aclObjectIdentityClosure)

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

Department

class Department {
   String name
}

Manager

class Manager {
   String name
   Department department
}

Employee

class Employee {
   String name
   Manager manager
}

我们创建一些实例:

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()

后来想运行查询:

Employee.createCriteria().list(max: 10, offset: 0) {
   eq 'name', 'employee1'

   manager {
      department {
         eq 'name', 'department1'
      }
   }
}

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

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等,因此,当您在条件闭包中进行这些调用时,它们将在构建器上运行。

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

def runCriteria(Class clazz, List<Closure> criterias, Map paginateParams) {
   clazz.createCriteria().list(paginateParams) {
      for (Closure criteria in criterias) {
         criteria.delegate = delegate
         criteria()
      }
   }
}

这意味着我们可以拆分

Employee.createCriteria().list(max: 10, offset: 0) {
   eq 'name', 'employee1'

   manager {
      department {
         eq 'name', 'department1'
      }
   }
}

进入

def closure1 = {
   eq 'name', 'employee1'
}

def closure2 = {
   manager {
      department {
         eq 'name', 'department1'
      }
   }
}

并运行为

runCriteria Employee, [closure1, closure2], [max: 10, offset: 0]

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

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

def projection = buildProjection('manager.department',
                                 'eq', ['name', 'department1'])
runCriteria Employee, [closure1, projection], [max: 10, offset: 0]

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

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
}

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

department {
   eq 'name', 'department1'
}

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

delegate.department({
   eq 'name', 'department1'
})

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

String methodName = 'department'

delegate."$methodName"({
   eq 'name', 'department1'
})

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

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

def closure = { ->
   delegate."$criterionMethod"(args)
}

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

def invoker = { String projectionName, Closure subcriteria ->
   delegate."$projectionName"(subcriteria)
}

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

for (String projectionName in (path.split('\\.').reverse())) {
   closure = invoker.clone().curry(projectionName, closure)
}

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值