2016-06-13 10 views
0

In meinem Grails Webapp habe ich die folgenden Domain-Klassen:Grails 3: Verwendung findAll mit Join-Tabellen

class Customer { 
    static hasMany = [ 
     addresses: Address 
    ] 

    static mapping = { 
     addresses (cascade: "all-delete-orphan", joinTable: [name: "bs_core_customer_addresses"]) 
    } 
} 

class Address { 
    ... 
} 

Jetzt möchte ich die abillity implementieren die 1 zu filtern: n-Beziehung wie Adressen für Dinge wie Land (sollte dynamisch sein, dh der Benutzer kann selbst verschiedene Filter hinzufügen).

Was ist der beste Weg, dies zu erreichen?

  1. Filtern der Sammlung aus dem Customer Objekt? z.B. customer.addresses.findAll{...}
  2. Direkte Abfrage von der Datenbank? Wie kann ich die Einschränkung für die Customer<->Address Beziehung hinzufügen. belongsTo bei der Domänenklasse Address ist keine Option, da das Address Objekt in mehreren 1: n-Beziehungen verwendet wird. z.B. Customer.findAll(...)
  3. Eine andere Option?

Antwort

0

Sie sollten mit

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') 
     } 
    } 

Die oben sollte selbsterklärend, aber gelesen zu durch Ihren Beitrag ein paar Mal jetzt wegzukommen der Lage sein, ich glaube, ich habe ein besseres Verständnis von dem, was Sie versuchen, zu erreichen und es gibt wahrscheinlich ein paar verschiedene Möglichkeiten, es zu tun. Also lassen Sie uns einige von ihnen erkunden:

Mit HQL-Abfrage können Sie dies zu einer anderen Methode ändern, bevorzuge ich 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) 
    } 
} 

Jetzt ist der Service aber eigentlich brauchen Sie nicht in Domain-Klasse auszuführen, es sei denn es eine andere Notwendigkeit ist, für die Anzeige usw. Sie immer diese Art von Service aus einem Controller Anruf nennen könnte für die Anzeige zu machen

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]) 
} 

ein anderer Ansatz könnte eine dritte Tabelle, die die eine schnelle Suche geben würde hinzugefügt bei jeder Adresse aktualisiert wird:

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 
} 

Dann Registe Wenn Sie eine neue Adresse hinzufügen, müssen Sie die Adress-ID und den Ländercode an diese Domänenklasse anhängen, und ich denke, Sie müssten rückwärtskompatiblen Code schreiben, um dieser Tabelle vorhandene Datensätze hinzuzufügen, damit sie ordnungsgemäß funktionieren.

Ich hinterließ einen Kommentar und entfernte ihn dann für Sie weiter zu erweitern, was oder wie die Filterung stattfand. da, obwohl Sie von country sprechen es kein tatsächlicher Code ist zu zeigen, wie sie paßt alles in.

Ich denke immer noch etwas so einfach wie diese funktionieren würde // Dies wäre ein nur mit all gebundenen Objekten von Adressen finden tun an diesen Kunden gebunden.so ein Fund innerhalb der hasMany Beziehung Elemente dieses spezifischen Kunden

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

Andere weit Ideen könnte so etwas wie diese

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(',') 
    } 

} 

sein, wenn Ihre tatsächliche CSV-Liste eine Liste von ‚COUNTRY_CODE-ADDRESS_ID‘ enthält dann Sie könnten so abfragen

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) 
+0

Wie könnte eine Einschränkung mir helfen, eine Filterfunktion zu implementieren (zum Beispiel erhalten alle Adressen eines Kunden in den USA)? 'checkAddress' validiert nur, dass dort nicht mehr als 2 Objekte in' addresses' und in 'checkAddress2'' findAllByCustomer' nicht möglich sind, da 'Address' kein' customer' Feld hat – Andreas

+0

obj? .addresses.findAll {it.country = = "US"}? möglicherweise . @Andreas – Vahid

+0

Dies ist eigentlich die Frage, der Nachteil davon ist, dass alle Objekte der Sammlung (in diesem Fall Adressen) aus der Datenbank – Andreas