Le framework Grails
Le framework GRAILS
Table des matières
- Introduction
- Groovy : Le langage sous-jacent
- Philosophie de Grails
- Architecture de Grails
- Évolution des versions de Grails
- Structure d'un projet
- Gestion des ressources
- Configuration d'une base de données
- BuildConfig et configuration (Grails 2.X)
- UrlMapping
- Préparation de l'environnement de travail
- Modélisation avec GORM et Hibernate
- Manipulation des objets
- Requêtes GORM
- Contrôleurs
- Bootstrap
- Services
- Taglibs
- Sécurité dans Grails
- Grails Moderne (Versions 3.x à 6.x)
- Déploiement et Conteneurisation
- Performances et Optimisations
- Documentation et Références
- Migration entre versions
- Bonnes pratiques et astuces
- Ressources complémentaires
- Conclusion
Introduction
Grails est un framework open source de développement agile, né en 2005 et initié par Graeme Rocher. Initialement appelé "Groovy on Rails", il a été renommé Grails et est basé sur le langage Groovy. Il est construit sur un modèle MVC (Modèle-Vue-Contrôleur) et s'est étendu sur d'autres aspects au fil du temps.
Groovy - Le langage sous-jacent
- Créé en 2003
- Langage orienté objet pour la plateforme Java
- Alternative inspirée de Python, Ruby et Smalltalk
- Intégré et compatible avec la JVM
- Permet d'utiliser des librairies Java existantes
Avantages de Groovy
- Développement rapide grâce à une syntaxe simple
- Beaucoup de raccourcis syntaxiques
- Manipulation facile de structures XML et JSON
- Système de collection complet et flexible
- Documentation : http://groovy-lang.org/single-page-documentation.html
Typage dynamique
- Par défaut dynamique
- Typage fort possible pour plus de rigueur
- Utilisation du mot clef « def »
def nombre = 10
def chaîne = "Bonjour"
String phraseTypée = "Je suis typée"Syntaxe simple
Affichage dans la console / debugging :
println "Hello World"
println(nomVariable)Collections Groovy
On utilise principalement:
- Lists : ordonnées, crée un ArrayList au Runtime
- Sets : contrainte d'unicité, non ordonnés
- Maps : collection clef-valeur, ordonnée
- Ranges : intervalle via la syntaxe ".."
Groovy Lists
def liste = [1, 2, 3, 4, 5]
def liste2 = ["a", "b", "c"]Accès via l'index :
liste[0] // retourne 1Ajout d'éléments :
liste.add(6)
liste << 7 // opérateur d'ajoutRetrait d'éléments :
liste.remove(2) // supprime l'élément à l'index 2Groovy Sets
def ensemble = [1, 2, 3, 3, 4] as Set
// résultat: [1, 2, 3, 4] (les doublons sont éliminés)Groovy Maps
def map = [clé1: "valeur1", clé2: "valeur2"]Ajout d'éléments :
map.put("clé3", "valeur3")
map.clé4 = "valeur4"Retrait d'éléments :
map.remove("clé2")Groovy Ranges
def range = 1..5 // représente [1, 2, 3, 4, 5]
def alphabet = 'a'..'e' // représente ['a', 'b', 'c', 'd', 'e']Manipulations des Collections
Compter les éléments :
liste.size()Créer un sous-ensemble :
liste[0..2] // extrait les 3 premiers élémentsAutres manipulations :
liste.findAll { it > 3 } // trouve tous les éléments > 3
liste.collect { it * 2 } // multiplie chaque élément par 2Closures
- Différentes des méthodes classiques
- Interprétation de code entre "{" et "}" pour en faire le corps d'une méthode
- Non liées à une classe
- Peuvent retourner une valeur
- Peuvent être assignées à une variable
def fermeture = { paramètre ->
// corps de la closure
return paramètre * 2
}
// utilisation:
def résultat = fermeture(5) // résultat = 10Paramètres implicites :
def doubler = { it * 2 } // 'it' est le paramètre impliciteClosures importantes :
- sort : tri de collection
- count : comptage selon un critère
- collect : transformation d'éléments
- each : parcours d'éléments
Philosophie de Grails
Les cinq fondamentaux
- DRY : Don't Repeat Yourself
- Développement rapide et prototypage
- Scaffolding (génération automatique de code)
- Adapté aux gros projets
- Architecture orientée modèle
- « Convention » plutôt que « Configuration »
- Exploite la puissance de la JVM (compilation en bytecode Java)
Avantages
- Web MVC facile à utiliser
- GSP : Langage de template simple et complet
- Serveur embarqué
- GORM : Modélisation et accès aux données
- Base de donnée simulée (développement)
- Internationalisation
- Tests & Tests unitaires
- Documentation très riche
Créer un projet : 3 étapes
- Création des classes de domaine
- Scaffold (génération automatique)
- Run (exécution)
Plugins
- Par défaut : Tomcat, Hibernate
- Utiles : Service de mail, Sécurité, Paiement en ligne, etc.
- Note : Accélèrent grandement le développement, ne pas en abuser
Architecture de Grails

Évolution des versions de Grails
| Version | Caractéristiques principales | Frameworks sous-jacents |
|---|---|---|
| 2.x | Ressources plugin, Gant comme système de build | Spring 3.x, Hibernate 3/4 |
| 3.x | Spring Boot, Gradle comme système de build | Spring Boot 1.x, Hibernate 5 |
| 4.x | Intégration avec Micronaut | Spring Boot 2.1, Groovy 2.5 |
| 5.x | Consolidation et mises à jour | Spring Boot 2.5, Groovy 3, Micronaut 3 |
| 6.x | Java 11 minimum, UI Forge | Spring Boot 2.7, Micronaut amélioré |
Différences de configuration entre Grails 2.x et les versions modernes
| Grails 2.X | Grails 3.X et + | Fonction |
|---|---|---|
| BuildConfig.groovy | build.gradle | Configuration de build, dépendances, plugins |
| Config.groovy | application.groovy/application.yml | Configuration générale du projet |
| DataSource.groovy | application.groovy/application.yml | Configuration des accès aux bases de données |
| UrlMappings.groovy | controllers/UrlMappings.groovy | Configuration des schémas d'URL (déplacé) |
| BootStrap.groovy | init/BootStrap.groovy | Initialisation au démarrage (déplacé) |
Structure d'un projet
- assets : Ressources Web
- config : Configuration
- controllers : Contrôleurs web
- domain : Définition du modèle
- i18n : Internationalisation
- init : contient le "Bootstrap"
- services : Couche de service
- taglib : "Taglibs" personnalisées
- utils : "Classes codec" (encoders/decoders)
- views : Groovy Server Pages
- src : Autres sources (Java/Groovy) et tests

Gestion des ressources
Evolution des plugins de ressources
Ressources (Grails 2.0)
- Plugin de base inclus depuis la version 2.0
- Gestion des ressources web (JavaScript, CSS)
- Optimisation (compression, concaténation)
Assets (Grails 2.4+)
- Plugin de base inclus depuis la version 2.4
- Gestion des ressources web (JavaScript, CSS, médias)
- Optimisation (compression, concaténation)
- Gestion par groupes ou ressources indépendantes


Configuration d'une base de données
DataSources (illustration Grails 2.X)
- Configurations différenciées : Développement / Test / Production
- Paramètre important
dbCreatequi définit le comportement au lancement:- create : Crée les tables à chaque démarrage
- create-drop : Crée puis supprime les tables à l'arrêt
- update : Met à jour le schéma si nécessaire

Basculer sur une base MySQL
- Ajouter le connecteur à votre projet (driver)
- Directement le JAR
- Plugin connecteur
- Changer la chaîne de connexion
BuildConfig et configuration (Grails 2.X)
- Configuration des VMs
- Configurations des dépendances
- Port de déploiement (
grails.server.port = 8081) - Configuration des plugins (runtime, build, compile, test, provided)
- Site officiel des plugins : https://grails.org/plugins/

Évolution avec Grails 3.X+
- Dans build.gradle
- Plus de distinction dépendances-plugins
- Nouveau site : https://bintray.com/grails/plugins
- Port de déploiement déporté dans conf/application.yml
UrlMapping
- Configuration des redirections
- Expressions régulières
- Vers des controllers/actions
- Vers des vues
- Vers des ressources (API REST)


Définitions complexes et contraintes :
- $ : permet de pré-nommer des variables
- Expressions régulières pour des besoins particuliers
Préparation de l'environnement de travail
Les outils à télécharger
- IntelliJ IDEA Ultimate edition : https://www.jetbrains.com/idea/download/
- Java 1.8 ou version supérieure (Java 11 minimum pour Grails 6)
- Grails (version à choisir selon vos besoins : 3.3, 4.x, 5.x ou 6.x)
Gestion des versions de Grails
- Linux / OS X / Cygwin : SDKMAN
- Windows :
- Téléchargement manuel : https://grails.org/download.html
- Posh GVM : https://github.com/flofreud/posh-gvm
Autres outils utiles
- Serveur Apache et SQL : WAMP / XAMP / LAMP
- MySQL Workbench
- Navigateur Chrome + Developer Tools ou Firefox + Firebug
- Outils pour requêtes HTTP & Services REST : curl, Postman
- Préprocesseurs CSS : SASS / SCSS / LESS
- Framework CSS : Bootstrap (plugin ou installation manuelle)
Modélisation avec GORM et Hibernate
Hibernate
- Framework open source de persistance (ORM)
- Utilisable dans tous les environnements (Client lourd / Web)
- Remplacement d'une DAL classique
- Utilisable sur n'importe quel SGBD relationnel
GORM (Grails Object Relational Mapping)
- Illusion de base de données orientée objet
- Correspondance monde objet / monde relationnel
- Appel de méthodes objets de haut niveau:
def book = Book.findByTitle("Groovy in Action")
book
.addToAuthors(name:"Dierk Koenig")
.addToAuthors(name:"Guillaume LaForge")
.save()Avantages de Hibernate/GORM
- On accède à des objets métiers au lieu de tables
- Rapidité de mise en place (petites applications)
- Syntaxe simple et unique pour tous les SGBD
- Gestion des transactions
- Génération et gestion automatique de propriétés
- Binding poussé
- Pas de SQL pour des requêtes simples
- On appelle directement les objets que l'on a créé
- Méthodes dynamiques
Inconvénients de Hibernate/GORM
- Peut être difficile à utiliser dans des projets complexes
- Surcouche, donc perte de performance
- On ne sait pas ce qu'il se passe derrière
- Blocage potentiel sans compréhension globale
Création du modèle
- Définition des attributs
- Relations (one-to-one, one-to-many, many-to-many)
- Contraintes
- Possibilité d'héritage (à éviter si possible - gourmand en ressources)
class Book {
String title
Date releaseDate
static hasMany = [authors: Author]
static constraints = {
title nullable: false, blank: false
releaseDate nullable: true
}
}Modélisation - Héritage
Différentes stratégies d'héritage:
- Table-per-hierarchy (par défaut)
- NOT NULL impossible au niveau de la base de données
- Possible au niveau métier
- Table-per-subclass
- Performances faibles
- Mieux pour certains cas d'utilisation
Relations dans GORM
One-to-one
Unidirectionnelle :
class Face {
Nose nose
}
class Nose {
}Bidirectionnelle :
class Face {
Nose nose
}
class Nose {
static belongsTo = [face: Face]
}One-to-many
class Author {
String name
static hasMany = [books: Book]
}
class Book {
String title
static belongsTo = [author: Author]
}BelongsTo cascade les "deletes" (suppression en cascade)
Many-to-many
class Book {
String title
static hasMany = [authors: Author]
}
class Author {
String name
static hasMany = [books: Book]
static belongsTo = Book // Définit le responsable de la relation
}Contraintes
static constraints = {
username nullable: false, blank: false, unique: true
email email: true, blank: false
age min: 18
website url: true
description maxSize: 1000
}Mapping personnalisé
static mapping = {
table 'USER_TABLE'
id column: 'USER_ID'
version false
firstName column: 'FIRST_NAME'
books lazy: false
cache true
}Manipulation des objets
Sauvegarde
objectInstance.save()Options :
- flush : quand défini à "true", persiste l'objet immédiatement
- validate : valide la persistance de l'objet
- insert : force hibernate à utiliser un INSERT(true) ou un UPDATE(false)
- failOnError : si la persistance échoue, une exception sera levée
- deepValidate : définit si les objets fils doivent aussi être validés
Ajout et suppression
// Ajout
vehiculeInstance.addToRoues(new Roue(...))
// Suppression
objectInstance.delete()
// ou
vehiculeInstance.removeFromRoues(roueInstance)Requêtes GORM
Dynamic Finders
// Récupération par ID
Object.get(id)
// Récupération de tous les objets
Object.list()
Object.getAll()
// Requête sur propriété
Object.findByName("lenom")
Object.findAllByName("lenom")Where Queries
Plus flexible que les "Dynamic Finders", moins verbeux que les "Critères".
Simple :
def books = Book.where { title == "The Stand" }.list()Complexe :
def books = Book.where {
(title =~ "%Grails%" || author.lastName == "Smith") &&
releaseDate > startDate &&
releaseDate < endDate
}.list(sort: "title", order: "asc")Critères
Mode de requête permettant de construire des requêtes complexes :
def books = Book.createCriteria().list {
eq("author.id", 1)
between("releaseDate", startDate, endDate)
or {
like("title", "%Grails%")
eq("publisher", "Manning")
}
order("title", "asc")
}Comparaison des modes de requête
| Where Queries | Critères | Description |
|---|---|---|
| == | eq | Égalité stricte |
| != | ne | Différence |
| > | gt | Supérieur à |
| < | lt | Inférieur à |
| >= | ge | Supérieur ou égal à |
| <= | le | Inférieur ou égal à |
| in | inList | Est contenu dans la liste fournie |
| ==~ | like | Like SQL case sensitif |
| =~ | ilike | Like SQL non case sensitif |
Contrôleurs
Structure de base
- Peut contenir plusieurs méthodes
- Méthode = Action
- Chaque action = une URI dans le modèle par défaut (UrlMapping)
- Action par défaut : index (si présente)
class BookController {
def index() {
[books: Book.list()] // retourne un modèle à la vue
}
def show(Long id) {
def book = Book.get(id)
[book: book]
}
}Scopes dans les contrôleurs
- servletContext : Portée application, instance de ServletContext
- session : Portée session, association d'état avec un utilisateur via cookies, instance de HttpSession
- request : Portée requête, stockage d'objets pour la requête, instance de HttpServletRequest
- params : Portée requête, tableau associatif contenant les paramètres
- flash : Temporaire, durée de vie de deux requêtes (courante et suivante)
def action() {
session.user = User.get(params.id)
flash.message = "Utilisateur connecté avec succès"
redirect(action: "dashboard")
}Retourner un objet
def show(Long id) {
def poiInstance = Poi.get(id)
respond poiInstance // Retourne format approprié selon le content-type
}Utilisation de withFormat pour spécifier des formats différents:
def show(Long id) {
def poiInstance = Poi.get(id)
withFormat {
html { respond poiInstance }
json { render poiInstance as JSON }
xml { render poiInstance as XML }
}
}Intercepteurs
Dans Grails 2.X
Directement dans le contrôleur:
def beforeInterceptor = {
log.debug "Executing action $actionName with params $params"
}
def afterInterceptor = { model ->
log.debug "Finished executing action $actionName"
return model
}Dans Grails 3.X+
Classe séparée:
class SecurityInterceptor {
SecurityInterceptor() {
matchAll().excludes(controller: "public")
}
boolean before() {
if (!session.user) {
redirect(controller: "login", action: "auth")
return false
}
return true
}
}Bootstrap
- Initialisation des données au lancement de l'application
- Pratique avec
dbCreate: create-droppour réinitialiser à chaque lancement - Utiliser les options de persistance immédiate (flush, failOnError)
- Utiliser des services pour la création des entités
// Bootstrap.groovy
def init = { servletContext ->
def adminRole = new Role(authority: 'ROLE_ADMIN').save(flush: true, failOnError: true)
def userRole = new Role(authority: 'ROLE_USER').save(flush: true, failOnError: true)
def adminUser = new User(username: 'admin', password: 'password').save(flush: true, failOnError: true)
UserRole.create(adminUser, adminRole, true)
}Services
- Contiennent la logique métier
- Souvent transactionnels
- Utilisent l'annotation @Transactional / @NotTransactional
- Rollback sur un throw d'une RuntimeException
@Transactional
class BookService {
Book save(Book book) {
book.save(flush: true, failOnError: true)
return book
}
@NotTransactional
List<Book> search(String query) {
Book.where { title =~ "%${query}%" }.list()
}
}Scopes des services
- Singleton (défaut) : une seule instance pour toute l'application
- Prototype : une nouvelle instance à chaque injection
- Request : une nouvelle instance par requête
- Flash : une nouvelle instance pour la requête courante et la suivante
- Flow : une nouvelle instance par "web flow"
- Conversation : une nouvelle instance par conversation (ensemble de flows)
- Session : une nouvelle instance par session HTTP
Injection de dépendances
class BookController {
// Injection par convention
BookService bookService
def list() {
[books: bookService.list()]
}
}Taglibs
- Balises personnalisées
- Factorisation du code dans les vues
// Définition
class MyTagLib {
static namespace = "custom"
def salut = { attrs, body ->
out << "Bonjour ${attrs.nom ?: 'inconnu'}"
out << body()
}
}
// Utilisation dans une GSP
<custom:salut nom="John">
contenu optionnel
</custom:salut>Sécurité dans Grails
Mécanismes de base
- Tous les accès via GORM sont systématiquement "escaped" (protection contre injections SQL)
- Formulaires générés via scaffolding "escaped" à l'affichage
- Liens générés via tags (link, form, createLink, createLinkTo) préviennent l'injection
- Codecs disponibles pour "escape" les données (
.encodeAsHTML())
Précautions avec les requêtes
Utiliser des paramètres positionnés ou nommés:
// Mauvaise pratique - risque d'injection
def users = User.executeQuery("from User where username='${params.username}'")
// Bonne pratique
def users = User.executeQuery("from User where username=?", [params.username])
// ou
def users = User.executeQuery("from User where username=:username", [username: params.username])Spring Security
Plugin populaire pour la gestion de l'authentification et des autorisations:
- Ajout de la dépendance
- Exécution du script d'initialisation:
grails s2-quickstart fr.mbds.tpgrails User Role UserRole - Configuration dans application.yml (ou Config.groovy pour Grails 2.x)
Service de sécurité
class MonController {
def springSecurityService
def action() {
def currentUser = springSecurityService.currentUser
def isLoggedIn = springSecurityService.isLoggedIn()
// ...
}
}Différentes méthodes de sécurisation
- Annotations:
@Secured(['ROLE_ADMIN'])
def adminAction() {
// Action sécurisée
}- Static Rules:
static rules = [
[pattern: '/admin/**', access: ['ROLE_ADMIN']],
[pattern: '/api/**', access: ['ROLE_API']],
[pattern: '/**', access: ['permitAll']]
]- Intercept URL Map:
grails.plugin.springsecurity.interceptUrlMap = [
[pattern: '/admin/**', access: ['ROLE_ADMIN']],
[pattern: '/api/**', access: ['ROLE_API']],
[pattern: '/**', access: ['permitAll']]
]Grails Moderne (Versions 3.x à 6.x)
Grails 3: Transition vers Spring Boot
Grails 3 a représenté un changement majeur avec l'adoption de Spring Boot comme fondation.
Changements principaux:
- Système de build: Passage de Gant à Gradle
- Structure du projet: Réorganisation alignée avec Spring Boot
- Configuration: Passage de Config.groovy à application.yml/application.groovy
- Profils d'application: Introduction pour différents types d'applications
- Asset pipeline: Amélioration de la gestion des ressources
Grails 4: Intégration avec Micronaut
Grails 4 a marqué l'intégration avec Micronaut, un framework optimisé pour les microservices.
Caractéristiques:
- Micronaut beans: Injection dans applications Grails
- Spring Boot 2.1: Mise à jour majeure
- Groovy 2.5: Support amélioré
- GORM 7: Performances optimisées
- Java 8 minimum: Prérequis technique
Utilisation de Micronaut dans Grails 4
// Dans build.gradle
dependencies {
implementation "io.micronaut:micronaut-http-client"
}
// Client HTTP déclaratif
@Client("https://api.example.com")
interface ExampleClient {
@Get("/data")
String getData()
}
// Injection dans un service
class ExampleService {
ExampleClient exampleClient
String fetchData() {
return exampleClient.getData()
}
}Grails 5: Consolidation
Mises à jour majeures:
- Groovy 3: Support du nouveau parseur Parrot
- Micronaut 3: Intégration améliorée
- Spring Boot 2.5: Nouvelles fonctionnalités
- Gradle 7: Build system modernisé
- Autowiring par type: Remplacement de l'autowiring par nom
Grails 6: Java 11 et améliorations
Nouveautés:
- Java 11 minimum: Performances et sécurité améliorées
- Spring Boot 2.7.12: Mise à jour majeure
- Grails Forge UI: Interface moderne pour création de projets
- CLI repensée: Basée sur Picocli et le moteur Rocker
Déploiement et Conteneurisation
Options modernes
- JAR autonome: "Make JAR, not WAR"
- Conteneurs Docker: Isolation et portabilité
- Cloud Platforms: AWS, Google Cloud, Azure
- Kubernetes: Orchestration des conteneurs
Exemple de Dockerfile pour Grails 6
FROM openjdk:11-jdk-slim
WORKDIR /app
COPY build/libs/myapp-0.1.jar application.jar
EXPOSE 8080
ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/app/application.jar"]Performances et Optimisations
Conseils d'optimisation
- Compilation statique:
@CompileStatic
class OptimizedService {
// Code compilé statiquement, plus rapide
}- Requêtes GORM optimisées:
// Éviter les requêtes N+1
def books = Book.findAll { author.id == 1 }
// Préférer
def books = Book.where { author.id == 1 }.join('author').list()- Caching:
@Cacheable('books')
List<Book> getAllBooks() {
return Book.list()
}- Contrôle du lazy loading:
static mapping = {
author lazy: false // Chargement eager
comments lazy: true // Chargement lazy
}Documentation et Références
Documentation officielle
- Documentation Grails: https://docs.grails.org/
- Plugins:
- https://grails.org/plugins/ (Grails 2.x)
- https://bintray.com/grails/plugins (Grails 3.x+)
- Spring Security Core: https://grails.github.io/grails-spring-security-core/
Migration entre versions
De Grails 2.x à Grails 3.x
La migration de Grails 2.x vers Grails 3.x représente un changement majeur en raison de l'adoption de Spring Boot:
- Système de build: Passer de BuildConfig.groovy à build.gradle
- Configuration: Migrer de Config.groovy vers application.yml/application.groovy
- Structure du projet: Adapter à la nouvelle structure basée sur Spring Boot
- Plugins: Vérifier la compatibilité et mettre à jour les dépendances
// Exemple de build.gradle pour Grails 3
apply plugin: "org.grails.grails-web"
dependencies {
compile "org.springframework.boot:spring-boot-starter-logging"
compile "org.springframework.boot:spring-boot-starter-actuator"
compile "org.springframework.boot:spring-boot-autoconfigure"
compile "org.grails:grails-core"
// ...
}De Grails 3.x à Grails 4.x
- Java 8: S'assurer que le projet utilise Java 8 minimum
- Spring Boot 2.1: Adapter les configurations
- Groovy 2.5: Vérifier la compatibilité du code
- Micronaut: Exploiter les nouvelles fonctionnalités d'intégration
De Grails 4.x à Grails 5.x
- Groovy 3: Adapter le code pour profiter des nouvelles fonctionnalités
- Spring Boot 2.5: Mettre à jour les configurations
- Autowiring par type: Remplacer l'autowiring par nom
- Gradle 7: Mettre à jour les scripts de build
De Grails 5.x à Grails 6.x
- Java 11: Mettre à niveau l'environnement de développement et de production
- Spring Boot 2.7: Adapter les configurations
- Nouvelle CLI: Adapter les scripts et workflow de développement
- Spring Security: Mettre à jour vers Spring Security Core 6 si utilisé
Bonnes pratiques et astuces
Patterns recommandés
Services transactionnels: Isoler la logique métier dans des services
@Transactional class BookService { // Logique métier ici }Contrôleurs légers: Déléguer la logique aux services
class BookController { BookService bookService def save() { // Délégation au service respond bookService.save(params) } }Tests unitaires: Tester chaque composant individuellement
@TestFor(BookService) class BookServiceSpec extends Specification { void "test saving a book"() { // Tests ici } }
Optimisation des requêtes GORM
Éviter les requêtes N+1:
// Mauvaise pratique def books = Book.list() books.each { book -> println book.author.name // Requête supplémentaire pour chaque livre } // Bonne pratique def books = Book.findAll { author != null } books.each { book -> println book.author.name // Pas de requête supplémentaire }Utiliser les requêtes par lot:
// Traitement par lots de 100 entités Book.withSession { session -> session.withBatch(100) { batch -> // Opérations par lot } }Requêtes projections:
// Au lieu de charger toutes les propriétés def titles = Book.createCriteria().list { projections { property('title') } }
Ressources complémentaires
- Grails Guides - Tutoriels pas à pas
- Communauté Grails - Forums et support
- Blog Grails - Articles et actualités
- GitHub Grails - Code source et contributions
- Grails Testing - Documentation sur les tests
Conclusion
Grails a considérablement évolué depuis sa création, passant d'un framework MVC simple à un écosystème complet intégrant Spring Boot et Micronaut. La philosophie originale de "convention plutôt que configuration" et DRY reste au cœur du framework, tout en s'adaptant aux tendances modernes comme les microservices, la conteneurisation et le cloud.
Les récentes versions (3.x à 6.x) ont apporté une modernisation importante, tant au niveau de l'architecture que des outils de développement, permettant à Grails de rester pertinent dans l'écosystème Java/Groovy contemporain.