Änderungen

Zur Navigation springen Zur Suche springen
78.042 Bytes hinzugefügt ,  15:30, 21. Okt. 2020
Kleine Texte verbessert
$ sdk install java 8.0.252.j9-adpt
</syntaxhighlight>Falls dies die erste Installation von Grails mithilfe des SDKMAN! ist, wird diese Version direkt als Standard in allen Umgebungen gesetzt und somit direkt angewendet.<br />
==[http://docs.grails.org/3.3.9/guide/single.html#introduction#creatingAnApplication App erstellen]==<code>$ grails [http://docs.grails.org/3.3.9/ref/Command%20Line/create-app .html create-app] APP-NAME</code> erstellt im derzeitigen Verzeichnis ''innerhalb eines neuen Ordner mit dem angegebenen Namen'' das Grundgerüst für eine neue Grails-Anwendung. ''Das Standardmäßig genutzte [https://github.com/grails-profiles Profil] zur Erstellung des Grundgerüsts ist <code>web</code>.''
===Grails-Befehle ausführen===
===Grails und Tomcat===
Grails bindet standardmäßig einen Tomcat-8 Container mit jeder WAR-Datei ein.<br>'''Da dies aber zu Problemen führen kann''', wenn der Server bereits einen aktiven Tomcat-Container mit einer anderen Version besitzt, kann man diese Abhängigkeit (Dependency) innerhalb der <code>build.gradle</code>-Datei von <code>compile</code> auf <code>provided</code> umstellen:
<syntaxhighlight lang="gradle">
provided "org.springframework.boot:spring-boot-starter-tomcat"
</syntaxhighlight>
Die Tomcat-Version kann man ebenfalls unter <code>build.gradle</code> innerhalb der des <code>dependencies {}</code>-Sektion Abschnitt wie folgt auf zB. Tomcat-7 ändern:
<syntaxhighlight lang="gradle">
ext['tomcat.version'] = '7.0.59'
*Jetty 8
*Oracle Weblogic 12c
*IBM WebSphere 8.0([http://docs.grails.org/3.3.9/guide/single.html#supportedJavaEEContainers *])
<br />
==Automatisches generieren von Artefakten für eine Domänen-Klasse (Scaffolding)==
Die Um schnell mit Grails zu beginnen, ist es oft nützlich, ein Feature namens Scaffoldingzu verwenden, um das Skelett einer Anwendung zu erzeugen. Hierfür gibt es diverse <code>generate-*</code>-Befehle sind eine Art Starthilfe umwie z.B*die benötigten <code>[httpshttp://docs.grails.org/3latest/ref/Command%20Line/generate-all.html generate-all]</code>, das einen [http://docs.3grails.10org/latest/guide/theWebLayer.html#gsp Viewscontrollers Controller] (.gsp),*die dazugehörigen sowie dessen [http://docs.grails.org/latest3.3.9/guide/theWebLayersingle.html#controllers Controllerservices Service]-Klassen für CRUD-Operationen,*die dazugehörigen klasse und Unit-Tests .. ganz einfach (mithilfe eines von Grails im Hintergrund bereits vordefinierten Templates) mit einem Befehl erstellen zu können Mit dem Befehl <code>$ grails und die zugehörigen [httphttps://docs.grails.org/latest3.3.10/refguide/Command%20Line/generate-alltheWebLayer.html generate-all#gsp Views] (PACKET.)KLASSEN-NAME</code> werden von alle Komponenten das Skelett erstellt. erzeugt:
''Möchte man dann aber z.B. seine eigene MySQL-Verbindung einrichten, kann man dies mithilfe simpler Konfigurationsdateien berwerkstelligenbewerkstelligen.''
==[https://docs.grails.org/3.3.9/guide/single.html#config Grundlegende Konfiguration]==
*ConfigSlurper ist eine Utility-Klasse zum Lesen von Konfigurationsdateien, die in Form von Groovy-Skripten definiert sind.
**Wie es bei Java <code>*.properties</code>-Dateien der Fall ist, erlaubt ConfigSlurper eine Punktnotation. Zusätzlich erlaubt es aber auch Closure-Scoped Konfigurationswerte und beliebige Objekttypen.
Zwei Groovy-Konfigurationsdateien sind hierbei verfügbar:
***runtime.groovy
}}
<br />
===[http://docs.grails.org/3.3.9/guide/single.html#_accessing_configuration_with_grailsapplication Konfigurationswerte mithilfe des <code>GrailsApplication</code>-Objekts abrufen]===
Um innerhalb von Controllern/Tag-Libraries auf die Laufzeit-Konfiguration zuzugreifen, gibt es eine spezielle injezierte Variable namens <code>grailsApplication</code> vom Typ [http://docs.grails.org/4.0.0.M1/api/grails/core/GrailsApplication.html GrailsApplication].
</syntaxhighlight>Die <code>config</code>-Eigenschaft des <code>grailsApplication</code>-Objekts ist eine Instanz der [http://docs.grails.org/3.3.9/api/grails/config/Config.html Config]-Schnittstelle und bietet eine Reihe nützlicher Methoden zum Auslesen der Konfiguration der Anwendung.
Insbesondere die <code>getProperty</code>-Methode (siehe oben) ist nützlich, um Konfigurationseigenschaften effizient abzurufen, während der Eigenschaftstyp angegeben wird (der Standardtyp ist String) und/oder ein Standard/Fallback-Wert bereitgestellt wird.
liest und sie zu einem einzigen Objekt zusammenführt.
  Das <code>GrailsApplication</code>-Objekt kann auch ganz einfach in <code>service's</code> und andere Grails-Artifakte wie folgt injected eingebunden werden:<syntaxhighlight lang="groovy">
import grails.core.*
Standardmäßig wird die Protokollierung in Grails 3.0 mithilfe des [http://logback.qos.ch/ Logback]-Frameworks ([http://logback.qos.ch/ Offizielle Dokumentation]) verarbeitet und kann mit der Datei unter <code>grails-app/conf/logback.groovy</code> konfiguriert werden.
====LoggerHier ist ein Beispiel meiner zusammengebastelten LogBack-Groovy-Namen===Konfiguration:<syntaxhighlight lang="groovy">import ch.qos.logback.core.util.FileSizeimport grails.util.BuildSettingsimport grails.util.Environmentimport org.springframework.boot.ApplicationPidimport org.springframework.boot.logging.logback.ColorConverterimport org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter // Set "PID"-Environment-Property to the one assigned to the GrailsApplication if it has not already been set -Artifakte We'll use it in the logging output.if (!System.getProperty("PID")) { System.setProperty(Controller"PID", Services(new ApplicationPid()).toString())} // Mimic Spring Boot logging configuration:conversionRule 'clr', ColorConverterconversionRule 'wex', WhitespaceThrowableProxyConverterdef COLORFUL_AND_VERBOSE_PATTERN = '%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} ' + // Date '%clr(%5p) ' + // Log level '%clr(%property{PID}){magenta} ' + // PID '%clr(---){faint} %clr([%15.15t]){faint} ' + // Thread '%clr(%-40.40logger{39}){cyan} %clr(:) wird automatisch eine <code>log<{faint} ' + // Logger '%m%n%wex' //code> Methode injeziertMessagedef UNCOLORFUL_HUMANOID_PATTERN = '%d{yyyy-MM-dd} | %d{HH:mm:ss.SSS} | %-20.20thread | %5p | %-25.25logger{25} | %12(ID: %8mdc{id}) | %m%n'
*Vor Grails 3// See http://logback.3qos.0 folgte der Name des Loggers für Grails Artefakt der Konvention <code>grails.app.<type>.<className><ch/code>, wobei <code>type<manual/code> für den Artefakt-Typ steht, zgroovy.html for details on configuration.B. Controller oder Dienste, und <code>className</code> für den voll qualifizierten Namen des Artefaktsdef HOME_DIR = "."
*Ab Grails 3.3.x wurden die Logger-Namen vereinfacht.// DEFINE Appenderappender('STDOUT', ConsoleAppender) { encoder(PatternLayoutEncoder) { pattern = COLORFUL_AND_VERBOSE_PATTERN }}
// DEFINE Appenderappender("ROLLING", RollingFileAppender) { encoder(PatternLayoutEncoder) { pattern = "%level %logger - %msg%n" } // TimeBasedRollingPolicy implement both the TriggeringPolicy (which specifies when the rollover should occur) and the RollingPolicy (How to perform the rollover) // See http://logback.qos.ch/manual/appenders.html#TimeBasedRollingPolicy for Documentation rollingPolicy(TimeBasedRollingPolicy) { /* * TimeBasedRollingPolicy gets both it's rolling behavior (creating a new log file with the current date/time in the file name) and it's triggering behavior (rollover will occur based on the specified timestamp pattern) from the fileNamePattern property. * The trigger policy is the most interesting part here - it takes an approach that bases the rollover occurrence on how specific you define the timestamp in the fileNamePattern. So if you specify down to the month, rollover will occur each month. Specify a pattern down to the day, and it will occur daily). * It's easier to understand when you see it in action, so here's some example patterns taken from Logback's documentation: fileNamePattern = "/myApp-log.%d{yyyy-MM}.log" //Rollover at the beginning of each month, file format: myApp-log.2016-11.log fileNamePattern = "/myApp-log.%d{yyyy-ww}.log" //Rollover at the first day of each week. Note that the first day of the week depends on the locale. fileNamePattern = "/myApp-log.%d{yyyy-MM-dd_HH}.log" //Rollover at the top of each hour. * * Note that in the above examples we are configuring the timestamp in the filename of the log files. We can also use the timestamp to create a file directory structure, like this example: fileNamePattern = "/logs/%d{yyyy/MM}/myApp.log" // Rollover at the beginning of each month. // Each log file will be stored in a year/month directory, e.g: /logs/2016/11/myApp.log, /logs/2016/12/myApp.log, /logs/2017/01/myApp.log * * Finally, adding a zip or gz file extension to our fileNamePattern will apply the selected compression to the rolled-over log files: fileNamePattern = "/myApp-log.%d{yyyy/MM}.gz" //Rollover at the beginning of each month, compress the rolled-over file with GZIP */ fileNamePattern = "${HOME_DIR}/logs/myApp-%d{yyyy-MM-dd_HH}.log" // maxHistory sets the upper limit on how many log files to preserve (when the max is reached the oldest file is deleted) maxHistory = 30 // totalSizeCap sets a cap on how much disk space our log files are allowed to use (again, when the cap is reached the oldest files are deleted). totalSizeCap = FileSize.valueOf("2GB") }} // Finally, let's specify that we want our new RollingFileAppender to be used in production mode only, while keeping the default ConsoleAppender for development mode:def targetDir = BuildSettings.TARGET_DIRif (Environment.isDevelopmentMode() && targetDir != null) { println("Using development logging configuration. targetDir = " + targetDir.getAbsolutePath()) appender("FULL_STACKTRACE", FileAppender) { file = "${targetDir}/stacktrace.log" append = true encoder(PatternLayoutEncoder) { pattern = UNCOLORFUL_HUMANOID_PATTERN } }  logger("StackTrace", ERROR, ['FULL_STACKTRACE'], false) root(ERROR, ['STDOUT', 'FULL_STACKTRACE'])} else { println("Using rolling production logging configuration. HOME_DIR = " + new File(HOME_DIR).getAbsolutePath()) root(ERROR, ['ROLLING'])} </syntaxhighlight> ====Logger-Namen====Grails-Artifakte (Controller, Services, ...) wird automatisch eine <code>log</code> Methode injeziert. *Vor Grails 3.3.0 folgte der Name des Loggers für Grails Artefakt der Konvention <code>grails.app.<type>.<className></code>, wobei <code>type</code> für den Artefakt-Typ steht, z.B. Controller oder Dienste, und <code>className</code> für den voll qualifizierten Namen des Artefakts. *Ab Grails 3.3.x wurden die Logger-Namen vereinfacht. Das nächste Beispiel veranschaulicht die Änderung:{| class="wikitable"|+|!'''Logger Name''' '''(Grails 3.3.x oder höher)'''
!'''Logger Name'''
'''(Grails 3.2.x oder niedrieger)'''
|com.company.BookController
|}
<br [https:/>/docs.grails.org/3.3.10/guide/single.html#externalLoggingConfiguration Logging-Konfigurationsdatei bestimmen]
====[https://docs.grails.org/3.3.10/guide/single.html#externalLoggingConfiguration Logging-Konfigurationsdatei bestimmen]====
Um zu bestimmen, von welcher Datei Logback seine Konfigurationswerte lesen soll, gibt es 3 Möglichkeiten:
break
}
</syntaxhighlight><br />
==[http://docs.grails.org/latest/guide/single.html#dataSource DataSource]==
Weil Grails auf Java aufbaut sollte man ein bisschen Verständnis von JDBC ([https://de.wikipedia.org/wiki/Java_Database_Connectivity Java Database Connectivity], ''die'' Datenbankschnittstelle der Java-Plattform) besitzen.<br>
===Treiber einfügen===
*<code>'''username'''</code>, <code>'''password'''</code> - Login-Daten, um die JDBC-Verbindung aufzubauen
*<code>jndiName</code> - Der Name der JNDI-Ressource für die DataSource
*<u><code>'''dbCreate'''</code> - Gibt an, ob die Datenbank automatisch aus dem Domänen-Modell generiert werden soll: "create-drop", "create", "update" oder "validate".</u>
**<code>create</code> - Falls sich das Datenbank-Schema verändert hat, wird die Tabelle entleert und mithilfe des neuen Schemas erstellt
**<code>'''create-drop'''</code> - Gleich wie <code>create</code>, '''leert die Datenbank (!)''' hingegen bei <u>jedem</u> neustart (Egal ob sich das Schema geändert hat oder nicht, nützlich zum Testen)
**<code>'''update'''</code> - Aktualisiert das Model der Tabelle, falls es sich verändert hat ('''Die Daten bleiben erhalten''', Die Domainklassen-Tabelle muss existieren) Notiz: Solch eine Weise veträgt natürlich nicht viele Änderung des Schemas. (zB. Beim ändern des Namens einer Spalte bleibt die alte Spalte mit den existierenden Daten und die neue wird einfach hinzugefügt)
**''<code>validate</code> - Warnt vor Änderungen, aber verändert das Datenbank-Schema bei Veränderung '''nicht'''''
**<code>none</code> - ''Siehe unten angeführte Notiz''
*<code>pooled</code> - Ob ein Pool von Verbindungen aufgebaut werden sollte (Standard: true)
*<code>lazy</code> - Ob ein <code>LazyConnectionDataSourceProxy</code> verwendet werden soll
==[http://docs.grails.org/3.3.9/guide/single.html#applicationClass Die <code>Application</code>-Klasse]==
Jede neue Grails-Anwendung verfügt über eine Klasse namens <code>Application .groovy</code> innerhalb des Verzeichnisses Verzeichniss <code>grails-app/init</code>.
Die <code>Application</code>-Klasse ist eine Unterklasse der <code>[http://docs.grails.org/3.3.9/api/grails/boot/config/GrailsAutoConfiguration.html GrailsAutoConfiguration]</code>-Klasse und verfügt über die <code>static void main</code>, d.h. sie kann als reguläre Anwendung ausgeführt werden.<br />
Wenn man eine IDE verwendet, kann man ganz einfach mit der rechten Maustaste auf die Klasse klicken und sie direkt von der IDE aus starten, wodurch Ihre Grails-Anwendung gestartet wird.
Dies ist auch für das Debugging nützlich, da Sie man hiermit <u>direkt von der IDE aus debuggen können</u> kann, ohne einen Remote-Debugger anschließen zu müssen, wenn Sie den mit dem Befehl <code>run-app --debug-jvm </code> von der Befehlszeile aus ausführenextern anschließen zu müssen.
Man kann die Anwendung auch z.B. in eine lauffähige WAR-Datei packen (nützlich, wenn man plant die Anwendung mit einem containerlosen Kontainerlosen Ansatz zu implementieren.):<syntaxhighlight lang="bash">
$ grails package
$ java -jar build/libs/myapp-0.1.war
</syntaxhighlight>
  <br />=[https://docs.grails.org/3.3.9/guide/commandLine.html Grails Scripte]=
''Das Befehlszeilensystem von Grails 3.0 unterscheidet sich '''stark''' von früheren Versionen von Grails und verfügt über APIs zum Aufrufen von Gradle für build-bezogene Aufgaben sowie zur Codegenerierung.''
Wenn man den folgenden Befehl eingibt, durchsucht Grails das [https://bintray.com/grails/profiles Profil-Repository] auf der Grundlage des Profils der aktuellen Anwendung. Wenn das Profil für eine Web-Anwendung ist, werden Befehle aus dem Web-Profil und dem Basis-Profil, von dem es erbt, gelesen.<syntaxhighlight>
$ grails <<command name>>
</syntaxhighlight>
 
Es wird zuerst die Anwendung und dann das Profil nach Befehlen durchsucht. Beispiel am Befehl <code>run-app</code>:
==[https://docs.grails.org/3.3.9/guide/commandLine.html Grails-Scripte erstellen]==
Mit dem Befehl <code>$ grails [https://docs.grails.org/3.3.9/ref/Command&#x20;Line/create-script.html create-script] NAME</code> kann man ein Grund-Gerüst für sein neues Skript unter <code>src/main/scripts/</code> erstellen lassen.<blockquote>Im Allgemeinen sollten Grails-Skripte für die Skripterstellung des Gradle-basierten Build-Systems und für die Code-Generierung verwendet werden. Skripte können keine Anwendungsklassen laden und sollten dies auch nicht tun, da Gradle zur Konstruktion des Anwendungs-Klassenpfades erforderlich ist.</blockquote><br />
==description()==
Jedes Skript erbt (unter anderem)
*von [https://docs.grails.org/3.3.9/ref/Command&#x20;Line/create-script.html GroovyScriptCommand], welches eine API für viele nützliche Aufgaben bereitstellt, sowie
*von [https://docs.grails.org/3.3.9/api/org/grails/cli/profile/commands/templates/TemplateRenderer.html TemplateRenderer], welches die zur Code-Generierung genutzten <code>render</code>/<code>template</code>-Methoden zur Verfügung stellt. (Siehe Beispiel unten)
 
 
Die description()-Methode wird für die Ausgabe vom <code>grails help</code> Befehl verwendet, um den Nutzern zu helfen.<br>Beispiel am <code>generate-all</code> Befehl:<syntaxhighlight lang="groovy">
argument name:'Domain Class', description:'The name of the domain class'
}
</syntaxhighlight><br />
==[https://docs.grails.org/3.3.9/guide/commandLine.html#_the_model model()]==
Wenn man <code>model()</code> mit einer Klassen/String/Datei/Resource-Parameter aufruft, erhält man eine neue Instanz der Klasse <code>[http://docs.grails.org/4.0.0.M1/api/grails/codegen/model/Model.html Model]</code> welche hiflreiche Methoden zur Quellcode-Generierung besitzt.
 
 
=TODO #6 erstmals ausgelassen=
Unter der Haube verwendet es '''Hibernate''' (eine sehr beliebte und flexible Open-Source-ORM-Lösung), und dank der dynamischen Natur von Groovy mit seiner statischen und dynamischen Typisierung sowie der Konvention von Grails ist bei der Erstellung von Grails-Domänenklassen '''weitaus weniger Konfiguration erforderlich'''.
*''Es gibt auch noch andere Implementation wie "[http://gorm.grails.org/latest/mongodb/manual GORM for MongoDB]".''
'''In dieser Anleitung befasse ich mich mit''' ''(den in meinen Augen wichtigsten Wissen/..)'' '''der originalen Implementierung "GORM for Hibernate" in der Version 6.1.x (die mit Grails 3.3.9 ausgeliefert wird).'''
'''In dieser Anleitung befasse ich mich mit''' ''(den in meinen Augen wichtigsten WissenEs gibt auch noch andere Implementation wie "[http://gorm.grails.)'' '''der originalen Implementierung "org/latest/mongodb/manual GORM for HibernateMongoDB]" in der Version 6.1.x (die mit Grails 3.3.9 ausgeliefert wird).'''
*Manche für mich eher unwichtige Kapitel werde ich hier nur insofern ansprechen, in dem ich einen Hyperlink mit dem Text "Siehe Offizielle Dokumentation" einfüge.
<br />
==Eine andere Hibernate-Version definieren==
Siehe [http://gorm.grails.org/6.1.x/hibernate/manual/#hibernateVersions Offizielle Dokumentation]<br />
==GORM in einer Spring-Boot-Applikation verwenden==
Siehe [http://gorm.grails.org/6.1.x/hibernate/manual/#springBoot Offizielle Dokumentation]<br />
==[http://gorm.grails.org/6.1.x/hibernate/manual/#outsideGrails GORM ausserhalb von Grails oder Spring verwenden]==
runtime "org.apache.tomcat.embed:tomcat-embed-logging-log4j:8.5.0"
runtime "org.slf4j:slf4j-api:1.7.10"
</syntaxhighlight><blockquote>Das obige obere Beispiel verwendet die [https://de.wikipedia.org/wiki/H2_Database H2-Datenbank ] und den Tomcat-Verbindungspool. Es werden jedoch auch andere Pool-Implementierungen unterstützt, einschließlich <code>[https://commons.apache.org/proper/commons-dbcp/ commons-dbcp]</code>, <code>[https://tomcat.apache.org/tomcat -7.0-doc/jdbc-pool.html#Introduction tomcat pool]</code> oder <code>hikari<[https://code>github. Wenn kein Verbindungspool angegeben ist, wird <code>org.springframework.jdbc.datasource.DriverManagerDataSourcecom/brettwooldridge/HikariCP hikari]</code> verwendet, die bei jeder Verbindungsanforderung eine neue Verbindung zur Datenbank herstellt.  *Letzteres wird wahrscheinlich Probleme mit einer H2-In-Memory-Datenbank verursachen, da bei jeder Verbindungsanforderung eine neue In-Memory-Datenbank erstellt wird, wodurch zuvor erstellte Tabellen verloren gehen. Normale Datenbanken (MySql, Postgres oder sogar dateibasierte H2-Datenbanken) sind nicht betroffen.</blockquote>
Wenn kein Verbindungspool angegeben ist, wird <code>org.springframework.jdbc.datasource.DriverManagerDataSource</code> verwendet, die bei jeder Verbindungsanforderung eine neue Verbindung zur Datenbank herstellt. Dies wird wahrscheinlich Probleme mit einer H2-In-Memory-Datenbank verursachen, da bei jeder Verbindungsanforderung eine neue In-Memory-Datenbank erstellt wird, wodurch zuvor erstellte Tabellen verloren gehen. Normale Datenbanken (MySql, Postgres oder sogar dateibasierte H2-Datenbanken) sind nicht betroffen. </blockquote>
Erstellen Sie dann Ihre Entities im Verzeichnis <code>src/main/groovy</code> und annotieren Sie sie mit der Annotation <code>grails.gorm.annotation.Entity</code>:<syntaxhighlight lang="groovy">
}
</syntaxhighlight>
 
 
Schlussendlich muss man die Bootstrap-Logik in seine Applikation einfügen, die <code>HibernatDatastore</code> verwendet: (Siehe die [http://gorm.grails.org/6.1.x/hibernate/manual/#configuration Offzielle Dokumentation] für mehr Informationen zur Konfiguration von GORM)<syntaxhighlight lang="groovy">
]
HibernateDatastore datastore = new HibernateDatastore( configuration, Person)
</syntaxhighlight><br />
==[http://gorm.grails.org/6.1.x/hibernate/manual/#configuration Konfiguration]==
Siehe [[Groovy als Web-Backend#DataSource|das bereits besprochene Kapitel "DataSource"]] und die [http://gorm.grails.org/6.1.x/hibernate/manual/#configuration Offizielle Dokumentation] <br />
===Standard-Mappings und Constraints===
Besondere Erwähnung verdienen die Einstellungen <code>grails.gorm.default.mapping</code> und <code>grails.gorm.default.constraints</code>. Diese definieren die Standard-ORM-Zuordnung Zuordnungen/DSL's und die von jeder Entität verwendeten Standard-Validierungseinschränkungen.
<br />
====Ändern der Standard-Datenbankzuordnung====
Möglicherweise hat man den Grund, die Art und Weise zu ändern, wie '''alle''' Domänenklassen auf die Datenbank abgebildet werden.
Beispielsweise sind standardmäßig alle Eigenschaften von GORM-Klassen nicht <code>nullable</code>. Das bedeutet, dass für jede Eigenschaft ein Wert angegeben werden muss (Ansonsten kommt es zu einem Validierungsfehler).
In den meisten Fällen ist es das, was Sie wollen, aber wenn Sie es mit einer großen Anzahl von Spalten zu tun haben, kann es sich als unbequem erweisen. Man kann die Standardbeschränkungen jedoch über die Groovy-Konfiguration mit der Einstellung <code>grails.gorm.default.constraints</code> ändern:<syntaxhighlight lang="groovy">
// grails-app/conf/application.groovy
grails.gorm.default.constraints = {
*'''Die SQL-Typen werden automatisch von den Java-Typen erkannt''', können aber mit [http://gorm.grails.org/6.1.x/hibernate/manual/#constraints Constraints] oder der [http://gorm.grails.org/6.1.x/hibernate/manual/#ormdsl ORM-DSL] angepasst werden.
<br />==[http://gorm.grails.org/6.1.x/hibernate/manual/#gormAssociation Domain-Modelling in GORM: EinführungAssoziationen]==
Assoziationen definieren wie Domänenklassen miteinander interagieren. Wenn nicht an beiden Enden explizit angegeben, existiert eine Beziehung nur in der Richtung, in der sie definiert ist.
===Unidirektionales Viele-zu-Einsohne Kaskadierung===
In diesem Fall haben wir eine '''uni'''direktionale Viele-zu-Eins-Beziehung von <code>Face</code>-to-<code>Nose</code>.
{| class="wikitable"
class Nose {}
</syntaxhighlight>
|Bemerke: Der Fremdschlüssel liegt beim Besitzer (vgl. Eins-zu-Eins mithilfe von hasOne)[[Datei:Grails-gorm-unidirectional many to one-db schema view.png|zentriert]]
|[[Datei:Grails-gorm-unidirectional many to one-dependency association view.png|zentriert|alternativtext=|219x219px]]
|}
}
</syntaxhighlight>
|Bemerke: Die Struktur bleibt gleich wie beim unidirektionalen ManyViele-tozu-OneEins ohne Kaskadierung![[Datei:Grails-gorm-unidirectional many to one-db schema view.png|zentriert]]|Bemerke: Die Struktur bleibt gleich wie beim unidirektionalen ManyViele-tozu-OneEins ohne Kaskadierung![[Datei:Grails-gorm-unidirectional many to one-dependency association view.png|zentriert|alternativtext=|219x219px]]
|}
In diesem Fall verwenden wir die Einstellung <code>belongsTo</code>, um zu sagen, dass <code>Nose</code> zum <code>Face</code> "gehört": <blockquote>Definiert <code>belongsTo</code> definiert eine "Zugehörigkeit zu" -Beziehung, in der die von <code>belongsTo</code> angegebene Klasse das Eigentum an der Beziehung übernimmt. Dadurch wird gesteuert, wie die Aktionen des Speicherns und Löschen kaskadieren. Das genaue Verhalten hängt von der Art der Beziehung ab:
*Viele-zu-Eins / <u>Eins-zu-Eins</u>: Die Aktionen '''Speichern und Löschen kaskadieren''' vom Eigentümer zum Abhängigen (die Klasse mit <code>belongsTo</code>).
*Eins-zu-viele: Die Aktion des '''Speichern kaskadiert''' immer von der "Eins" Seite zur "Viele" Seite. Wenn die "Viele" Seite auch ein <code>belongsTo</code> besitzt, kaskadiert auch das Löschen in diese Richtung gelöscht.
*Viele-zu-Viele: Nur die Aktion des '''Speichern kaskadiert''' vom Eigentümer zum Abhängigen, '''das Löschen kaskadiert hingegen nie.'''
</syntaxhighlight>
|
|}<br />===Unidirektionales Viele-Zu-Eins mithilfe von belongsTo===Eine Sache, die Menschen oft verwirrt, ist, dass <code>belongsTo</code> zwei verschiedene Syntaxen unterstützt.  Die oben verwendete Syntax definiert nicht nur eine einfache Kaskadierung zwischen zwei Klassen, sondern fügt auch eine entsprechende Rückrefrenz zum Modell hinzu, wodurch die Beziehung automatisch in eine bidirektionale Beziehung umgewandelt wird.
===Bidirektionales Eins-zu-Eins mithilfe von hasOne===Um die obige Beziehung einfach nur das Kaskadierungsverhalten zu einer echten Eins-zu-Eins-Assoziation zu machen, verwendet definieren (ohne Rückrefrenz auf den "Eigentümer") kann man die Eigenschaft <code>hasOne</code> auf der besitzenden Seite.<blockquote><code>[httpfolgende Syntax benutzen://docs.grails.org/3.3.9/ref/Domain%20Classes/hasOne.html hasOne]</code> definiert eine bidirektionale Eins-zu-Eins-Zuordnung zwischen zwei Klassen, in denen sich der Fremdschlüssel im Kind befindet.</blockquote>
{| class="wikitable"
|+Beispiel einer Bidirektionalen EinsUnidirektionalen Viele-zu-Eins Beziehung von <code>Face</code> zu <code>Nose</code>.
!Domänenklasse
!IntelliJ's Datenbank-Schema-Ansicht dieser Domänenklasse
|<syntaxhighlight lang="groovy" line="1">
class Face {
static hasOne = [Nose nose:Nose]
}
class Nose {
static belongsTo = Face face
}
</syntaxhighlight>
|Bemerke: Die Struktur bleibt gleich wie beim bidirektionalen Many-to-One![[Datei:Grails-gorm-bidirectional one unidirectional many to one-db schema view.png|alternativtext=|zentriert]]|Bemerke: Die Struktur bleibt gleich wie beim bidirektionalen Many-to-One![[Datei:Grails-gorm-bidirectional one unidirectional many to one-dependency association view.png|zentriert|alternativtext=|zentriert|224x224px219x219px]]
|}
Beachte: Die Verwendung dieser Eigenschaft verlagert den Fremdschlüssel auf die Kindes-Tabelle, so dass in diesem Fall die Fremdschlüsselspalte in der <code>nose</code>-Tabelle innerhalb einer Spalte namens <code>face_id</code> gespeichert wird.
===Unidirektionales Eins-zu-Eins mithilfe von belongsTo===(Alle Beim ersten Beispielder "Viele-Codes vom obigen zu-Eins"[[Groovy als Webhat man sich vielleicht gefragt, warum diese Assoziation eine Viele-Backend#Bidirektionales%20Manyzu-ToEins und keine Eins-One|Bidirektionale Manyzu-To-One]]" verhalten sich auch hier gleich)   Letztendlich Eins ist. Der Grund ist , dass es eine gute Ideemöglich ist, das man mehrere Instanzen von <code>Face</code> dieselbe Instanz von <code>Nose</code> zuordnen kann. Wenn man diese Zuordnung als echte Eins-zu-Eins-Zuordnung definieren möchten, ist eine <code>unique</code>-Einschränkung auf erforderlich:{| class="wikitable"|+Beispiel einer Seite der Unidirektionalen Eins-zu-EinsBeziehung von <code>Face</code> zu <code>Nose</code>.!Domänenklasse!IntelliJ's Datenbank-Schema-Beziehung hinzuzufügenAnsicht dieser Domänenklasse(dataSource:SQL)!IntelliJ's Domänenklassen-Abhängigkeiten-Ansicht|-|<syntaxhighlight lang="groovy" line="1">
class Face {
static hasOne = [nose:Nose]}
class Nose {
static belongsTo = [face: Face]
static constraints = {
nose face unique: true
}
}
</syntaxhighlight>
|[[Datei:Grails-GORM-unidirectional one to one-db schema view.png|alternativtext=|zentriert]]
|[[Datei:Grails-GORM-unidirectional one to one-association view.png|alternativtext=|zentriert|215x215px]]
|}
 ===Bidirektionales Eins-zu-Eins mithilfe von hasOne===<blockquote><code>[http://gormdocs.grails.org/63.13.x9/hibernateref/manualDomain%20Classes/#_controlling_the_ends_of_the_association Kontrollieren der Enden von AssoziationenhasOne.html hasOne]===Gelegentlich kann es vorkommen, dass man sich mit Domänen</code> definiert eine bidirektionale Eins-klassen konfrontiert sieht, die '''mehrere Eigenschaften desselben Typs''' haben. Sie können sogar '''selbstzu-referenziell''' sein, d.h. die Assoziationseigenschaft hat denselben Typ wie die DomänenEins-klasseZuordnung zwischen zwei Klassen, in denen sich der sie sich Fremdschlüssel im Kind befindet. '''Solche Situationen können Probleme verursachen, weil GORM den Typ der Assoziation möglicherweise falsch errät.''' Betrachten Sie diese einfache Klasse</blockquote>
{| class="wikitable"
|+Beispiel einer DomänenBidirektionalen Eins-klasse die mehrere Eigenschaften des gleichen Typen trägt, die sogar noch vom selben Typen wie die Domänenzu-klasse selbst sind (selbst-referenziell)Eins Beziehung von <code>Face</code> zu <code>Nose</code>.
!Domänenklasse
!IntelliJ's Datenbank-Schema-Ansicht dieser Domänenklasse
|-
|<syntaxhighlight lang="groovy" line="1">
class Person Face { String namestatic hasOne = [nose:Nose] Person parent}
// Bildet die rechts zu sehende, selbst-referenzielle Eins-zu-Eins Assoziation static belongsTo = [ supervisor: Person ]  static constraints = class Nose { supervisor nullable: true parent nullable: true }Face face
}
</syntaxhighlight>
|[[Datei:Grails-GORMgorm-self referential and multiple fields with same typesbidirectional one to one-db schema view.png|alternativtext=|zentriert]]|[[Datei:Grails-GORMgorm-self referential and multiple fields with same typesbidirectional one to one-dependency association view.png|alternativtext=|zentriert|203x203px224x224px]]
|}
Für GORM sind Beachte: Die Verwendung dieser Eigenschaft verlagert den Fremdschlüssel auf die Kindes-Tabelle, so dass in diesem Fall die Eigenschaften Fremdschlüsselspalte in der <code>parentnose</code> und -Tabelle innerhalb einer Spalte namens <code>supervisorface_id</code> zwei Richtungen derselben Zuordnunggespeichert wird. Wenn man also die <code>parent</code> Eigenschaft für eine Personen  (Alle Beispiel-Instanz festlegt, legt GORM automatisch die <code>supervisor</code> Codes vom obigen "Bidirektionale Many-Eigenschaft für die andere PersonenTo-Instanz fest. Dies mag ggf. das sein was man will, aber wenn man One" verhalten sich die Klasse ansieht haben wir tatsächlich zwei unidirektionale Beziehungen.auch hier gleich)
Um GORM zur richtigen Zuordnung zu führen kann man über die Eigenschaft Letztendlich ist es eine gute Idee, eine <code>mappedByunique</code> feststellen, dass eine bestimmte Zuordnung unidirektional ist-Einschränkung auf einer Seite der Eins-zu-Eins-Beziehung hinzuzufügen:<syntaxhighlight lang="groovy">class Face { static hasOne = [nose:Nose]  static constraints = { nose unique:true }}<blockquote/syntaxhighlight>Mit der statischen Eigenschaft   ===[http://docsgorm.grails.org/36.31.9x/refhibernate/Domain%20Classesmanual/mappedBy#_controlling_the_ends_of_the_association Kontrollieren der Enden von Assoziationen]===Gelegentlich kann es vorkommen, dass man sich mit Domänen-klassen konfrontiert sieht, die '''mehrere Eigenschaften desselben Typs''' haben.html <code>mappedBy</code>] Sie können Sie steuernsogar '''selbst-referenziell''' sein, ob eine Zuordnung als unidirektional oder bidirektional zugeordnet wird und welche Eigenschaften bei bidirektionalen Zuordnungen d.h. die Assoziationseigenschaft hat denselben Typ wie die umgekehrte Richtung bildenDomänen-klasse, in der sie sich befindet. '''Solche Situationen können Probleme verursachen, weil GORM den Typ der Assoziation möglicherweise falsch errät.</blockquote>''' Betrachten Sie diese einfache Klasse{| class="wikitable"|+Beispiel einer Domänen-klasse die mehrere Eigenschaften des gleichen Typen trägt, die sogar noch vom selben Typen wie die Domänen-klasse selbst sind (selbst-referenziell)
!Domänenklasse
!IntelliJ's Datenbank-Schema-Ansicht dieser Domänenklasse
Person parent
// Bildet die rechts zu sehende, selbst-referenzielle Eins-zu-Eins Assoziation
static belongsTo = [ supervisor: Person ]
// Die Eigenschaft "none" wird als umgekehrte Richtung der Zuordnung (oder als "Rückverweis") behandelt.
static mappedBy = [ supervisor: "none", parent: "none" ]
static constraints = {
}
</syntaxhighlight>
|Bemerke: Die Struktur bleibt gleich![[Datei:Grails-GORM-self referential and multiple fields with same types-db schema view.png|alternativtext=|zentriert]]|Bemerke: Die Struktur bleibt gleich![[Datei:Grails-GORM-self referential and multiple fields with same types-association view.png|alternativtext=|zentriert|203x203px]]
|}
Für GORM sind die Eigenschaften <code>parent</code> und <code>supervisor</code> zwei Richtungen derselben Zuordnung. Wenn man also die <code>parent</code> Eigenschaft für eine Personen-Instanz festlegt, legt GORM automatisch die <code>supervisor</code> -Eigenschaft für die andere Personen-Instanz fest. Dies mag ggf. das sein was man will, aber wenn man sich die Klasse ansieht haben wir tatsächlich zwei unidirektionale Beziehungen.
 
Um GORM zur richtigen Zuordnung zu führen kann man über die Eigenschaft <code>mappedBy</code> feststellen, dass eine bestimmte Zuordnung unidirektional ist:<blockquote>Mit der statischen Eigenschaft [http://docs.grails.org/3.3.9/ref/Domain%20Classes/mappedBy.html <code>mappedBy</code>] können Sie steuern, ob eine Zuordnung als unidirektional oder bidirektional zugeordnet wird und welche Eigenschaften bei bidirektionalen Zuordnungen die umgekehrte Richtung bilden.</blockquote>
{| class="wikitable"
!Domänenklasse
!IntelliJ's Datenbank-Schema-Ansicht dieser Domänenklasse
(dataSource: SQL)
!IntelliJ's Domänenklassen-Abhängigkeiten-Ansicht
|-
|<syntaxhighlight lang="groovy" line="1">
class Person {
String name
Person parent
 
static belongsTo = [ supervisor: Person ]
// Die Eigenschaft "none" wird als umgekehrte Richtung der Zuordnung (oder als "Rückverweis") behandelt.
static mappedBy = [ supervisor: "none", parent: "none" ]
 
static constraints = {
supervisor nullable: true
parent nullable: true
}
}
</syntaxhighlight>
|Bemerke: Die Struktur bleibt gleich![[Datei:Grails-GORM-self referential and multiple fields with same types-db schema view.png|alternativtext=|zentriert]]
|Bemerke: Die Struktur bleibt gleich![[Datei:Grails-GORM-self referential and multiple fields with same types-association view.png|alternativtext=|zentriert|203x203px]]
|}
 
Man kann "none" auch durch einen beliebigen Eigenschaftsnamen der Zielklasse ersetzen. Und dies funktioniert natürlich auch für normale Domänenklassen, nicht nur für selbst-referenzielle. Die Eigenschaft <code>mappedBy</code> ist auch nicht auf viele-zu-eins- und eins-zu-eins-Zuordnungen beschränkt - Sie funktioniert auch für Eins-zu-viele- und viele-zu-viele-Zuordnungen
Man kann "none" auch durch einen beliebigen Eigenschaftsnamen der Zielklasse ersetzen. Und dies funktioniert natürlich auch für normale Domänenklassen, nicht nur für selbst-referenzielle. Die Eigenschaft <code>mappedBy</code> ist auch nicht auf viele-zu-eins- und eins-zu-eins-Zuordnungen beschränkt - Sie funktioniert auch für Eins-zu-viele- und viele-zu-viele-Zuordnungen
 
===Kollektionen einer bidirektionalen Eins-zu-Viele-Klasse leeren/ersetzen===
|}
  Mit dieser Struktur kann man wie folgt einen Beispiel-Datensatz aus einem Buch mit 2 Reviews anlegen:<syntaxhighlight lang="groovy" line="1">
new Book(name: 'Daemon')
.addToReviews(new Review(quote: 'Daemon does for surfing the Web what Jaws did for swimming in the ocean.', author: 'Chicago Sun-Times'))
.addToReviews(new Review(quote: 'Daemon is wet-yourself scary, tech-savvy, mind-blowing!', author: 'Paste Magazine'))
.save()
</syntaxhighlight>Einfach, simpel und logisch. Was jetzt aber, wenn man alle <code>reviews</code> löschen (und von der Assoziationen entfernen) oder durch eine neue Liste ersetzen möchte? Mit der jetzigen ORM-Definition würde man z.B. die folgenden Hilfsfunktionen basteln:<syntaxhighlight lang="groovy" line="1">
Book replaceReviews(Serializable idParam, List<Review> newReviews) {
Book book = Book.where { id == idParam }.join('reviews').get()
|[[Datei:Grails-GORM-bidirectional one to many-association view.png|alternativtext=|zentriert|202x202px]]
|}
 
 
Das Kaskadenverhalten <code>all-delete-orphan</code> sorgt dafür, dass jede verwaiste <code>Review</code> gelöscht wird. Das Aufrufen von <code>.clear()</code> reicht daher aus, um die vorherigen <code>Review</code>s des <code>Book</code>s zu entfernen.<syntaxhighlight lang="groovy" line="1">
println book.title
}
</syntaxhighlight>Die von GORM verwendete Standardabrufstrategie ist <code>lazy</code>. Dies bedeutet, dass die Sammlung beim ersten Zugriff träge initialisiert wird. Dies kann zu N+1 führen, wenn Sie nicht vorsichtig sind. Das Standard-Kaskadenverhalten einer solchen Assoziation besteht darin, "Speichern" und "Aktualisieren" zu kaskadieren, jedoch nicht "Löschen". Es sei denn, es wird auch eine Zugehörigkeit /Abhängigkeit (<code>belongsTo</code>) definiert. <br />
====Mehrere Eigenschaften des gleichen Types auf der Vielen-Seite====
Wenn man zwei Eigenschaften desselben Typs auf der "Vielen"-Seite eines Eins-zu-Viele hat, muss man "mappedBy" verwenden, um anzugeben, welche Sammlung zugeordnet werden soll:<br /><br />
{| class="wikitable"
|+Beispiel einer Unidirektionalen Eins-zu-Viele Beziehung von <code>Airport</code> zu <code>Flight</code>, bei dem
|[[Datei:Grails-GORM-unidirectional one to many-multiple of same type-association view.png|alternativtext=|zentriert|206x206px]]
|}
 
Dies gilt auch, wenn man mehrere Sammlungen hat, die auf der "vielen" Seite unterschiedlichen Eigenschaften zugeordnet sind:
 
<br />
{| class="wikitable"
|+Beispiel einer Unidirektionalen Eins-zu-Viele Beziehung von <code>Airport</code> zu <code>Flight</code>, bei dem
|}
 === (Bidirektionales) Viele-zu-Viele ===
GORM unterstützt viele-zu-viele-Beziehungen, indem man auf beiden Seiten der Beziehung ein <code>hasMany</code> definiert und auf der besitzenden Seite der Beziehung ein "Gehört zu" definiert:
|}
 === Grundlegende Kollektions-Typen + Einstellen der Join-Table ===
Neben den Zuordnungen zwischen verschiedenen Domänenklassen unterstützt GORM auch die Zuordnung grundlegenden/primitiven Sammlungstypen. Die folgende Klasse erstellt beispielsweise eine Zuordnung von Spitznamen, bei der es sich um eine Reihe von String-Instanzen handelt:
<br />
{| class="wikitable"
|+Beispiel einer Unidirektionalen Eins-zu-Viele Beziehung von <code>Person</code> zu <code>String</code>
class Person {
static hasMany = [nicknames: String]
// NUR ZU SCHAUZWECKEN ::: BITTE NICHT VERWENDEN!
String[] strings
}
|[[Datei:Grails-GORM-unidirectional one to many-with primitive-example code-resulting table view.png|alternativtext=|zentriert]]
|}
 
Mit der Eigenschaft <code>joinTable</code> kann man die verschiedenen Aspekte der Join-Tabelle ändern:<blockquote>[http://docs.grails.org/3.3.9/ref/Database%20Mapping/joinTable.html joinTable] passt die Verknüpfungstabelle an, die für unidirektionale Eins-zu-Viele-, Viele-zu-Viele- und primitive Auflistungstypen verwendet wird.</blockquote>
</syntaxhighlight>
|[[Datei:Grails-GORM-unidirectional one to many-with primitive-joinTable example-db schema view.png|alternativtext=|zentriert]]
|}<br />==[http://gorm.grails.org/6.1.x/hibernate/manual/#gormComposition Domain-Modelling in GORM: Komposition]==Neben Assoziationen unterstützt GORM das Konzept der Komposition/Zusammensetzung. In diesem Fall kann anstelle der Abbildung von Klassen auf separate Tabellen eine Klasse in die aktuelle Tabelle "eingebettet" werden. Zum Beispiel:{| class="wikitable"|+!Domänenklasse!IntelliJ's Datenbank-Schema-Ansicht dieser Domänenklasse(dataSource: SQL)!IntelliJ's Domänenklassen-Abhängigkeiten-Ansicht|-|<syntaxhighlight lang="groovy" line="1">// grails-app/domain/Person.groovyclass Person { Address homeAddress Address workAddress static embedded = ['homeAddress', 'workAddress']} // grails-app/domain/Address.groovyclass Address { String number String code}</syntaxhighlight>|[[Datei:Grails-GORM-composition-db schema view.png|alternativtext=|zentriert]]|[[Datei:Grails-GORM-composition-association view.png|alternativtext=|zentriert|350x350px]]|}TIPP: Wenn man die Klasse <code>Address</code> in einer separaten Groovy-Datei im Verzeichnis <code>grails-app/domain</code> definiert, erhält man auch eine Tabelle <code>address</code> (wie im oberen Beispiel). Wenn dies nicht geschehen soll, kann man mithilfe der Funktion von Groovy mehrere Klassen pro Datei definieren und die Klasse <code>Address</code> unterhalb der Klasse <code>Person</code> in die Datei <code>grails-app/domain/Person.groovy</code> aufnehmen.{| class="wikitable"!Domänenklasse!IntelliJ's Datenbank-Schema-Ansicht dieser Domänenklasse(dataSource: SQL)!IntelliJ's Domänenklassen-Abhängigkeiten-Ansicht|-|<syntaxhighlight lang="groovy" line="1">// grails-app/domain/Person.groovyclass Person { Address homeAddress Address workAddress static embedded = ['homeAddress', 'workAddress'] class Address { String number String code }}</syntaxhighlight>|[[Datei:Grails-GORM-composition without extra table-db schema view.png|alternativtext=|zentriert]]|Bei der Assoziations-Ansicht hat sich natürlich nichts geändert.. Das Groovy-Objekt <code>Address</code>, das ja in diesem Szenario nur zum halten/gruppieren von Daten zuständig ist (DRY), wird hierdurch einfach nicht als eigene Tabelle abgebildet![[Datei:Grails-GORM-composition-association view.png|alternativtext=|zentriert|350x350px]]|}<br />==[http://gorm.grails.org/6.1.x/hibernate/manual/#inheritanceInGORM Domain-Modelling in GORM: Vererbung]==[[Datei:Single Table Inheritance.svg|mini|Tabelle pro VererbungshierarchieQuelle: [https://commons.wikimedia.org/wiki/File:Single_Table_Inheritance.svg?uselang=de Wikimedia]]][[Datei:Class Table Inheritance.svg|mini|Tabelle pro UnterklasseQuelle: [https://commons.wikimedia.org/wiki/File:Class_Table_Inheritance.svg?uselang=de Wikimedia]]][[Datei:Concrete Table Inheritance.svg|mini|Tabelle pro konkrete KlasseQuelle: [https://commons.wikimedia.org/wiki/File:Concrete_Table_Inheritance.svg?uselang=de Wikimedia]]]GORM unterstützt die Vererbung sowohl von abstrakten Basisklassen als auch von konkreten persistenten GORM-Entitäten.  <br />===Abbildungsverfahren von Vererbungshierarchien===Quelle der Beschreibungen: [https://de.wikipedia.org/wiki/Objektrelationale_Abbildung#Abbildung_von_Vererbungshierarchien Wikipedia]
Es gibt im Wesentlichen drei verschiedene Verfahren, um Vererbungshierarchien auf Datenbanktabellen abzubilden.
;Tabelle pro Vererbungshierarchie
:(auch Single Table, ''einzelne Tabelle'') Bei diesem Verfahren werden alle Attribute der Basisklasse und aller davon abgeleiteten Klassen in '''einer gemeinsamen Tabelle''' gespeichert. Zusätzlich wird ein sogenannter „Diskriminator“ in einer weiteren Spalte abgelegt, der festlegt, welcher Klasse das in dieser Zeile gespeicherte Objekt angehört. '''Attribute von abgeleiteten Klassen dürfen bei diesem Ansatz aber in den meisten Fällen nicht mit einem NOT-NULL-[[Constraint]] versehen werden.''' '''Außerdem können Beschränkungen der Anzahl erlaubter Spalten pro Tabelle diesen Ansatz bei großen Klassen bzw. Klassenhierarchien vereiteln.'''
;Tabelle pro Unterklasse
:(auch ''Joined'' oder Class Table) Bei diesem Verfahren wird '''eine Tabelle für die Basisklasse angelegt und für jede davon abgeleitete Unterklasse eine weitere Tabelle.''' Ein Diskriminator wird nicht benötigt, weil die Klasse eines Objekts durch eine 1-zu-1-Beziehung zwischen dem Eintrag in der Tabelle der Basisklasse und einem Eintrag in einer der Tabellen der abgeleiteten Klassen festgelegt ist.
;Tabelle pro konkrete Klasse
:(auch ''Table per Class'' oder Concrete Table) Hier werden die Attribute der abstrakten Basisklasse in die Tabellen für die konkreten Unterklassen mit aufgenommen. '''Die Tabelle für die Basisklasse entfällt.''' Der Nachteil dieses Ansatzes besteht darin, dass es nicht möglich ist, mit einer Abfrage Instanzen verschiedener Klassen zu ermitteln.
 
 
Die gewünschte Vererbungshierarchie kann mithilfe von ORM-DSL-Mapping wie folgt festgelegt werden:<syntaxhighlight lang="groovy">
class Payment {
int amount
static mapping = {
tablePerHierarchy false
// ODER
tablePerConcreteClass true
}
}
 
class CreditCardPayment extends Payment {
String cardNumber
}
</syntaxhighlight>
===[http://gorm.grails.org/6.1.x/hibernate/manual/#_polymorphic_queries Polymorph-ische Abfragen]===
<syntaxhighlight lang="groovy">
class Content {
String author
}
class BlogEntry extends Content {
URL url
}
class Book extends Content {
String ISBN
}
class PodCast extends Content {
byte[] audioStream
}
</syntaxhighlight>
 
Das Ergebnis der Vererbung ist, dass man die Möglichkeit hat, polymorph abzufragen. Wenn man beispielsweise die Methode <code>list()</code> für die Superklasse <code>Content</code> verwendet, werden auch alle Unterklassen von <code>Content</code> zurückgegeben:<syntaxhighlight lang="groovy">
def content = Content.list() // list all blog entries, books and podcasts
content = Content.findAllByAuthor('Joe Bloggs') // find all by author
 
def podCasts = PodCast.list() // list only podcasts
</syntaxhighlight>
 
 
==[http://gorm.grails.org/6.1.x/hibernate/manual/#sets Domain-Modelling in GORM: <code>Set</code>s, <code>List</code>s und <code>Map</code>s]==
Wenn man eine Viele-Beziehung in GORM definiert, handelt es sich standardmäßig um eine <code>java.util.Set</code>.
 
===SortedSet===
<code>Set</code>s garantieren Einzigartigkeit, aber keine Reihenfolge, was möglicherweise nicht das ist, was man will. Um eine benutzerdefinierte Ordnung zu erhalten, konfigurieren Sie das Set als <code>SortedSet</code>:
{| class="wikitable"
!Domänenklasse
!IntelliJ's Datenbank-Schema-Ansicht dieser Domänenklasse
(dataSource: SQL)
|-
|<syntaxhighlight lang="groovy">
class Author {
SortedSet books
static hasMany = [books: Book]
}
</syntaxhighlight>
|[[Datei:Grails-GORM-sortedset-db schema view.png|alternativtext=|zentriert]]
|}
In diesem Fall wird eine Implementierung von <code>java.util.SortedSet</code> für die injezierte Variable verwendet. Dies bedeutet, dass man <code>java.lang.Comparable</code> in seiner Book-Klasse implementieren müsste:<syntaxhighlight lang="groovy">
class Book implements Comparable {
String title
Date releaseDate = new Date()
 
@Override
int compareTo(obj) {
releaseDate.compareTo(obj.releaseDate)
}
}
</syntaxhighlight><br />
===List===
Um Objekte in der Reihenfolge zu halten, in der sie hinzugefügt wurden, und um sie wie ein Array nach Index zu referenzieren, kann man den Sammlungstyp als <code>List</code> definieren:
{| class="wikitable"
!Domänenklasse
!IntelliJ's Datenbank-Schema-Ansicht dieser Domänenklasse
(dataSource: SQL)
|-
|<syntaxhighlight lang="groovy">
class Author {
List books
static hasMany = [books: Book]
}
</syntaxhighlight>
|[[Datei:Grails-GORM-list-db schema view.png|alternativtext=|zentriert]]
|}
Auf Datenbankebene funktioniert dies so, dass Hibernate eine Spalte <code>books_idx</code> erstellt, in der der Index der Elemente in der Sammlung gespeichert wird, um diese Reihenfolge auf Datenbankebene beizubehalten. (Siehe Bild)
 
Bei Verwendung einer Liste müssen Elemente zur Sammlung hinzugefügt werden, bevor sie gespeichert werden. Andernfalls löst Hibernate eine Ausnahme aus (<code>org.hibernate.HibernateException: Nullindexspalte für die Sammlung</code>):<syntaxhighlight>
// Wirft den genannten Fehler!
def book = new Book(title: 'The Shining')
book.save()
author.addToBooks(book)
 
// Richtiger Weg
def book = new Book(title: 'Misery')
author.addToBooks(book)
author.save()
</syntaxhighlight><br />
===Hibernate Bags (Collection)===
Wenn Ordnung und Eindeutigkeit keine Rolle spielen (oder wenn man diese explizit verwaltet), kann man den Typ <code>[http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/collections.html Bag]</code> von Hibernate verwenden, um zugeordnete Sammlungen darzustellen.
 
Die einzige dafür erforderliche Änderung besteht darin, den Sammlungstyp als <code>Collection</code> zu definieren<syntaxhighlight lang="groovy">
class Author {
Collection books
static hasMany = [books: Book]
}
</syntaxhighlight>Da Eindeutigkeit und Reihenfolge nicht von Hibernate verwaltet werden, <u>löst das Hinzufügen oder Entfernen von Sammlungen</u>, die als Bag zugeordnet sind, <u>nicht das Laden aller vorhandenen Instanzen aus der Datenbank aus</u>. Daher ist dieser Ansatz leistungsfähiger und erfordert weniger Speicher als die Verwendung eines Sets oder eine Liste.
<br />
===Maps von Objekten===
Wenn man eine einfache Zuordnung von Zeichenfolge / Wert-Paaren wünscht, kann GORM dies wie folgt zuordnen:
{| class="wikitable"
!Domänenklasse
!IntelliJ's Datenbank-Schema-Ansicht dieser Domänenklasse
(dataSource: SQL)
!Resultat des Beispiel-Codes
|-
|<syntaxhighlight lang="groovy">
class Author {
Map books // map of ISBN:book names
}
 
def a = new Author()
a.books = ['1590597583':"My Book"]
a.save()
</syntaxhighlight>
|[[Datei:Grails-GORM-map of strings-db schema view.png|alternativtext=|zentriert]]
|[[Datei:Grails-GORM-map of strings-example code-resulting table view.png|alternativtext=|zentriert]]
|}
In oberen Fall '''müssen''' der Schlüssel und der Wert der Map ein String sein.
 
Wenn man eine Map mit Objekten möchte, kann man dies wie folgt tun:
{| class="wikitable"
!Domänenklasse
!IntelliJ's Datenbank-Schema-Ansicht dieser Domänenklasse
(dataSource: SQL)
!Resultat des Beispiel-Codes
|-
|<syntaxhighlight lang="groovy">
class Book {
Map authors
static hasMany = [authors: Author]
}
 
def a = new Author(name:"Stephen King")
 
def book = new Book()
book.authors = [stephen:a]
book.save()
</syntaxhighlight>
|[[Datei:Grails-GORM-map of objects-db schema view.png|alternativtext=|zentriert]]
|[[Datei:Grails-GORM-map of objects-example code-resulting table view.png|alternativtext=|zentriert]]
|}
Die statische <code>hasMany</code>-Eigenschaft definiert den Typ der Elemente in der Map. Die Schlüssel für die Map '''müssen''' Zeichenfolgen sein.
 
<br />
===Hinweis zu Sammlungen und dessen Leistung===
Der Java <code>Set</code>-Typ erlaubt keine Duplikate. Um die Eindeutigkeit beim Hinzufügen eines Eintrags zu einer festgelegten Zuordnung sicherzustellen, '''muss Hibernate die gesamten Zuordnungen aus der Datenbank laden.''' Wenn man eine große Anzahl von Einträgen in der Zuordnung hat, kann dies in Bezug auf die Leistung kostspielig sein.
 
'''Das gleiche Verhalten ist für den Typ <code>List</code> erforderlich''', da Hibernate die gesamte Zuordnung laden muss, um die Reihenfolge aufrechtzuerhalten. Wenn man eine große Anzahl von Datensätzen in der Zuordnung erwartet, sollte man die Zuordnung daher bidirektional machen, damit die Verknüpfung auf der umgekehrten Seite erstellt werden kann.<syntaxhighlight lang="groovy">
def book = new Book(title:"New Grails Book")
def author = Author.get(1)
book.author = author
book.save()
</syntaxhighlight>Im oberen Beispiel wird der Zuordnungs-Link vom untergeordneten Element (Buch) erstellt. '''Daher ist es nicht erforderlich,''' '''die Sammlung''' direkt '''zu bearbeiten''', '''was zu weniger Abfragen und effizienterem Code führt'''. Wenn man bei einem Autor mit einer großen Anzahl zugeordneter Buchinstanzen Code wie den folgenden schreiben, '''wirkt sich dies auf die Leistung aus:'''<syntaxhighlight lang="groovy">
def book = new Book(title:"New Grails Book")
def author = Author.get(1)
author.addToBooks(book)
author.save()
</syntaxhighlight>
 
 
==[http://gorm.grails.org/6.1.x/hibernate/manual/#persistenceBasics Persistenz Grundlagen]==
<blockquote>
'''Persistenz''' ist in der Informatik der Begriff, der die Fähigkeit bezeichnet, Daten (oder Objekte) oder ''logische Verbindungen'' über lange Zeit (insbesondere über einen Programmabbruch hinaus) bereitzuhalten.
 
Da ein Programm jederzeit unvorhergesehen unterbrochen werden kann, bedeutet persistente Datenhaltung insbesondere, dass jede Zustandsänderung der Daten sofort auf dem nichtflüchtigen Medium gespeichert werden muss.
 
„Persistent“ wird als ein im Kontext wohldefinierter Fachbegriff für „nicht unkontrolliert veränderlich“ verwendet. </blockquote>Eine wichtige Sache, an die man sich bei GORM erinnern sollte, ist, dass GORM unter der Oberfläche [http://www.hibernate.org/ Hibernate] für die Persistenz verwendet. Wenn man mit [http://api.rubyonrails.org/classes/ActiveRecord/Base.html ActiveRecord] oder [http://www.mybatis.org/ iBatis/MyBatis] gearbeitet hat, fühlt sich das "Sitzungs"-Modell von Hibernate möglicherweise etwas seltsam an.<br />
 
Wenn man Grails verwendet, bindet Grails automatisch eine Hibernate-Sitzung an die aktuell ausgeführte Anforderung. Auf diese Weise kann man die Methoden <code>save</code> und <code>delete</code> sowie andere GORM-Methoden transparent verwenden.
 
Wenn man Grails nicht verwendet, muss man sicherstellen, dass eine Sitzung an die aktuelle Anforderung gebunden ist. Eine Möglichkeit, dies zu erreichen, ist die <code>[http://gorm.grails.org/6.1.x/hibernate/api/org/grails/datastore/gorm/GormEntity.html#withNewSession(groovy.lang.Closure) withNewSession(Closure)]</code>-Methode:<syntaxhighlight lang="groovy">
Book.withNewSession {
// your logic here
}
</syntaxhighlight>Eine weitere Option besteht darin, eine Transaktion mit der <code>[http://gorm.grails.org/6.1.x/hibernate/api/org/grails/datastore/gorm/GormEntity.html#withTransaction(groovy.lang.Closure) withTransaction(Closure)]</code>-Methode zu binden:<syntaxhighlight lang="groovy">
Book.withTransaction {
// your logic here
}
</syntaxhighlight>
 
 
===Transaktions-Write-Behind===
Eine nützliche Funktion von Hibernate über direkte JDBC-Aufrufe und sogar andere Frameworks ist, dass beim Aufrufen von <code>save()</code> oder <code>delete()</code> zu diesem Zeitpunkt nicht unbedingt SQL-Vorgänge ausgeführt werden. '''Hibernate stapelt SQL-Anweisungen und führt sie so spät wie möglich aus, häufig am Ende der Anforderung, wenn die Sitzung geleert (flush) und geschlossen wird.'''''Wenn man Grails verwendet, wird dies normalerweise automatisch für einen erledigt. Wenn man GORM außerhalb von Grails verwendet, muss man die Sitzung möglicherweise am Ende des Vorgangs manuell leeren.''
 
 
'''Hibernate speichert Datenbankaktualisierungen nach Möglichkeit zwischen, wobei die Änderungen nur dann tatsächlich übertragen werden, wenn bekannt ist, dass ein Flush erforderlich ist, oder wenn ein Flush programmgesteuert ausgelöst wird.''' Ein häufiger Fall, in dem Hibernate zwischengespeicherte Aktualisierungen löscht, ist das Ausführen von Abfragen, da die zwischengespeicherten Informationen möglicherweise in den Abfrageergebnissen enthalten sind. Solange man jedoch konfliktfreie Speicherungen, Aktualisierungen und Löschvorgänge durchführt, werden diese gestapelt, bis die Sitzung gelöscht wird. Dies kann eine erhebliche Leistungssteigerung für Anwendungen sein, die viele Datenbankschreibvorgänge ausführen.
 
 
'''Zu beachten gilt, dass das Flushing nicht mit dem Commiten einer Transaktion identisch ist.''' Wenn die Aktionen im Kontext einer Transaktion ausgeführt werden, führt das Flushing SQL-Aktualisierungen aus, aber die Datenbank speichert die Änderungen in ihrer Transaktionswarteschlange und schließt die Aktualisierungen erst ab, wenn die Transaktion festgeschrieben wird.
 
===Speichern und Aktualisieren von Objekten===
Ein Beispiel für die Verwendung der <code>save()</code>-Methode wäre:<syntaxhighlight lang="groovy">
def p = Person.get(1)
p.save()
</syntaxhighlight>Die eigentliche Speicherung wird nicht sofort in die Datenbank übertragen, sondern beim nächsten Flush. Es gibt jedoch Fälle, in denen man steuern möchten, wann diese Anweisungen ausgeführt werden (oder in der Hibernate-Terminologie, wenn die Sitzung "geleert" wird). Dazu kann man das Argument <code>flush</code> für die <code>save()</code>-Methode verwenden:<syntaxhighlight lang="groovy">
def p = Person.get(1)
p.save(flush: true)
</syntaxhighlight>Zu Beachten gilt, dass in diesem Fall '''alle''' ausstehenden SQL-Anweisungen einschließlich vorheriger Speicherungen, Löschungen usw. mit der Datenbank synchronisiert werden. Auf diese Weise kann man auch Ausnahmen abfangen, was in der Regel in Szenarien mit [http://gorm.grails.org/6.1.x/hibernate/manual/#locking optimistischem Sperren] hilfreich ist:<syntaxhighlight lang="groovy">
def p = Person.get(1)
try {
p.save(flush: true)
}
catch (org.springframework.dao.DataIntegrityViolationException e) {
// deal with exception
}
</syntaxhighlight>Außerdem gilt zu beachten dass GORM eine Domain-Instanz jedes Mal überprüft, wenn man sie speichern will. Wenn diese Überprüfung fehlschlägt, wird die Domäneninstanz nicht in der Datenbank gespeichert. '''Standardmäßig gibt <code>save()</code> in diesem Fall einfach null zurück. Wenn Sie jedoch eine Ausnahme auslösen möchten, können Sie das Argument <code>failOnError</code> verwenden:'''<syntaxhighlight lang="groovy">
def p = Person.get(1)
try {
p.save(failOnError: true)
}
catch (ValidationException e) {
// deal with exception
}
</syntaxhighlight>
 
 
===Löschen von Objekten===
Ein Beispiel für die Verwendung der <code>delete()</code>-Methode wäre:<syntaxhighlight lang="groovy">
def p = Person.get(1)
p.delete()
</syntaxhighlight>Wie beim Speichern verwendet Hibernate das Transaktions-Write-Behind-Konzept, um das Löschen durchzuführen. Um das Löschen an Ort und Stelle durchzuführen, kann man das Argument <code>flush</code> verwenden:<syntaxhighlight lang="groovy">
def p = Person.get(1)
p.delete(flush: true)
</syntaxhighlight>Mit dem <code>flush</code>-Argument kann man alle Fehler abfangen, die beim Löschen auftreten. Ein häufiger Fehler, der auftreten kann, ist, wenn Sie eine Datenbankeinschränkung verletzt, obwohl dies normalerweise auf einen Programmier- oder Schemafehler zurückzuführen ist. Das folgende Beispiel zeigt, wie eine <code>DataIntegrityViolationException</code> abgefangen wird, die ausgelöst wird, wenn man die Datenbankeinschränkungen verletzt:<syntaxhighlight lang="groovy">
import org.springframework.dao.*
 
def p = Person.get(1)
 
try {
p.delete(flush: true)
}
catch (DataIntegrityViolationException e) {
// handle the error
}
</syntaxhighlight>Um einen Stapel-Löschvorgang durchzuführen, gibt es verschiedene Möglichkeiten, dies zu erreichen. Eine Möglichkeit besteht darin, eine [http://gorm.grails.org/6.1.x/hibernate/manual/#whereQueries Where-Abfrage] zu verwenden:<syntaxhighlight lang="groovy">
Person.where {
name == "Fred"
}.deleteAll()
</syntaxhighlight><br />
 
===Eager und Lazy Fetching===
Zuordnungen in GORM sind standardmäßig faul. Dies lässt sich am besten anhand eines Beispiels erklären:<syntaxhighlight lang="groovy">
class Airport {
String name
static hasMany = [flights: Flight]
}
 
class Flight {
String number
Location destination
static belongsTo = [airport: Airport]
}
 
class Location {
String city
String country
}
</syntaxhighlight>Angesichts der oben genannten Domänenklassen und des folgenden Codes:<syntaxhighlight lang="groovy">
def airport = Airport.findByName("Gatwick")
for (flight in airport.flights) {
println flight.destination.city
}
</syntaxhighlight>'''GORM führt einzelne SQL-Abfrage aus, um...'''
 
*die "Flughafen"-Instanz abzurufen,
*eine andere, um ihre Flüge abzurufen,
*und dann eine zusätzliche Abfrage für jede Iteration über die Flugzuordnung, um das aktuelle Flugziel abzurufen.
 
Mit anderen Worten: '''Man führt N+1-Anfragen aus!''' (wenn Sie die ursprüngliche ausschließen, um den Flughafen zu erhalten).
<br />
 
<br />
 
===Eager Fetching konfigurieren===
<syntaxhighlight lang="groovy">
class Airport {
String name
static hasMany = [flights: Flight]
static mapping = {
flights lazy: false
}
}
</syntaxhighlight>Für weitere Informationen zu Eager Fetching (dessen Nachteile und Prinzipien) sowie zum Batch-Fetching Siehe die [http://gorm.grails.org/6.1.x/hibernate/manual/#_configuring_eager_fetching Offizielle Dokumentation]
 
 
===Checken auf Modifikation===
Sobald man eine persistente Domänenklasseninstanz geladen und möglicherweise geändert hat, ist es nicht einfach, die ursprünglichen Werte abzurufen. Wenn man versucht, die Instanz mit <code>get(id)</code> neu zu laden, gibt Hibernate die aktuell geänderte Instanz aus dem Sitzungscache zurück.
 
Das neuladen mit einer neuen Abfrage würde einen Flush auslösen, der Probleme verursachen kann, wenn seine Daten noch nicht zum Flush bereit sind. Daher bietet GORM einige Methoden zum Abrufen der ursprünglichen Werte, die Hibernate beim Laden der Instanz zwischenspeichert (die für die fehlerhafte Überprüfung verwendet wird)
 
*[http://gorm.grails.org/6.1.x/hibernate/api/org/grails/datastore/gorm/GormEntity.html#isDirty(java.lang.String) isDirty()]: Checkt ob überhaupt irgend-ein Feld verändert wurde (Funktioniert mit allen Persistenten Eigenschaften und Assoziationen, nur Collection-Assoziationen (Bags) funktionieren in der jetzigen Version noch nicht)
*isDirty(fieldName): Checkt ob ein bestimmtes Feld verändert wurde
*[http://gorm.grails.org/6.1.x/hibernate/api/org/grails/datastore/gorm/GormEntity.html#getDirtyPropertyNames() getDirtyPropertyNames()]: Gibt die Namen der Felder zurück die verändert wurden
*[http://gorm.grails.org/6.1.x/hibernate/api/org/grails/datastore/gorm/GormEntity.html#getPersistentValue(java.lang.String) getPersistentValue(fieldName)]: Gibt den ursprünglichen / derzeitig persistenten Wert eines Felds zurück
 
 
==[http://gorm.grails.org/6.1.x/hibernate/manual/#querying Abfragen erstellen (Querying)]==
GORM unterstützt eine Reihe leistungsstarker Abfragemöglichkeiten, von dynamischen Findern, über Kriteriensuche bis hin zu eigens definierbaren '''HQL-Anfragen (Hibernate Object Oriented Query Language)'''. Abhängig von der Komplexität der Abfrage hat man die folgenden Optionen in der Reihenfolge der Flexibilität und Leistung:
 
*Dynamische Finder
*"Wo/Bei Dem"-Abfragen
*Kriterien Abfragen
*Hibernate Query Language (HQL)
 
''Darüber hinaus führt die Fähigkeit von Groovy, Sammlungen mit [https://groovy-lang.org/processing-xml.html#_gpath GPath] und Methoden wie sort, findAll usw. in Kombination mit GORM zu bearbeiten, zu einer leistungsstarken Kombination.''
 
 
===[http://gorm.grails.org/6.1.x/hibernate/manual/#_listing_instances Instanzen auflisten]===
Die [http://gorm.grails.org/6.1.x/hibernate/api/org/grails/datastore/gorm/GormEntity.html#list() <code>list()</code>]-Methode gibt (ohne Argumente) alle Domäneninstanzen einer Klasse zurück:<syntaxhighlight lang="groovy">
def books = Book.list()
</syntaxhighlight>Die [http://gorm.grails.org/6.1.x/hibernate/api/org/grails/datastore/gorm/GormEntity.html#list(java.util.Map) <code>list(params)</code>]-Methode unterstützt Argumente zur Paginierung..<syntaxhighlight lang="groovy">
def books = Book.list(offset:10, max:20)
</syntaxhighlight>..und zum simplen sortieren. (Das argument <code>sort</code> trägt den Namen des Feldes der Domänenklasse nach der man sortieren will, und das <code>order</code>-Argument akzeptiert die Werte <code>asc</code> (ascending, aufsteigend) und <code>desc</code> (descenting, absteigend))<syntaxhighlight lang="groovy">
def books = Book.list(sort:"title", order:"asc")
</syntaxhighlight>
 
 
===[http://gorm.grails.org/6.1.x/hibernate/manual/#_retrieval_by_database_identifier Instanz über ID ausfindig machen]===
Die zweite Grundform eines Abrufs ist mithilfe die Datenbankkennung über der Methode <code>[http://gorm.grails.org/6.1.x/hibernate/api/org/grails/datastore/gorm/GormEntity.html#get(java.io.Serializable) get(id)]</code>:<syntaxhighlight lang="groovy">
def book = Book.get(23)
</syntaxhighlight>Mit der Methode [http://gorm.grails.org/6.1.x/hibernate/api/org/grails/datastore/gorm/GormEntity.html#getAll(java.io.Serializable) getAll(ids)] kann man auch gleich mehrere Instanzen nach der ID ausfindig machen.<syntaxhighlight lang="groovy">
def books = Book.getAll(23, 93, 81)
</syntaxhighlight>
 
 
===[http://gorm.grails.org/6.1.x/hibernate/manual/#finders Dynamische Finder]===
'''G'''ORM unterstützt das Konzept der dynamischen Finder. Dynamische Finder sieht aus wie ein statischer Methodenaufruf, aber die Method selbst sind auf Codeebene in keiner Form vorhanden.
 
Stattdessen wird eine Methode mithilfe von [https://docs.groovy-lang.org/docs/next/html/documentation/core-metaprogramming.html Codesynthese zur Laufzeit] auf magische Weise generiert, basierend auf den Eigenschaften einer bestimmten Klasse. Nehmen wir zum Beispiel die Klasse <code>Book</code>:<syntaxhighlight lang="groovy">
class Book {
String title
Date releaseDate
Author author
}
class Author {
String name
}
</syntaxhighlight>Die Klasse <code>Book</code> hat Eigenschaften wie <code>title</code>, <code>releaseDate</code> und <code>author</code>. Diese können mithilfe der dynamischen <code>findBy*</code> und <code>findAllBy*</code>-Methoden in form von '''Methodenausdrücken''' genutzt werden:<syntaxhighlight lang="groovy">
def book = Book.findByTitle("The Stand")
 
book = Book.findByTitleLike("Harry Pot%")
 
book = Book.findByReleaseDateBetween(firstDate, secondDate)
 
book = Book.findByReleaseDateGreaterThan(someDate)
 
book = Book.findByTitleLikeOrReleaseDateLessThan("%Something%", someDate)
</syntaxhighlight><br />
====Methodenausdrücke====
Ein Methodenausdruck in GORM besteht aus dem Präfix wie <code>findBy*</code>, gefolgt von einem Ausdruck, der eine oder mehrere Eigenschaften kombiniert. Die Grundform ist:<syntaxhighlight lang="groovy">
Book.findBy(<<Property>><<Comparator>><<Boolean Operator>>)?<<Property>><<Comparator>>
</syntaxhighlight>Die mit einem "?" Token sind optional. Jeder Komparator ändert die Art der Abfrage. Beispielsweise:<syntaxhighlight lang="groovy">
def book = Book.findByTitle("The Stand")
 
book = Book.findByTitleLike("Harry Pot%")
</syntaxhighlight>Im obigen Beispiel entspricht die erste Abfrage der (exakten) Gleichheits-Abfrage, während die letztere aufgrund des <code>Like</code>-Komparators einem SQL-<code>Like</code>-Ausdruck entspricht.
 
 
'''<u>Die verfügbaren Komperatoren (Comperators) sind:</u>'''
 
*<code>InList</code> - In der Liste der angegebenen Werte
 
*<code>LessThan</code> - weniger als der gegebener Wert
*<code>LessThanEquals</code> - kleiner oder gleich als der gegeben Wert
*<code>GreaterThan</code> - größer als der gegebene Wert
*<code>GreaterThanEquals</code> - Größer als oder gleich dem gegeben Wert
*<code>Like</code> - Entspricht einem SQL-Like-Ausdruck
*<code>Ilike</code> - Wie <code>Like</code>, außer dass Groß- und Kleinschreibung nicht berücksichtigt wird
*<code>NotEqual</code> - Negiert die Gleichheitsbedingung
*<code>InRange</code> - Zwischen den <code>from</code> und <code>to</code> Werten einer [http://grails.asia/groovy-range-examples Groovy-Range]
*<code>Rlike</code> - Führt eine Regexp-LIKE in MySQL oder Oracle aus, anderenfalls wird auf <code>Like</code> zurückgefallen
*<code>Between</code> - Zwischen zwei Werten (erfordert zwei Argumente)
*<code>IsNotNull</code> - Kein Nullwert (nimmt kein Argument an)
*<code>IsNull</code> - Ist ein Nullwert (nimmt kein Argument an)
 
Beachte: Die letzten drei Komperatoren fordern im Vergleich zu den anderen Komperatoren eine unterschiedliche Anzahl von Methodenargumenten an, wie das folgende Beispiel demonstrieren sollte:<syntaxhighlight lang="groovy">
def now = new Date()
def lastWeek = now - 7
def book = Book.findByReleaseDateBetween(lastWeek, now)
 
books = Book.findAllByReleaseDateIsNull()
books = Book.findAllByReleaseDateIsNotNull()
</syntaxhighlight><br />
====Boolesche Logik====
Methodenausdrücke können auch einen booleschen Operator verwenden, um zwei oder mehr Kriterien zu kombinieren:<syntaxhighlight lang="groovy">
def books = Book.findAllByTitleLikeAndReleaseDateGreaterThan("%Java%", new Date() - 30)
</syntaxhighlight>In diesem Fall verwenden wir <code>And</code> in der Mitte der Abfrage, um sicherzustellen, dass beide Bedingungen erfüllt sind. Man kann jedoch auch <code>Or</code> verwenden:<syntaxhighlight lang="groovy">
def books = Book.findAllByTitleLikeOrReleaseDateGreaterThan("%Java%", new Date() - 30)
</syntaxhighlight>Man kann so viele Kriterien kombinieren wie man möchten, aber alle müssen mit <code>And</code> oder <code>Or</code> kombiniert werden. Wenn man <code>And</code> und <code>Or</code> kombinieren möchte oder wenn die Anzahl der Kriterien einen sehr langen Methodennamen ergibt, wäre es gut stattdessen einfach in eine Kriterien- oder HQL-Abfrage zu verwenden (siehe später).
<br />
====Assoziationen Abfragen====
<syntaxhighlight lang="groovy">
def author = Author.findByName("Stephen King")
 
def books = author ? Book.findAllByAuthor(author) : []
</syntaxhighlight>In diesem Fall verwenden wir die <code>Author</code>-Instanz wenn sie nicht null ist um alle <code>Book</code>-Instanzen für den angegebenen <code>Author</code> abzurufen.
<br />
====Pagination und Sortierung der Ergebnisse====
Wie bei der <code>list()</code>-Methode funktionieren hier auch die genannten Argumente zur Pagination und Sortierung der Ergebnisse. Diese werden als finales Argument in einer Map angegeben.<syntaxhighlight lang="groovy">
def books = Book.findAllByTitleLike("Harry Pot%",
[max: 3, offset: 2, sort: "title", order: "desc"])
</syntaxhighlight>
 
 
===[http://gorm.grails.org/6.1.x/hibernate/manual/#whereQueries "Where" Abfragen]===
Die <code>[http://gorm.grails.org/6.1.x/hibernate/api/org/grails/datastore/gorm/GormEntity.html#where(groovy.lang.Closure) where()]</code>-Methode baut auf der Unterstützung von [http://gorm.grails.org/6.1.x/hibernate/manual/#detachedCriteria Detached Criteria] auf, indem sie eine erweiterte DSL-Abfrage zur Kompilierungszeit für allgemeine Abfragen bereitstellt. Die <code>where</code>-Methode ist flexibler als dynamische Finder, weniger ausführlich als Kriterien und bietet dennoch einen leistungsstarken Mechanismus zum Erstellen von Abfragen.
 
====Grundlegende Abfragen====
Die <code>[http://gorm.grails.org/6.1.x/hibernate/api/org/grails/datastore/gorm/GormEntity.html#where(groovy.lang.Closure) where()]</code> Methode akzeptiert eine Closure, die den regulären <code>Collection</code>-Methoden von Groovy sehr ähnlich sieht. Die Closure sollte die logischen Kriterien im regulären Groovy-Syntax definieren:<syntaxhighlight lang="groovy">
def query = Person.where {
firstName == "Bart"
}
Person bart = query.find()
</syntaxhighlight>Das zurückgegebene Objekt ist eine [http://gorm.grails.org/6.1.x/hibernate/api/grails/gorm/DetachedCriteria.html DetachedCriteria]-Instanz. Dies bedeutet, dass es keiner bestimmten Datenbankverbindung oder Sitzung zugeordnet ist. Dies bedeutet, dass man die <code>where</code>-Methode verwenden könnte, um allgemeine Abfragen auf Klassenebene zu definieren:<syntaxhighlight lang="groovy">
import grails.gorm.*
 
class Person {
static DetachedCriteria<Person> simpsons = where {
lastName == "Simpson"
}
...
}
...
Person.simpsons.each { Person p ->
println p.firstname
}
</syntaxhighlight>'''Die Ausführung von where-Abfragen ist verzögert und erfolgt nur bei Verwendung der <code>DetachedCriteria</code>-Instanz'''. '''Wenn man eine Abfrage im <code>where</code>-Stil sofort ausführen möchten, gibt es Variationen von <code>findAll</code> und <code>find</code>-Methoden, um dies zu erreichen:'''<syntaxhighlight lang="groovy">
def results = Person.findAll {
lastName == "Simpson"
}
def results = Person.findAll(sort:"firstName") {
lastName == "Simpson"
}
Person p = Person.find { firstName == "Bart" }
</syntaxhighlight>Jeder Groovy-Operator kann einer regulären Kriterien-methode zugeordnet werden. Die folgende Tabelle enthält eine Zuordnung der Groovy-Operatoren zu den Methoden:
{| class="wikitable"
!Operator
!Abgebildet als Kriterien <u>Methode</u>
!Beschreibung
|-
|'''<code>==</code>'''
|eq
|Gleich wie
|-
|'''<code>!=</code>'''
|ne
|Nicht gleich wie
|-
|'''<code>></code>'''
|gt
|Größer als
|-
|'''<code><</code>'''
|lt
|Kleiner als
|-
|'''<code>>=</code>'''
|ge
|Größer als oder gleich wie
|-
|'''<code><=</code>'''
|le
|Kleiner als oder gleich wie
|-
|'''<code>in</code>'''
|inList
|In der gegebenen Liste enthalten
|-
|'''<code>==~</code>'''
|like
|Ähnlich wie dem gegeben String
|-
|'''<code>=~</code>'''
|ilike
|<code>like</code>, aber ohne Acht auf Groß-und-Kleinschreibung
|}
Es ist möglich, reguläre Groovy-Vergleichsoperatoren und -Logik zu verwenden, um komplexe Abfragen zu formulieren:<syntaxhighlight lang="groovy">
def query = Person.where {
(lastName != "Simpson" && firstName != "Fred") || (firstName == "Bart" && age > 9)
}
def results = query.list(sort:"firstName")
</syntaxhighlight>Die Groovy-Regex-Matching-Operatoren werden <code>like</code> und <code>ilike</code>-Abfragen zugeordnet, es sei denn, der Ausdruck auf der rechten Seite ist ein <code>Pattern</code>-Objekt. In diesem Fall wird sie einer <code>rlike</code> Abfrage zugeordnet:<syntaxhighlight lang="groovy">
def query = Person.where {
firstName ==~ ~/B.+/
}
</syntaxhighlight>Der von den dynamischen Findern bekannte <code>between</code>-Ausdruck lässt sich in einer <code>where</code>-Abfrage mit dem Schlüsselwort <code>in</code> durchsetzen:<syntaxhighlight lang="groovy">
def query = Person.where {
age in 18..65
}
</syntaxhighlight>Die von den dynamischen Findern bekannte <code>isNull</code> und <code>isNotNull</code> Abfrage kann durch einen simplen Vergleich mit <code>null</code> realisiert werden<syntaxhighlight lang="groovy">
def query = Person.where {
middleName == null
}
</syntaxhighlight><br />
 
====Komposition von Abfragen====
Da der Rückgabewert der <code>where</code>-Methode eine [http://gorm.grails.org/6.1.x/hibernate/manual/#detachedCriteria DetachedCriteria]-Instanz ist, kann man aus der ursprünglichen Abfrage neue Abfragen erstellen (<u>aneinanderreihen</u>):<syntaxhighlight lang="groovy">
DetachedCriteria<Person> query = Person.where {
lastName == "Simpson"
}
DetachedCriteria<Person> bartQuery = query.where {
firstName == "Bart"
}
Person p = bartQuery.find()
</syntaxhighlight>Beachten: Eine als Variable definierte Closure kann nur dann an die <code>where</code>-Methode übergeben können, wenn er explizit in eine DetachedCriteria-Instanz umgewandelt wurde. Mit anderen Worten, Folgendes führt zu einem Fehler:<syntaxhighlight lang="groovy">
def callable = {
lastName == "Simpson"
}
def query = Person.where(callable)
</syntaxhighlight>Das Obige muss wie folgt geschrieben werden:<syntaxhighlight lang="groovy">
import grails.gorm.DetachedCriteria
 
def callable = {
lastName == "Simpson"
} as DetachedCriteria<Person>
def query = Person.where(callable)
</syntaxhighlight>Wie man sieht, wird die Closure-Definition (unter Verwendung des Schlüsselworts <code>as</code>) in eine DetachedCriteria-Instanz umgewandelt (ge-castet), die auf die Person-Klasse abzielt.
 
<br />
 
====''Konjunktion, Disjunktion und Negation''====
Wie bereits erwähnt, kann man reguläre logische Groovy-Operatoren (<code>||</code> und <code>&&</code>) zu Konjunktionen und Disjunktionen kombinieren:<syntaxhighlight lang="groovy">
def query = Person.where {
(lastName != "Simpson" && firstName != "Fred") || (firstName == "Bart" && age > 9)
}
</syntaxhighlight>Man kann logische Komparationen wie gewohnt mit einem <code>!</code> negieren:<syntaxhighlight lang="groovy">
def query = Person.where {
firstName == "Fred" && !(lastName == 'Simpson')
}
</syntaxhighlight><br />
 
====Eigenschaftenvergleichsabfragen====
Wenn man einen Eigenschaftsnamen sowohl auf der linken als auch auf der rechten Seite eines Vergleichsausdrucks verwendet, werden automatisch die entsprechenden Eigenschaftsvergleichs<u>kriterien</u> verwendet:<syntaxhighlight lang="groovy">
def query = Person.where {
firstName == lastName
}
</syntaxhighlight>In der folgenden Tabelle wird beschrieben, wie jeder Vergleichsoperator den Eigenschaftenvergleichs<u>methoden</u> der einzelnen Kriterien zugeordnet wird:
{| class="wikitable"
!Operator
!Abgebildet als Kriterien <u>Methode</u>
!Beschreibung
|-
|'''=='''
|eqProperty
|Gleich wie
|-
|'''!='''
|neProperty
|Nicht gleich wie
|-
|'''>'''
|gtProperty
|Größer als
|-
|'''<'''
|ltProperty
|Kleiner als
|-
|'''>='''
|geProperty
|Größer als oder gleich wie
|-
|'''<='''
|leProperty
|Kleiner als oder gleich wie
|}
<br />
 
====Assoziationen====
Zuordnungen können mithilfe des Punktoperators abgefragt werden, um den Eigenschaftsnamen der abzufragenden Zuordnung anzugeben:<syntaxhighlight lang="groovy">
def query = Pet.where {
owner.firstName == "Joe" || owner.firstName == "Fred"
}
</syntaxhighlight>Man kann mehrere Kriterien in einem Closure-Methodenaufruf gruppieren, wobei der Name der Methode mit dem Zuordnungsnamen übereinstimmt:<syntaxhighlight lang="groovy">
def query = Person.where {
pets { name == "Jack" || name == "Joe" }
}
</syntaxhighlight>Diese Technik kann mit anderen Kriterien der obersten Ebene kombiniert werden:<syntaxhighlight lang="groovy">
def query = Person.where {
pets { name == "Jack" } || firstName == "Ed"
}
</syntaxhighlight>Bei Assoziationen mit Sammlungen können Abfragen auf die Größe der Sammlung angewendet werden:<syntaxhighlight lang="groovy">
def query = Person.where {
pets.size() == 2
}
</syntaxhighlight>Die folgende Tabelle zeigt, welcher Operator für jeden <code>size()</code>-Vergleich auf welche Kriterien<u>methode</u> abgebildet wird:
{| class="wikitable"
!Operator
!Abgebildet als Kriterien <u>Methode</u>
!Beschreibung
|-
|'''=='''
|sizeEq
|Die Größe der Sammlung ist gleich wie
|-
|'''!='''
|sizeNe
|Die Größe der Sammlung ist nicht gleich wie
|-
|'''>'''
|sizeGt
|Die Größe der Sammlung ist größer als
|-
|'''<'''
|sizeLt
|Die Größe der Sammlung ist kleiner als
|-
|'''>='''
|sizeGe
|Die Größe der Sammlung ist größer als oder gleich wie
|-
|'''<='''
|sizeLe
|Die Größe der Sammlung ist kleiner als oder gleich wie
|}
<br />
 
====Aliase und Sortierung====
Wenn man eine Abfrage für eine Assoziation definiert wird automatisch ein Alias für die Abfrage generiert. Zum Beispiel die folgende Abfrage:<syntaxhighlight lang="groovy">
def query = Pet.where {
owner.firstName == "Fred"
}
</syntaxhighlight>Generiert einen Alias für die <code>owner</code>-Assoziation, z.B. <code>owner_alias_0</code>. Diese generierten Aliase sind in den meisten Fällen in Ordnung, aber nicht nützlich, wenn man die Ergebnisse später sortieren oder mit einer Projektion versehen möchten. Die folgende Abfrage schlägt beispielsweise fehl:<syntaxhighlight lang="groovy">
// fails because a dynamic alias is used
Pet.where {
owner.firstName == "Fred"
}.list(sort:"owner.lastName")
</syntaxhighlight>Wenn man die Ergebnisse sortieren möchte, sollte ein expliziter Alias verwendet werden, der durch einfaches Deklarieren einer Variablen in der <code>where</code>-Abfrage definiert werden kann:<syntaxhighlight lang="groovy">
def query = Pet.where {
// Define an alias called o1
def o1 = owner
// Use the alias in the query itself
o1.firstName == "Fred"
// Use the alias to sort the results
}.list(sort:'o1.lastName')
</syntaxhighlight>Durch Zuweisen des Namens einer Zuordnung zu einer lokalen Variablen wird diese automatisch zu einem Alias, der innerhalb der Abfrage selbst und auch zum Sortieren oder Projizieren der Ergebnisse verwendet werden kann.
 
<br />
 
====Unterabfragen====
Es ist möglich, Unterabfragen innerhalb von Abfragen auszuführen. Um beispielsweise alle Personen zu finden, die älter als das Durchschnittsalter sind, kann die folgende Abfrage verwendet werden:<syntaxhighlight lang="groovy">
final query = Person.where {
age > avg(age)
}
</syntaxhighlight><br />
{| class="wikitable"
!Methode
!Beschreibung
|-
|'''avg'''
|Der Durchschnitt aller Werte
|-
|'''sum'''
|Die Summe aller Werte
|-
|'''max'''
|Der maximale Wert
|-
|'''min'''
|Der minimale Wert
|-
|'''count'''
|Die Anzahl an Werten
|-
|'''property'''
|Gibt eine Eigenschaft der resultierenden Entitäten zurück
|}
Man kann zusätzliche Kriterien auf jede Unterabfrage anwenden, indem man die <code>of</code>-Methode verwendet und eine Closure übergibt, der die Kriterien enthält:<syntaxhighlight lang="groovy">
def query = Person.where {
age > avg(age).of { lastName == "Simpson" } && firstName == "Homer"
}
</syntaxhighlight>Da die <code>property</code>-Unterabfrage mehrere Ergebnisse zurückgibt, vergleicht das verwendete Kriterium alle Ergebnisse. Die folgende Abfrage findet beispielsweise alle Personen, die jünger als Personen mit dem Nachnamen "Simpson" sind:<syntaxhighlight lang="groovy">
Person.where {
age < property(age).of { lastName == "Simpson" }
}
</syntaxhighlight><br />
 
====Erweiterte Unterabfragen in GORM====
Die Unterstützung für Unterabfragen wurde erweitert. Man kann jetzt mit '''verschachtelten''' '''Unterabfragen''' rumwerken:<syntaxhighlight lang="groovy">
def results = Person.where {
firstName in where { age < 18 }.firstName
}.list()
</syntaxhighlight>Kriterien und <code>where</code> Abfragen können nahtlos '''gemischt''' werden:<syntaxhighlight lang="groovy">
def results = Person.withCriteria {
notIn "firstName", Person.where { age < 18 }.firstName
}
</syntaxhighlight>Unterabfragen können mit '''Projektionen''' verwendet werden:<syntaxhighlight lang="groovy">
def results = Person.where {
age > where { age > 18 }.avg('age')
}
</syntaxhighlight>'''Korrelierte Abfragen, die zwei Domänenklassen umfassen''', können verwendet werden:<syntaxhighlight lang="groovy">
def employees = Employee.where {
region.continent in ['APAC', "EMEA"]
}.id()
def results = Sale.where {
employee in employees && total > 100000
}.employee.list()
</syntaxhighlight>Unterstützung für Aliase (Querverweise) mithilfe einfacher Variablendeklarationen wurde hinzugefügt:<syntaxhighlight lang="groovy">
def query = Employee.where {
def em1 = Employee
exists Sale.where {
def s1 = Sale
def em2 = employee
return em2.id == em1.id
}.id()
}
def results = query.list()
</syntaxhighlight><br />
 
====Andere Funktionen====
Im Rahmen einer Abfrage stehen einem verschiedene Funktionen zur Verfügung. Diese sind in der folgenden Tabelle zusammengefasst:
{| class="wikitable"
!Method
!Description
|-
|'''second'''
|Die Sekunde einer <code>Date</code>-Eigenschaft
|-
|'''minute'''
|Die Minute einer <code>Date</code>-Eigenschaft
|-
|'''hour'''
|Die Stunde einer <code>Date</code>-Eigenschaft
|-
|'''day'''
|Der Tag einer <code>Date</code>-Eigenschaft
|-
|'''month'''
|Den Monat einer <code>Date</code>-Eigenschaft
|-
|'''year'''
|Der Jahr einer <code>Date</code>-Eigenschaft
|-
|'''lower'''
|Konvertiert eine String-Eigenschaft in kleinbuchstaben
|-
|'''upper'''
|Konvertiert eine String-Eigenschaft in Großbuchstaben
|-
|'''length'''
|Die Länge einer String-Eigenschaft
|-
|'''trim'''
|[http://grails.asia/groovy-trim-examples Trimmt] eine String-Eigenschaft
|}
'''HINWEIS: Derzeit können Funktionen nur auf Eigenschaften oder Zuordnungen von Domänenklassen angewendet werden. Man kann beispielsweise keine Funktion für ein Ergebnis einer Unterabfrage verwenden.'''
 
Die folgende Abfrage kann beispielsweise verwendet werden, um alle 2011 geborenen <code>Pet</code>s zu finden:<syntaxhighlight lang="groovy">
def query = Pet.where {
year(birthDate) == 2011
}
</syntaxhighlight>Man kann Funktionen auch auf Assoziationen anwenden:<syntaxhighlight lang="groovy">
def query = Person.where {
year(pets.birthDate) == 2009
}
</syntaxhighlight><br />
 
====Batch-Updates und Löschungen====
Da jeder <code>where</code>-Methodenaufruf eine DetachedCriteria-Instanz zurückgibt kann man <code>where</code>-Abfragen verwenden, um Stapelvorgänge wie Stapelaktualisierungen und -löschungen auszuführen. Mit der folgenden Abfrage werden beispielsweise alle Personen mit dem Nachnamen "Simpson" aktualisiert, um den Nachnamen "Bloggs" zu erhalten:<syntaxhighlight lang="groovy">
DetachedCriteria<Person> query = Person.where {
lastName == 'Simpson'
}
int total = query.updateAll(lastName:"Bloggs")
</syntaxhighlight>HINWEIS: Join-Abfragen (Abfragen, die Zuordnungen abfragen) sind nicht zulässig sind.
 
Um die abgefragten/gefunden Datensätze stapelweisen zu Löschen kann man die Methode <code>deleteAll</code> verwenden:<syntaxhighlight lang="groovy">
DetachedCriteria<Person> query = Person.where {
lastName == 'Simpson'
}
int total = query.deleteAll()
</syntaxhighlight>
 
 
===[http://gorm.grails.org/6.1.x/hibernate/manual/#criteria (Angehängte/Attached) Kriterien]===
Kriterien sind eine erweiterte/komplexere Methode, bei der mithilfe eines Groovy-Builders potenziell komplexe Abfragen erstellt werden. Kriterien-Abfragen zu nutzen ist ein viel besserer Ansatz als das Erstellen von Query-Strings mit einem StringBuilder.
 
Kriterien können entweder mit den Methoden [http://gorm.grails.org/6.1.x/hibernate/api/org/grails/datastore/gorm/GormEntity.html#createCriteria() createCriteria()] oder [http://gorm.grails.org/6.1.x/hibernate/api/org/grails/datastore/gorm/GormEntity.html#withCriteria(groovy.lang.Closure) withCriteria(Closure)] verwendet werden.
 
Der Builder verwendet die Kriterien-API von Hibernate. Die Knoten in diesem Builder ordnen die statischen Methoden zu, die in der [http://docs.jboss.org/hibernate/orm/current/javadocs/org/hibernate/criterion/Restrictions.html Restrictions]-Klasse der Hibernate Criteria-API gefunden werden kann. Beispielsweise:<syntaxhighlight lang="groovy">
def c = Account.createCriteria()
def results = c {
between("balance", 500, 1000)
eq("branch", "London")
or {
like("holderFirstName", "Fred%")
like("holderFirstName", "Barney%")
}
maxResults(10)
order("holderLastName", "desc")
}
</syntaxhighlight>Mit diesem Kriterium werden bis zu 10 <code>Account</code>-Objekte in einer Liste ausgewählt, die den folgenden Kriterien entsprechen:
 
*<code>balance</code> liegt zwischen 500 und 1000
*<code>branche</code> ist London
 
*<code>holderFirstName</code> beginnt mit Fred oder Barney
 
Die Ergebnisse werden in absteigender Reihenfolge nach <code>holderLastName</code> sortiert.
 
Wenn keine Datensätze mit den oben genannten Kriterien gefunden werden, wird eine leere Liste zurückgegeben.
<br />
 
====Konjunktionen und Disjunktionen====
Wie im vorherigen Beispiel gezeigt können Kriterien in einem logischen ODER mit einem <code>or {}</code>-Block gruppiert werden:<syntaxhighlight lang="groovy">
or {
between("balance", 500, 1000)
eq("branch", "London")
}
</syntaxhighlight>Dies funktioniert auf die gleiche Angehensweiße mit einem logischen UND:<syntaxhighlight lang="groovy">
and {
between("balance", 500, 1000)
eq("branch", "London")
}
</syntaxhighlight>Auch Negierungen können mithilfe einer logischen NOT-Anweisung durchgesetzt werden:<syntaxhighlight lang="groovy">
not {
between("balance", 500, 1000)
eq("branch", "London")
}
</syntaxhighlight>Alle Bedingungen der obersten Ebene sind implizit mit einem logischen UND verbunden.
 
<br />
 
====Assoziationen====
Assoziationen können abgefragt werden, indem ein Knoten vorhanden ist, der dem Eigenschaftsnamen entspricht. Angenommen, die <code>Account</code>-Klasse hatte viele <code>Transaction</code>-Objekte:<syntaxhighlight lang="groovy">
class Account {
//...
static hasMany = [transactions: Transaction]
//...
}
</syntaxhighlight>Wir können diese Zuordnung abfragen, indem wir den Eigenschaftsnamen <code>transactions</code> als Builder-Knoten verwenden:<syntaxhighlight lang="groovy">
def c = Account.createCriteria()
def now = new Date()
def results = c.list {
transactions {
between('date', now - 10, now)
}
}
</syntaxhighlight>
 
Mit dem obigen Code werden alle <code>Account</code>-Instanzen gefunden, die in den letzten 10 Tagen Transaktionen ausgeführt haben. Man kann solche Assoziations-Abfragen auch in logischen Blöcken verschachteln:<syntaxhighlight lang="groovy">
def c = Account.createCriteria()
def now = new Date()
def results = c.list {
or {
between('created', now - 10, now)
transactions {
between('date', now - 10, now)
}
}
}
</syntaxhighlight>In diesem Beispiel finden wir alle Konten, die entweder Transaktionen in den letzten 10 Tagen ausgeführt haben oder in den letzten 10 Tagen kürzlich erstellt wurden.
 
<br />
 
====Projections====
Projektionen können verwendet werden, um die Ergebnisse anzupassen. Hierzu definiert man einen Knoten namens <code>projections</code> im Baum des Kriterienerstellers. Innerhalb des Projektionsknotens gibt es äquivalente Methoden zu den Methoden die man in der Klasse <code>[http://docs.jboss.org/hibernate/orm/current/javadocs/org/hibernate/criterion/Projections.html Projections]</code> findet:<syntaxhighlight lang="groovy">
def c = Account.createCriteria()
 
def numberOfBranches = c.get {
projections {
countDistinct('branch')
}
}
</syntaxhighlight>Wenn in der Projektion mehrere Felder angegeben sind, wird eine Werte<u>liste</u> zurückgegeben. Andernfalls wird ein einzelner Wert zurückgegeben.
<br />
 
====Projection-Ergebnisse transformieren====
Wenn der von der Kriterienmethode zurückgegebene Rohwert oder das einfache Objektarray nicht den Anforderungen entspricht, kann das Ergebnis mit einem [http://docs.jboss.org/hibernate/orm/current/javadocs/org/hibernate/transform/ResultTransformer.html ResultTransformer] transformiert werden. Angenommen, man möchte die Kriterienergebnisse in eine Karte umwandeln, damit wir die Werte einfach nach Schlüssel referenzieren können:<syntaxhighlight lang="groovy">
def c = Account.createCriteria()
 
def accountsOverview = c.get {
// ALIAS_TO_ENTITY_MAP: Each row of results is a Map from alias to entity instance
resultTransformer(CriteriaSpecification.ALIAS_TO_ENTITY_MAP)
projections {
sum('balance', 'allBalances')
countDistinct('holderLastName', 'lastNames')
}
}
 
// accountsOverview.allBalances
// accountsOverview.lastNames
</syntaxhighlight>Beachte: Wir haben jeder Projektion einen Alias als zusätzlichen Parameter hinzugefügt, der als Schlüssel verwendet werden soll. Damit dies funktioniert, müssen für alle Projektionen Aliase definiert sein, da sonst der entsprechende Karteneintrag nicht erstellt wird.
 
Wir können das Ergebnis auch über die [http://docs.jboss.org/hibernate/orm/current/javadocs/org/hibernate/transform/Transformers.html#aliasToBean-java.lang.Class- Transformers.aliasToBean()]-Methode in ein Objekt unserer Wahl umwandeln. In diesem Fall verwandeln wir es in ein <code>AccountOverview</code>-Objekt:<syntaxhighlight lang="groovy">
class AccountsOverview {
Number allBalances
Number lastNames
}
 
 
</syntaxhighlight><syntaxhighlight lang="groovy">
def c = Account.createCriteria()
 
def accountsOverview = c.get {
// aliasToBean: Creates a resulttransformer that will inject aliased values into instances of Class via property methods or fields.
resultTransformer(Transformers.aliasToBean(AccountsOverview))
projections {
sum('balance', 'allBalances')
countDistinct('holderLastName', 'lastNames')
}
}
 
// accountsOverview instanceof AccountsOverview
</syntaxhighlight>Jeder Alias muss eine entsprechende Eigenschaft oder einen expliziten Setter für die Bean haben, andernfalls wird eine Ausnahme ausgelöst.
<br />
====SQL-Projektionen====
Die Kriterien-DSL bietet Zugriff auf die SQL-Projektions-API von Hibernate.
 
Das erste Argument für die <code>[https://docs.jboss.org/hibernate/orm/current/javadocs/org/hibernate/criterion/Projections.html#sqlProjection-java.lang.String-java.lang.String:A-org.hibernate.type.Type:A- sqlProjection]</code>-Methode ist das SQL-Fragment, das die Projektionen definiert.
 
Das zweite Argument ist eine Liste von Zeichenfolgen, die die Aliasnamen der Spalten darstellen, die den in SQL ausgedrückten projizierten Werten entsprechen.
 
Das dritte Argument ist eine Liste von <code>org.hibernate.type.Type</code>-Instanzen, die den in SQL ausgedrückten projizierten Werten entsprechen. Die API unterstützt alle <code>org.hibernate.type.Type</code>-Objekte, aber Konstanten wie INTEGER, LONG, FLOAT usw. werden vom DSL bereitgestellt, die allen in <code>org.hibernate.type.StandardBasicTypes</code> definierten Typen entsprechen.
 
 
 
Betrachte man die folgenden Domänenklasse..<syntaxhighlight>
// Box is a domain class...
class Box {
int width
int height
}
 
 
</syntaxhighlight>..und führt die folgende Projektionsanweisung..<syntaxhighlight lang="groovy">
// Use SQL projections to retrieve the perimeter and area of all of the Box instances...
def c = Box.createCriteria()
 
def results = c.list {
projections {
sqlProjection '(2 * (width + height)) as perimeter, (width * height) as area', ['perimeter', 'area'], [INTEGER, INTEGER]
}
}
</syntaxhighlight>...mit diesem Datensatz aus..
{| class="wikitable"
!width
!height
|-
|2
|7
|-
|2
|8
|-
|2
|9
|-
|4
|9
|}
..bekommt man folgendes Ergebniss:<syntaxhighlight lang="groovy">
[[18, 14], [20, 16], [22, 18], [26, 36]]
</syntaxhighlight>
 
 
 
Wenn nur 1 Wert projiziert wird, müssen der Alias und der Typ nicht in eine Liste aufgenommen werden:<syntaxhighlight lang="groovy">
def results = c.list {
projections {
sqlProjection 'sum(width * height) as totalArea', 'totalArea', INTEGER
}
}
</syntaxhighlight>Diese Abfrage würde ein einzelnes Ergebnis mit dem Wert 84 als Gesamtfläche aller Box-Instanzen zurückgeben.
 
DSL unterstützt gruppierte Projektionen mit der Methode <code>[https://docs.jboss.org/hibernate/orm/current/javadocs/org/hibernate/criterion/Projections.html#sqlGroupProjection-java.lang.String-java.lang.String-java.lang.String:A-org.hibernate.type.Type:A- sqlGroupProjection]</code>.<syntaxhighlight lang="groovy">
def results = c.list {
projections {
sqlGroupProjection 'width, sum(height) as combinedHeightsForThisWidth', 'width', ['width', 'combinedHeightsForThisWidth'], [INTEGER, INTEGER]
}
}
</syntaxhighlight>Das erste Argument für die Methode <code>sqlGroupProjection</code> ist das SQL-<code>SELECT</code>-Fragment, das die Projektionen definiert.
 
Das zweite Argument repräsentiert die SQL <code>GROUP BY</code>-Klausel, die Teil der Abfrage sein sollte. Diese Zeichenfolge kann ein einzelner Spaltenname oder eine durch Kommas getrennte Liste von Spaltennamen sein.
 
Das dritte Argument ist eine Liste von Zeichenfolgen, die Spaltenaliasnamen darstellen, die den in SQL ausgedrückten projizierten Werten entsprechen.
 
Das vierte Argument ist eine Liste von <code>org.hibernate.type.Type</code>-Instanzen, die den in SQL ausgedrückten projizierten Werten entsprechen.
 
Die obige Abfrage projiziert die kombinierten Höhen von Feldern, die nach Breite gruppiert sind, und liefert Ergebnisse, die wie folgt aussehen:<syntaxhighlight lang="groovy">
[[2, 24], [4, 9]]
</syntaxhighlight>Jede der inneren Listen enthält 2 Werte. Der erste Wert ist eine Breite und der zweite Wert ist die Summe der Höhen aller Boxen, die diese Breite haben.
 
 
====''SQL-Restriktionen benutzen''====
''Man kann auch auf die von Hibernate bereitgestellten SQL-Restriktions-Funktion zugreifen.''<syntaxhighlight lang="groovy">
def c = Person.createCriteria()
 
def peopleWithShortFirstNames = c.list {
sqlRestriction "char_length(first_name) <= 4"
}
</syntaxhighlight>''SQL-Einschränkungen können parametrisiert werden, um SQL-Injection-Schwachstellen im Zusammenhang mit dynamischen Einschränkungen zu beheben.''<syntaxhighlight lang="groovy">
def c = Person.createCriteria()
 
def peopleWithShortFirstNames = c.list {
sqlRestriction "char_length(first_name) < ? AND char_length(first_name) > ?", [maxValue, minValue]
}
</syntaxhighlight>''Beachte: Der Parameter ist SQL. Das im Beispiel referenzierte Attribut <code>first_name</code> bezieht sich auf das Persistenzmodell und nicht auf das Objektmodell wie in HQL-Abfragen. Die Person-Eigenschaft mit dem Namen <code>firstName</code> wird der Spalte <code>first_name</code> in der Datenbank zugeordnet, und man muss in der Zeichenfolge <code>sqlRestriction</code> darauf verweisen.''
 
''Das hier verwendete SQL ist nicht unbedingt datenbankübergreifend portierbar.''
<br />
 
====Skrollbare Ergebnisse====
Man kann die [http://docs.jboss.org/hibernate/orm/current/javadocs/org/hibernate/ScrollableResults.html ScrollableResults]-Funktion von Hibernate verwenden, indem man die <code>scroll</code>-Methode aufruft:<syntaxhighlight lang="groovy">
def results = crit.scroll {
maxResults(10)
}
def f = results.first()
def l = results.last()
def n = results.next()
def p = results.previous()
 
def future = results.scroll(10)
def accountNumber = results.getLong('number')
</syntaxhighlight>Um die Dokumentation zu quotieren:<blockquote>Ein Ergebnisiterator, mit dem Sie sich in beliebigen Schritten innerhalb der Ergebnisse bewegen können. Das Query / ScrollableResults-Muster ist dem JDBC PreparedStatement / ResultSet-Muster sehr ähnlich, und die Semantik der Methoden dieser Schnittstelle ähnelt den ähnlich benannten Methoden in ResultSet.</blockquote>Im Gegensatz zu JDBC sind die Ergebnisspalten von Null an nummeriert.
<br />
 
====Festlegen von Eigenschaften in der Criteria-Instanz====
Wenn ein Knoten in der Builder-Struktur nicht mit einem bestimmten Kriterium übereinstimmt, wird versucht, eine Eigenschaft für das Criteria-Objekt selbst festzulegen. Dies ermöglicht den vollständigen Zugriff auf alle Eigenschaften in dieser Klasse. In diesem Beispiel werden <code>[https://docs.jboss.org/hibernate/orm/current/javadocs/org/hibernate/Criteria.html#setMaxResults-int- setMaxResults]</code>, <code>[https://docs.jboss.org/hibernate/orm/current/javadocs/org/hibernate/Criteria.html#setFirstResult-int- setFirstResult]</code> und <code>[https://docs.jboss.org/hibernate/orm/current/javadocs/org/hibernate/Criteria.html#setFetchMode-java.lang.String-org.hibernate.FetchMode- setFetchMode]</code> für die [http://docs.jboss.org/hibernate/orm/current/javadocs/org/hibernate/Criteria.html Criteria]-Instanz aufgerufen:<syntaxhighlight lang="groovy">
import org.hibernate.FetchMode as FM
...
def results = c.list {
maxResults(10)
firstResult(50)
fetchMode("aRelationship", FM.JOIN)
}
</syntaxhighlight><br />
 
====Abfragen mit Eager-Fetching durchführen====
Die <code>join</code>-Methode weist die Kriterien-API an, einen JOIN zu verwenden, um die benannten Zuordnungen zu den <code>Task</code>-Instanzen abzurufen. <syntaxhighlight lang="groovy">
def criteria = Task.createCriteria()
def tasks = criteria.list{
eq "assignee.id", task.assignee.id
join 'assignee'
join 'project'
order 'priority', 'asc'
}
</syntaxhighlight>
 
=====Nicht empfohlen für Eins-zu-Viele=====
Es ist wahrscheinlich am besten, dies nicht für Eins-zu-Viele-Assoziationen zu verwenden, da man höchstwahrscheinlich doppelte Ergebnisse erzielen wird. Verwende stattdessen den <code>select</code> Fetch-Modus:<syntaxhighlight lang="groovy">
import org.hibernate.FetchMode as FM
...
def results = Airport.withCriteria {
eq "region", "EMEA"
fetchMode "flights", FM.SELECT
}
</syntaxhighlight>Obwohl dieser Ansatz eine zweite Abfrage auslöst, um die <code>flights</code>-Assoziationen zu erhalten, erhält man zuverlässige Ergebnisse - auch mit der Option <code>maxResults</code>. <code>fetchMode</code> und <code>join</code> sind allgemeine Einstellungen der Abfrage und können nur auf der obersten Ebene angegeben werden, d. h. Sie können nicht in Projektionen oder Zuordnungsbeschränkungen verwendet werden.
 
=====Implizites Eager-Fetching=====
Ein wichtiger Punkt, den man berücksichtigen sollte, ist, dass Assoziationen automatisch geladen werden, wenn man Assoziationen in die Abfrageeinschränkungen afunimmt. Zum Beispiel in dieser Abfrage..<syntaxhighlight lang="groovy">
def results = Airport.withCriteria {
eq "region", "EMEA"
flights {
like "number", "BA%"
}
}
</syntaxhighlight>..würde die <code>flights</code>-Sammlung eifrig über einen <code>JOIN</code> geladen, obwohl der Abrufmodus nicht explizit festgelegt wurde.
<br />
 
====Methoden-Referenz====
Wenn man den Builder ohne Methodennamen aufgerufen wird..<syntaxhighlight lang="groovy">
c { /* */ }
</syntaxhighlight>.. ist die Standard-Methode alle Ergebnisse zurückzugeben - was das obere Beispiel äquivalent zu dem folgenden Beispiel macht:<syntaxhighlight lang="groovy">
c.list { /* */ }
</syntaxhighlight>
{| class="wikitable"
!Methode
!Beschreibung
|-
|'''list'''
|Dies ist die Standardmethode. Es werden alle übereinstimmenden Zeilen zurückgegeben.
|-
|'''get'''
|Gibt eine eindeutige Ergebnismenge zurück, d. H. Nur eine Zeile. Die Kriterien müssen so formuliert sein, dass nur eine Zeile abgefragt wird. Diese Methode ist nicht mit einer Beschränkung auf die erste Zeile zu verwechseln.
|-
|'''scroll'''
|Gibt eine scrollbare Ergebnismenge zurück.
|-
|'''listDistinct'''
|Wenn Unterabfragen oder Zuordnungen verwendet werden, kann es sein, dass in der Ergebnismenge mehrere Male dieselbe Zeile angezeigt wird. Dies ermöglicht das Auflisten nur bestimmter Entitäten und entspricht DISTINCT_ROOT_ENTITY der CriteriaSpecification-Klasse.
|-
|'''count'''
|Gibt die Anzahl der übereinstimmenden Zeilen zurück.
|}
 
====Kriterien kombinieren====
Man kann mehrere (angehängte) Kriterien-Closures wie folgt definieren:<syntaxhighlight lang="groovy">
def emeaCriteria = {
eq "region", "EMEA"
}
 
def results = Airport.withCriteria {
emeaCriteria.delegate = delegate
emeaCriteria()
flights {
like "number", "BA%"
}
}
</syntaxhighlight>
Diese Technik erfordert, dass sich jedes Kriterium auf dieselbe Domänenklasse bezieht. Ein flexiblerer Ansatz ist die Verwendung von getrennten Kriterien, wie im folgenden Abschnitt beschrieben wird:
 
===[http://gorm.grails.org/6.1.x/hibernate/manual/#detachedCriteria getrennte Kriterien]===
Getrennte Kriterien sind Kriterienabfragen, die keiner bestimmten Datenbanksitzung / -verbindung zugeordnet sind. Abgelöste Kriterienabfragen werden seit Grails 2.0 unterstützt und haben viele Verwendungsmöglichkeiten, einschließlich der Möglichkeit, '''allgemeine Abfragen für wiederverwendbare Kriterien zu erstellen, Unterabfragen auszuführen und Stapelaktualisierungen / -löschungen auszuführen.'''
<br />
 
====getrennte Kriterien definieren====
Der primäre Einstiegspunkt für die Verwendung der getrennten Kriterien ist die [http://gorm.grails.org/6.1.x/hibernate/api/grails/gorm/DetachedCriteria.html DetachedCriteria]-Klasse, die eine Domänenklasse als einziges Argument für ihren Konstruktor akzeptiert:<syntaxhighlight lang="groovy">
import grails.gorm.*
...
def criteria = new DetachedCriteria(Person)
</syntaxhighlight>Sobald man einen Verweis auf eine getrennte Kriterieninstanz erhalten hat, kann man <code>where</code> oder Kriterien -Abfragen ausführen, um die entsprechende Abfrage aufzubauen. Um eine normale Kriterienabfrage zu erstellen, kann man die Methode <code>build</code> verwenden:<syntaxhighlight lang="groovy">
def criteria = new DetachedCriteria(Person).build {
eq 'lastName', 'Simpson'
}
</syntaxhighlight>'''Die Methoden in der DetachedCriteria-Instanz mutieren das ursprüngliche Objekt nicht''', sondern geben stattdessen eine neue Abfrage zurück. Mit anderen Worten: Man muss den Rückgabewert der <code>build</code> -Methode verwenden, um das mutierte Kriterienobjekt zu erhalten:<syntaxhighlight lang="groovy">
def criteria = new DetachedCriteria(Person).build {
eq 'lastName', 'Simpson'
}
def bartQuery = criteria.build {
eq 'firstName', 'Bart'
}
 
 
</syntaxhighlight><br />
 
====getrennte Kriterien ausführen====
'''Im Gegensatz zu regulären Kriterien sind getrennte Kriterien insofern faul, als zum Zeitpunkt der Definition keine Abfrage ausgeführt wird.''' Sobald eine Abfrage mit getrennten Kriterien erstellt wurde, gibt es eine Reihe nützlicher Abfragemethoden, die in der folgenden Tabelle zusammengefasst sind:
{| class="wikitable"
!Method
!Description
|-
|'''list'''
|Listet alle übereinstimmenden Entitäten auf
|-
|'''get'''<nowiki> | </nowiki>'''find'''
|Gibt ein einzelnes übereinstimmendes Ergebnis zurück
|-
|'''count'''
|Zählt alle übereinstimmenden Datensätze
|-
|'''exists'''
|Gibt true zurück, wenn übereinstimmende Datensätze vorhanden sind
|-
|'''deleteAll'''
|Löscht alle übereinstimmenden Datensätze
|-
|'''updateAll(Map)'''
|Aktualisiert alle übereinstimmenden Datensätze mit den angegebenen Eigenschaften
|}
Im folgenden Code werden beispielsweise die ersten 4 übereinstimmenden Datensätze aufgelistet, die nach der Eigenschaft <code>firstName</code> sortiert werden:<syntaxhighlight lang="groovy">
def criteria = new DetachedCriteria(Person).build {
eq 'lastName', 'Simpson'
}
def results = criteria.list(max:4, sort:"firstName")
</syntaxhighlight>Man kann der <code>list</code>-Methode auch zusätzliche Kriterien übergeben:<syntaxhighlight lang="groovy">
def results = criteria.list(max:4, sort:"firstName") {
gt 'age', 30
}
</syntaxhighlight>
 
 
Die <code>'''DetachedCriteria'''</code>-Klasse selbst '''implementiert''' auch '''die <code>Iterable</code>-Schnittstelle''', was bedeutet, dass sie wie eine Liste behandelt werden kann.
 
In diesem Fall wird die Abfrage nur ausgeführt, wenn die einzelnen Methoden aufgerufen werden. Gleiches gilt für alle anderen Iterationsmethoden der Groovy-Sammlung.<syntaxhighlight lang="groovy">
def criteria = new DetachedCriteria(Person).build {
eq 'lastName', 'Simpson'
}
criteria.each {
println it.firstName
}
</syntaxhighlight>
 
Man kann auch dynamische Finder auf <code>DetachedCriteria</code>-Objekten ausführen, genau wie bei Domänenklassen:<syntaxhighlight lang="groovy">
def criteria = new DetachedCriteria(Person).build {
eq 'lastName', 'Simpson'
}
def bart = criteria.findByFirstName("Bart")
</syntaxhighlight>
 
====getrennte Kriterien für Unterabfragen benutzen====
'''Im Rahmen einer regulären/angehängten Kriterienabfrage kann man ein DetachedCriteria verwenden, um eine Unterabfrage auszuführen.''' Wenn man beispielsweise alle Personen finden möchten, die älter als das Durchschnittsalter sind, kann man dies zum Beispiel so lösen:<syntaxhighlight lang="groovy">
def results = Person.withCriteria {
gt "age", new DetachedCriteria(Person).build {
projections {
avg "age"
}
}
order "firstName"
}
</syntaxhighlight>
In diesem Fall ist die Unterabfrageklasse dieselbe wie die ursprüngliche Kriterienabfrageklasse (d. H. Person), und daher kann die Abfrage verkürzt werden auf:<syntaxhighlight lang="groovy">
def results = Person.withCriteria {
gt "age", {
projections {
avg "age"
}
}
order "firstName"
}
</syntaxhighlight>
Wenn sich die Unterabfrageklasse von der ursprünglichen Kriterienabfrage unterscheidet, müsste man die ursprüngliche Syntax verwenden.
 
Im vorherigen Beispiel stellte die Projektion sicher, dass '''nur ein einziges Ergebnis''' zurückgegeben wurde ('''das Durchschnittsalter'''). Wenn seine Unterabfrage mehrere Ergebnisse zurückgibt, müssen verschiedene Kriterienmethoden verwendet werden, um das Ergebnis zu vergleichen. Um beispielsweise alle Personen zu finden, die älter als 18 bis 65 Jahre sind, kann eine <code>gtAll</code>-Abfrage verwendet werden:<syntaxhighlight lang="groovy">
def results = Person.withCriteria {
gtAll "age", {
projections {
property "age"
}
between 'age', 18, 65
}
 
order "firstName"
}
</syntaxhighlight>
In der folgenden Tabelle sind die '''Kriterienmethoden für die Bearbeitung von Unterabfragen''' zusammengefasst, die mehrere Ergebnisse zurückgeben:
{| class="wikitable"
!Methode
!Beschreibung
|-
|'''gtAll'''
|größer als alle Unterabfrageergebnisse
|-
|'''geAll'''
|größer oder gleich allen Unterabfrageergebnissen
|-
|'''ltAll'''
|weniger als alle Unterabfrageergebnisse
|-
|'''leAll'''
|kleiner oder gleich allen Unterabfrageergebnissen
|-
|'''eqAll'''
|gleich allen Unterabfrageergebnissen
|-
|'''neAll'''
|nicht gleich allen Unterabfrageergebnissen
|}
<br />
 
===Hibernate Query Language (HQL)===
Siehe [http://gorm.grails.org/6.1.x/hibernate/manual/#hql GORM-Dokumentation] für einen Überblick über HQL sowie die [http://docs.jboss.org/hibernate/orm/current/userguide/html_single/Hibernate_User_Guide.html#hql Hibernate-Dokumentation] für eine ausführliche Version
 
 
TODO
 
<nowiki>#</nowiki>8 Advanced GORM Features | Überfliegen und schauen was wichtig sein könnt
 
<nowiki>#</nowiki>9 Programmatic Transactions | ?
 
<nowiki>#</nowiki>10 Data Services
 
<nowiki>#</nowiki>11 Mutliple DataSources | auslassen und "Siehe Dokumentation"
 
<nowiki>#</nowiki>12 Multi-Tenancy | ???
 
<nowiki>#</nowiki>13 Constraints
 
<nowiki>#</nowiki>14 Testing
<br />
=[https://docs.grails.org/3.3.9/guide/traits.html Traits]=
Grails stellt eine Reihe von <code>[http://docs.groovy-lang.org/next/html/documentation/core-traits.html Traits]</code> zur Verfügung, die Zugang zu Eigenschaften und Verhalten bieten, auf die von verschiedenen Grails-Artefakten sowie von beliebigen Groovy-Klassen aus zugegriffen werden kann, die Teil eines Grails-Projekts sind.

Navigationsmenü