Grails 3:将findAll与连接表一起使用

时间:2016-06-13 19:38:42

标签: sql grails join database-performance grails-3.0

在我的Grails webapp中,我有以下域类:

DogList->

现在我想实现abillity来过滤像国家这样的地址的1:n关系(应该是动态的,这意味着用户可以自己添加不同的过滤器)。

实现这一目标的最佳方法是什么?

  1. class Customer { static hasMany = [ addresses: Address ] static mapping = { addresses (cascade: "all-delete-orphan", joinTable: [name: "bs_core_customer_addresses"]) } } class Address { ... } 对象过滤集合?例如Customer
  2. 从数据库直接查询?如何添加customer.addresses.findAll{...}关系的限制。 Customer<->Address域类中的belongsTo是没有选项的,因为Address对象用于几个1:n关系。例如Address
  3. 还有其他选择吗?

1 个答案:

答案 0 :(得分:0)

你应该能够逃脱

 static constraints = {
 addresses(validator: checkAddress)
 }
  // This is a static method which is used for validation 
  // and can be used for when inserting a record to check how many 
  // existing addresses exist for the end user that has countryCode of US
  // This is directly bound to all the object the user and will
  // will not be searching the entire DB (A local find limited to user records)
 static def checkAddress={val,obj,errors->
        if (!obj?.addresses.findAll{it.countryCode=='US'}?.size() >= 2) {
            return errors.rejectValue('adress','exceeds limit')
        }
    }  

以上内容应该是自我解释的,但现在已经阅读了几篇文章,我认为我对你想要实现的目标有了更好的理解,并且可能有几种不同的方法。那么让我们来探讨一下:

使用HQL查询,您可以将其更改为其他方法,我更喜欢HQL。

class Customer {
    def userService
  //UserAddress does not have setter transients not essential
   static transients = ['userAddress','userService']

  //This is a protected function that will return an address 
  // object given a countryCode 
  // Execute like this:
  //  Customer cm = Customer.get(customer.id as Long)
  //Address usa = cm.getCountry('US')

   protected Address getUserAddress(String countryCode) {
     return userService.findAddress(countryCode, this.id)
   }
}

现在服务但实际上你不需要在域类中执行,除非有其他需要,为了显示等,你总是可以在控制器调用中调用这种服务来呈现以用于显示目的

class UserSerice {
 // This should return the entire address object back to the domain class 
 // that called it and will be quicker more efficient than findAll 
 Address findAddress(String countryCode, Long customerId) {
    String  query="""
      select address from Address a 
      where a.id :id and countryCode = :code 
    """
   def inputParams=[code:countryCode, id:customerId]
        return Address.executeQuery(query,inputParams,[readOnly:true,timeout:15])
 }

另一种方法可能是第3个表,它会根据添加的每个地址进行更新,以便快速查找:

class Customer {
    static hasMany = [
        addresses: Address
         //probably don't even need this
         //, userCountries:UserCountries
    ]
 }

 Class UserCountries {
  // register customer 
  Customer customer
  String CountryCode
  //maybe address object or Long addressId - depending on if how plain you wanted this to be
  Address address
 }

每次添加新地址时,都会将地址ID和countryCode注册到此域类,我猜您需要编写一些向后兼容的代码,以便将现有记录添加到此表中,以使其正常工作。

我发表评论然后将其删除,以便进一步了解过滤的内容或方式。因为虽然你谈到countryCode,但没有实际的代码来展示它是如何适应的。

我仍然认为这样的事情很简单 //这只会使用绑定到此客户的所有绑定地址对象进行查找。所以在这个特定客户的hasMany关系元素中查找

protected def getCustomAddress(String countryCode) {
   return   addresses.findAll{it.code==countryCode}
}

其他很远的想法可能是这样的

class Customer {
  String _bindAddress 
  List bindAddress=[]

  static transients = [ 'bindAddress' ]

  static constraints = {  
   _bindAddress(nullable:true)
  }

  //you store a flat CSV as _bindAddress
  //you need to work out your own logic to ammend to existing CSV each time address is added 
  // you will also update _bindAddress of this domainClass each time customer gets a hasMany address added
  // so no need for setBindAddress 
 //  void setBindAddress(String b) {
 //    bindAddress=b.split(',')
 // }
   //Inorder to set a list back to flat file 
  //pass in list object
   void setBindAddress(List bindAddress) {
    _bindAddress=bindAddress.join(',')
     /for 1 element this captures better 
    //_bindAddress=bindAddress.tokenize(',').collect{it}
   }
  //This is now your object as a list that you can query for what you are querying.
  List getBindAdress() {
    return _bindAddress.split(',')
  }

}

如果您的实际csv列表包含'COUNTRY_CODE-ADDRESS_ID'列表,那么您可以像这样查询

def found = customer.bindAddress.find{it.startsWith('US-')}
Address usaAddress= Address.get(found.split('-')[1] as Long)
 //Slightly longer explaining above:
def found = customer.bindAddress.find{it.startsWith('US-')}
def record = found.split('-')
String countryCode=record[0]
Long addressId=record[1] as Long
Address usaAddress= Address.get(addressId)