[JAVA] Grails pour commencer

Qu'est-ce que Grails

Grails est un framework complet développé pour résoudre de nombreux défis du développement Web grâce à la technologie de base et aux plugins associés.

Alors que de nombreux frameworks Web dans le monde Java sont complexes et n'embrassent pas les principes DRY, Grails s'appuie sur des concepts de framework dynamiques qui ont conduit à une réflexion moderne sur les applications Web telles que Rails et Django. , Repose sur les technologies Java existantes telles que Spring et Hibernate.

Installer Grails

Référence http://docs.grails.org/latest/guide/single.html#requirements

Dans l'article, j'ai utilisé la version suivante.

* Comment installer Grails pour les utilisateurs de nix (Mac OS, Linux, BSD OS)

Comment installer Grails pour les utilisateurs Windows

Créer une application Grails

Lors de la création d'une application Grails, utilisez la commande grails create-app pour créer les fichiers requis en bloc.

$ cd ${Répertoire de destination d'enregistrement de l'application}
$ grails create-app grails-sample-app
# grails-sample-Remplacez l'application par le nom de l'application le cas échéant.
# "grails-sample-app"Un répertoire est créé
#Le nom du package de l'application est"grails.sample.app"Sera.

Grails Vous pouvez également utiliser un mode qui vous permet d'exécuter chaque commande de manière interactive. Le mode interactif est disponible lorsque vous exécutez la commande grails sans options, et vous pouvez terminer la commande avec la touche Tab en mode interactif.

Exécutez l'application Grails

Exécutez la commande run-app pour exécuter l'application.

Exécutez l'application Grails(Lorsque vous n'utilisez pas le mode interactif Grails)


$ cd ${create-répertoire de destination d'enregistrement de l'application}
$ grails run-app

Exécutez l'application Grails(Lors de l'utilisation du mode interactif Grails)


$ cd ${create-répertoire de destination d'enregistrement de l'application}
$ grails #Lancez Grails en mode interactif
grails> run-app

Écran d'application

Après avoir exécuté l'application avec run-app, accédez à http: // localhost: 8080 / et vous verrez l'écran d'introduction Grails comme indiqué ci-dessous. (L'écran affiche le HelloController créé en plus du fichier créé par create-app)

image.png

Structure du répertoire

.gradle/
.idea/              ...Fichier de configuration pour IntelliJ IDE
build/              ...Créer un fichier(create-N'existe pas immédiatement après l'exécution de l'application)
gradle/
grails-app/         ...Code source de l'application
  assets/           ...Stockage des ressources d'actifs(Fichiers traités par le pipeline d'actifs)
  conf/             ...Paramètres d'exécution
  controllers/      ...manette(MVC modèle C)
    ${nom de l'application}/       ...nom de l'application(Si des tirets sont inclus, le répertoire est séparé pour chaque tiret)
      UrlMappings.groovy ...Mappage d'URL
  domain/           ...Classe de domaine(MVC modèle M)
  i18n/             ... internationalization(i18n)Prise en charge de la configuration
  init/             ...Traitement au démarrage de l'application
  services/         ...Couche de service
  taglib/           ...Bibliothèque de balises(Balises personnalisées définies pouvant être utilisées dans View)
  utils/            ...Utilitaire spécifique aux Grails
  views/            ... Groovy Server Page(GSP) or JSON Views (MVC modèle V)
src/
  integration-test/ ...Test d'intégration
  main/             ...Fichier statique qui ne passe pas par le traitement du pipeline d'actifs
    groovy/         ...Classe de domaine que vous ne souhaitez pas associer à la table DB
    webapp/         ...Fichier statique(Inclus dans WAR, pas dans JAR)
    resources/public ...Fichier statique(/static/x/y/Accès avec z)
  test/             ...Test de l'unité
    groovy/
      ${nom de l'application}/
        ${***Spec}.groovy
.gitignore          ...Paramètres de fichier non géré VCS pour Git
build.gradle        ...Paramètres de construction
gradle.properties   ...Paramètres Gradle
gradlew             ...Script de lancement Gradle(UN*Pour X)
gradlew.bat         ...Script de lancement Gradle(Pour les fenêtres)
grails-wrapper.jar
grailsw             ...Script de lancement Grails(UN*Pour X)
grailsw.bat         ...Script de lancement Grails(Pour les fenêtres)
README.md           ... README

Des informations détaillées

Afficher Hello World

Affiche «Hello World!» Sous forme de chaîne de caractères.

Dans Grails, lorsque vous créez un contrôleur et décrivez une action dans cette classe, elle est mappée à une URL unique et est accessible par un navigateur.

La règle de mappage sera / <appname> / <controller> / <action>, et <controller> sera la classe du contrôleur moins Class.

Par exemple, pour créer un HelloController et que l'action ʻindex` affiche" HelloWorld ", procédez comme suit:

Exécutez l'application Grails


grails> create-controller hello #La classe HelloController est créée

Modifiez la classe HelloController créée dans grails-app / controllers / grails / sample / app / HelloController.groovy avec le contenu suivant.

grails-app/controllers/grails/sample/app/HelloController.groovy


package grails.sample.app

class HelloController {
  def index() {
    render "Hello World!"
  }
}

Lorsque vous exécutez l'application et accédez à http: // localhost: 8080 / hello / index ou http: // localhost: 8080 / hello /, la chaîne "Hello World!" S'affiche. (L'action d'indexation peut être omise sur l'URL)

Créez une fonctionnalité CRUD pour votre domaine

Le domaine est M de MVC dans Grails. Vous pouvez créer des contrôleurs et des vues individuels, comme dans l'exemple précédent d'affichage HelloWorld, mais vous pouvez utiliser l'option generate-all pour créer des contrôleurs et des vues avec la fonctionnalité CRUD pour votre domaine (plus pour les tests). Vous pouvez créer une classe).

Créer une classe de domaine d'employé, un contrôleur pour CRUD, une vue et un test


#Créer une classe de domaine Employee
$ grails create-domain-class employee
| Created grails-app/domain/grails/sample/app/Employee.groovy
| Created src/test/groovy/grails/sample/app/EmployeeSpec.groovy
#Créer des classes de contrôleur, de vue et de test dans les classes de domaine des employés CRUD
$ grails generate-all grails.sample.app.Employee
| Rendered template Controller.groovy to destination grails-app\controllers\grails\sample\app\EmployeeController.groovy
| Rendered template Service.groovy to destination grails-app\services\grails\sample\app\EmployeeService.groovy
| Rendered template Spec.groovy to destination src\test\groovy\grails\sample\app\EmployeeControllerSpec.groovy
| Rendered template ServiceSpec.groovy to destination src\integration-test\groovy\grails\sample\app\EmployeeServiceSpec.groovy
| Scaffolding completed for grails-app\domain\grails\sample\app\Employee.groovy
| Rendered template create.gsp to destination grails-app\views\employee\create.gsp
| Rendered template edit.gsp to destination grails-app\views\employee\edit.gsp
| Rendered template index.gsp to destination grails-app\views\employee\index.gsp
| Rendered template show.gsp to destination grails-app\views\employee\show.gsp
| Views generated for grails-app\domain\grails\sample\app\Employee.groovy

Vous avez maintenant créé la possibilité de CRUD la classe de domaine Employé.

La classe de domaine n'a pas encore d'attributs, alors éditez le fichier grails-app / domain / grails / sample / app / Employee.groovy et ajoutez les attributs appropriés.

grails-app/domain/grails/sample/app/Employee.groovy


package grails.sample.app

class Employee {

  String name

  static constraints = {
  }
}

Lorsque j'exécute à nouveau run-app pour afficher l'écran TOP de l'application dans le navigateur, grails.sample.app.EmployeeController est ajouté aux contrôleurs disponibles:.

La chaîne de caractères grails.sample.app.EmployeeController est un lien, et en cliquant dessus, vous serez redirigé vers l'écran de la liste des employés (http: // localhost: 8080 / employee / index).

Écran de la liste des employés
image.png

Comme vous pouvez le voir avec certaines opérations, les itinéraires suivants ont été ajoutés.

racine une fonction
GET /employee/index Afficher l'écran de la liste des employés
GET /employee/create Afficher l'écran de création d'un nouvel employé
GET /employee/show/:id Écran des détails de l'employé(:id est l'ID de la classe de domaine)Spectacle
GET /employee/edit/:id Écran d'édition des employés(:id est l'ID de la classe de domaine)Spectacle
POST /employee/save Nouvelle création d'employé
PUT /employee/update/:id Mise à jour des employés(:id est l'ID de la classe de domaine)
DELETE /employee/delete/:id Employé supprimé(:id est l'ID de la classe de domaine)

La base de données utilisée par l'application peut être modifiée pour chaque variable d'environnement telle que le développement, le test et la production. Grails est configuré pour utiliser H2 Database par défaut dans tous les environnements.

Vous remarquerez également que la base de données est initialisée lorsque vous avez terminé l'opération CRUD, quittez run-app et réexécutez l'application. Cela est dû au fait que H2 est une base de données en mémoire et que la base de données dans l'environnement de développement est définie pour DROP la base de données à la fin de l'application et la crée au démarrage.

Ces paramètres sont définis dans grails-app / conf / application.yml. (Voir The Grails Framework> The DataSource pour plus de détails sur les paramètres.)

Cette fois, nous nous mettrons à ne pas détruire la base de données créée dans l'environnement de développement. Réécrivez grails-app / conf / application.yml avec le contenu suivant.

grails-app/conf/application.yml


environments:
  development:
    dataSource:
      # create-Passer d'une goutte à l'autre
      dbCreate: update
      # mem:de devDb./Changer en devDb
      url: jdbc:h2:./devDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE

Les valeurs qui peuvent être définies avec dbCreate sont les suivantes.

Paramètres dbCreate La description
create Schéma existant au démarrage(table,indice,autre)Déposer et recréer
create-drop Identique à créer, mais supprimer la table en quittant l'application
update Créez ou mettez à jour une table ou un index qui n'existe pas. Ne supprimez pas les tables ou les données existantes.(Notez que les anciennes colonnes et données resteront car les modifications de nom de colonne ne peuvent pas être gérées correctement.)
validate Générer un avertissement en comparant le schéma et les paramètres existants sans apporter de modifications à la base de données
any other value ne fais rien

Manipuler les classes de domaine dans le code source

Créer un domaine

def p = new Person(name: "Fred", age: 40, lastVisit: new Date())
p.save()

Lire le domaine

def p = Person.get(1)
println('name: ' + p.name)

Mettre à jour le domaine

def p = Person.get(1)
p.name = 'Bob'
p.save()

Supprimer le domaine

def p = Person.get(1)
p.delete()

Créer une application qui gère le modèle Employee

Étendons un peu plus la classe de domaine Employee et rendons-la plus pratique.

grails-app/domain/grails/sample/app/Employee.groovy


package grails.sample.app

class Employee {

  String name
  String department
  String gender
  Date birth
  Date joinedDate
  Long payment
  String note

  static constraints = {
    name blank: false, unique: true
    department blank: false
    gender blank: false, inList: ['male', 'female', 'other']
    birth blank: false
    joinedDate blank: false
    payment min: new Long(0), blank: false
    note blank: true
  }

  /**
  *La durée du service
  */
  public int serviceYears() {
    def today = new Date()
    return Math.max(0, today.year - this.joinedDate.year)
  }
}

Pour le moment, sauf pour la note, j'ai défini nullable: false (puisque c'est par défaut, il n'est pas spécifié dans les contraintes) et blank: false. Si vous définissez blank: true comme indiqué dans la note, vous devez désactiver le processus de conversion en null si les données sont vides.

Le paramètre peut être désactivé en définissant ʻapplication.groovycomme suit. Si le fichier n'existe pas, créez-le sousgrails-app / conf /`.

grails-app/conf/application.groovy


// the default value for this property is true
grails.databinding.convertEmptyStringsToNull = false

Maintenant, changez l'écran de liste et l'écran d'édition pour afficher l'employé comme suit.

Affichage de l'écran de liste(grails-app/views/employee/index.gsp)


<!DOCTYPE html>
<html>
  <head>
    <meta name="layout" content="main" />
    <g:set var="entityName" value="${message(code: 'employee.label', default: 'Employee')}" />
    <title><g:message code="default.list.label" args="[entityName]" /></title>
    <script type="text/javascript">
      /*Définir l'état du bouton d'édition en fonction de l'état du bouton radio*/
      function setEditButtonStatusByRadioButton() {
        var edit_button_id = "edit_button";
        var radios = document.getElementsByName('id');
        var checkedNum = 0;
        radios.forEach(e => e.checked && checkedNum++);
        if (checkedNum > 0) {
          document.getElementById(edit_button_id).disabled = false;
        } else {
          document.getElementById(edit_button_id).disabled = true;
        }
      }
    </script>
  </head>
  <body>
    <a href="#list-employee" class="skip" tabindex="-1"><g:message code="default.link.skip.label" default="Skip to content&hellip;"/></a>
    <div class="nav" role="navigation">
      <ul>
        <li><a class="home" href="${createLink(uri: '/')}"><g:message code="default.home.label"/></a></li>
        <li><g:link class="create" action="create"><g:message code="default.new.label" args="[entityName]" /></g:link></li>
      </ul>
    </div>
    <div id="list-employee" class="content scaffold-list" role="main">
      <h1><g:message code="default.list.label" args="[entityName]" /></h1>
      <g:if test="${flash.message}">
        <div class="message" role="status">${flash.message}</div>
      </g:if>

      <g:form action="edit">
        <table class="table table-striped">
          <thead>
            <tr>
              <th>Edit</th>
              <g:each in="${['id', 'name', 'department', 'gender']}" var="p">
                <g:sortableColumn property="${p}" title="${p}" />
              </g:each>
            </tr>
          </thead>
          <tbody>
          <g:each in="${employeeList}" var="employee">
            <tr>
              <td><input name="id" type="radio" value="${employee.id}" onclick="setEditButtonStatusByRadioButton()" /></td>
              <g:each in="${['id', 'name', 'department', 'gender']}" var="p">
                <g:if test="${p=='id'}">
                  <td><g:link method="GET" resource="${employee}">${employee.properties[p]}</g:link></td>
                </g:if>
                <g:else>
                  <td>${employee.properties[p]}</td>
                </g:else>
              </g:each>
            </tr>
          </g:each>
          </tbody>
        </table>

        <button disabled="false" id="edit_button"><g:message code="default.edit.label" args="[entityName]" /></button>
      </g:form>

      <div class="pagination">
        <g:paginate total="${employeeCount ?: 0}" />
      </div>
    </div>
  </body>
</html>

Vue d'écran détaillée(grails-app/views/employee/show.gsp)


<!DOCTYPE html>
<html>
  <head>
    <meta name="layout" content="main" />
    <g:set var="entityName" value="${message(code: 'employee.label', default: 'Employee')}" />
    <title><g:message code="default.show.label" args="[entityName]" /></title>
  </head>
  <body>
    <a href="#show-employee" class="skip" tabindex="-1"><g:message code="default.link.skip.label" default="Skip to content&hellip;"/></a>
    <div class="nav" role="navigation">
      <ul>
        <li><a class="home" href="${createLink(uri: '/')}"><g:message code="default.home.label"/></a></li>
        <li><g:link class="list" action="index"><g:message code="default.list.label" args="[entityName]" /></g:link></li>
        <li><g:link class="create" action="create"><g:message code="default.new.label" args="[entityName]" /></g:link></li>
      </ul>
    </div>
    <div id="show-employee" class="content scaffold-show" role="main">
      <h1><g:message code="default.show.label" args="[entityName]" /></h1>
      <g:if test="${flash.message}">
      <div class="message" role="status">${flash.message}</div>
      </g:if>

      <ol class="property-list employee">
        <li class="fieldcontain">
          <g:each in="${['id', 'name', 'department', 'gender', 'birth', 'serviceYears', 'payment', 'note']}" var="p">
            <span id="name-label" class="property-label">${p}</span>
            <g:if test="${p=='serviceYears'}">
              <div class="property-value" aria-labelledby="name-label">${employee.serviceYears()}</div>
            </g:if>
            <g:else>
              <div class="property-value" aria-labelledby="name-label">${employee.properties[p]}</div>
            </g:else>
          </g:each>
        </li>
      </ol>
      
      <g:form resource="${this.employee}" method="DELETE">
        <fieldset class="buttons">
          <g:link class="edit" action="edit" resource="${this.employee}"><g:message code="default.button.edit.label" default="Edit" /></g:link>
          <input class="delete" type="submit" value="${message(code: 'default.button.delete.label', default: 'Delete')}" onclick="return confirm('${message(code: 'default.button.delete.confirm.message', default: 'Are you sure?')}');" />
        </fieldset>
      </g:form>
    </div>
  </body>
</html>

C'est un peu plus pratique.

La vue est décrite dans Groovy Server Pages (GSP), et «<g: link>», «<g: form>», etc. sont des balises GSP. (Voir Groovy Server Pages (GSP) pour plus d'informations)

Présentez Twitter Bootstrap

Grails 3.3.5 a introduit Bootstrap 3.3.6 par défaut.

Il semble y avoir twitter-bootstrap-grails-plugin du plugin Grails, mais la version actuelle est 3.3.5 le 18 septembre '18. Il semble que le commit final date d'il y a deux ans, il ne semble donc pas bien entretenu, j'ai donc décidé d'installer Bootstrap v4 manuellement.

Procédure d'installation de Twitter Bootstrap v4

  1. Supprimez le Bootstrap installé (js, css), JQuery
    • grails-app/assets/javascripts/bootstrap.js
    • grails-app/assets/stylesheets/bootstrap.css
    • grails-app/assets/javascripts/jquery-2.2.0.min.js
  2. Téléchargez Bootstrap v4
    • https://getbootstrap.com/docs/4.1/getting-started/download/ -Appuyez sur le bouton Télécharger de "Compiled CSS and JS"
  3. Téléchargez JQuery v3
    • https://jquery.com/download/ -Enregistrer le fichier lié de "Télécharger le jQuery 3.x.y de production compressé"
  4. Copiez Bootstrap v4, JQuery v3 dans le dossier de l'application Grails
    • grails-app/assets/javascripts/bootstrap.bundle.js
    • grails-app/assets/javascripts/jquery-3.3.1.min.js
    • grails-app/assets/stylesheets/bootstrap.css --grails-app / assets / stylesheets / bootstrap.css.map (Ajouter comme vous le souhaitez tel quel pour le débogage CSS)
  5. Modifiez grails-app / assets / javascripts / application.js --Change de // = require jquery-2.2.0.min à // = require jquery-3.3.1.min --Changer de // = require bootstrap à // = require bootstrap.bundle

Réexécutez l'application et vous avez terminé. (La barre de navigation s'effondre, mais je ne l'utilise pas, alors ignorez-la)

Confirmation après introduction

Modifions la vue index.gsp décrite précédemment.

grails-app/views/layouts/main.gsp


<head>
  : <snip>
  <asset:stylesheet src="application.css"/>
  <asset:javascript src="application.js"/> <!--Déplacez la ligne écrite dans le corps dans la tête-->
  : <snip>
</head>
  : <snip>

grails-app/views/employee/index.gsp


  : <snip>
<button class="btn btn-secondary" disabled="false" id="edit_button"><g:message code="default.edit.label" args="[entityName]" /></button>
  : <snip>

Ce n'est pas grave si le bouton a le style Bootstrap 4 appliqué.

Ajouter une fonction de filtre à l'écran de liste à l'aide de DataTable

Préparation préalable

Ajoutons une fonction de filtre à l'écran de la liste des employés pour le rendre un peu plus pratique.

J'utiliserai DataTables pour ajouter la fonction de recherche au tableau affiché sur l'écran de liste cette fois.

Il y avait grails-datatables comme plugin Grails, mais installez la dernière version de DataTables comme bootstrap et jquery. Je déciderai.

Sélectionnez les éléments suivants sur la Page de téléchargement de DataTables pour télécharger le fichier.

Copiez les fichiers suivants du fichier téléchargé dans grails-app / assets / (javascripts | stylesheets) / en fonction du type.

Enfin, éditez grails-app / assets / application (Js | css). Les fichiers de destination requis décrits dans l'application. (Js | css) sont lus dans l'ordre à partir du haut, faites donc attention à l'ordre de description des packages avec des dépendances.

grails-app/assets/javascripts/application.js


  : <snip>
//= require jquery-3.3.1.min
//= require bootstrap.bundle
//= require jquery.dataTables
//= require dataTables.bootstrap4
//= require_tree .
//= require_self
  : <snip>

grails-app/assets/stylesheets/application.css


/*
  : <snip>
*= require bootstrap
*= require dataTables.bootstrap4
*= require grails
*= require main
*= require mobile
*= require_self
  : <snip>
*/

Vous êtes maintenant prêt à utiliser DataTables.

Appliquer les DataTables aux tables sur l'écran de liste

Appliquez DataTables aux tables sur l'écran de liste.

Pour appliquer DataTables, appliquez la méthode dataTable () au DOM qui pointe vers la table que vous souhaitez appliquer dans jQuery.

grails-app/views/employee/index.gsp


<!DOCTYPE html>
<html>
  <head>
    : <snip>
    <script type="text/javascript">
      /*Définir l'état du bouton d'édition en fonction de l'état du bouton radio*/
      function setEditButtonStatusByRadioButton() {
        var edit_button_id = "edit_button";
        var radios = document.getElementsByName('id');
        var checkedNum = 0;
        radios.forEach(e => e.checked && checkedNum++);
        if (checkedNum > 0) {
          document.getElementById(edit_button_id).disabled = false;
        } else {
          document.getElementById(edit_button_id).disabled = true;
        }
      }

      $(document).ready(function() {
        $('#employeeindex').dataTable();
      } );
    </script>
  </head>
  <body>
    : <snip>
        <table id="employeeindex" class="display table table-striped">
          : <snip>
        </table>
    : <snip>
  </body>
</html>

Lorsque vous exécutez l'application, vous pouvez voir que DataTables a été appliqué.

image.png

Ici, vous pouvez voir que les fonctionnalités de pagination de DataTables et Grails sont devenues redondantes.

La fonctionnalité à activer dépend de la conception de l'application, mais cette fois, nous laisserons la fonction de pagination de Grails pour réduire la quantité de traitement des données.

Pour désactiver la fonction de pagination de DataTables, ajoutez le paramètre paging: true à la méthode dataTable () comme décrit dans la page officielle (https://datatables.net/reference/option/paging). Je vais le passer.

#Appliquer DataTables au DOM Employeeindex sans pagination


$(document).ready(function() {
  $('#employeeindex').dataTable({
    "paging": false
  });
} );

Enfin, modifiez la barre de navigation en haut de l'écran. De plus, modifiez le style en conséquence et ajoutez Font Awesome dans le CDN pour utiliser ICON.

grails-app/assets/stylesheets/main.css


/* NAVIGATION MENU */

.nav, nav {
    zoom: 1;
}

.nav ul {
    overflow: hidden;
    padding-left: 0;
    zoom: 1;
}

.nav li {
    display: block;
    float: left;
    list-style-type: none;
    margin-right: 0.5em;
    padding: 0;
}

.nav a, nav a {
    color: #666666;
    display: block;
    padding: 0.25em 0.7em;
    text-decoration: none;
    -moz-border-radius: 0.3em;
    -webkit-border-radius: 0.3em;
    border-radius: 0.3em;
}

.nav a:active, .nav a:visited, nav a:active, nav a:visited {
    color: #666666;
}

.nav a:focus, .nav a:hover, nav a:focus, nav a:hover {
    background-color: #999999;
    color: #ffffff;
    outline: none;
    text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.8);
}

.no-borderradius .nav a:focus, .no-borderradius nav a:focus, .no-borderradius .nav a:hover, .no-borderradius nav a:hover {
    background-color: transparent;
    color: #444444;
    text-decoration: underline;
}

.nav a.home, .nav a.list, .nav a.create, nav a.home, nav a.list, nav a.create {
    background-position: 0.7em center;
    background-repeat: no-repeat;
    text-indent: 25px;
}

.nav a.home, nav a.home {
    background-image: url(../images/skin/house.png);
}

.nav a.list, nav a.list {
    background-image: url(../images/skin/database_table.png);
}

.nav a.create, nav a.create {
    background-image: url(../images/skin/database_add.png);
}

.nav li.dropdown ul.dropdown-menu, nav li.dropdown ul.dropdown-menu {
    background-color: #424649;
}

grails-app/assets/stylesheets/mobile.css


@media screen and (max-width: 480px) {
    .nav, nav {
        padding: 0.5em;
    }

    .nav li, nav li {
        margin: 0 0.5em 0 0;
        padding: 0.25em;
    }
  : <snip>
}

grails-app/views/layouts/main.gsp


<!doctype html>
<html lang="en" class="no-js">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
  <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
  <title>
    <g:layoutTitle default="Grails"/>
  </title>
  <meta name="viewport" content="width=device-width, initial-scale=1"/>
  <asset:link rel="icon" href="favicon.ico" type="image/x-ico" />

  <asset:stylesheet src="application.css"/>
  <asset:javascript src="application.js"/>
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU" crossorigin="anonymous">

  <g:layoutHead/>
</head>
<body>

  <nav class="navbar-expand-lg pr-3 navbar navbar-default">
    <a class="navbar-brand" href="/#">
      <asset:image src="grails.svg" alt="Grails Logo"/>
    </a>
    <button class="navbar-toggler mx-2" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <span class="fas fa-bars" style="font-size: 3rem;"></span>
    </button>
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav ml-auto">
        <g:pageProperty name="page.nav" />
      </ul>
    </div>
  </nav>

  <g:layoutBody/>

  <div class="footer" role="contentinfo"></div>

  <div id="spinner" class="spinner" style="display:none;">
      <g:message code="spinner.alt" default="Loading&hellip;"/>
  </div>

</body>
</html>
Large Display Size Small Display Size
image.png image.png

Migration de base de données

Jusqu'à présent, la base de données était mise à jour en définissant dbCreate: update en référence au fichier modèle au démarrage de l'application, mais en raison des conditions limitées qui peuvent être modifiées et que les anciennes colonnes restent, plusieurs personnes Lors du développement d'une application, différentes personnes peuvent avoir des structures de base de données différentes.

Par conséquent, j'ai décidé de créer un fichier de migration et de créer une base de données à partir de celui-ci.

Utilisez le plug-in database-migration pour effectuer la migration de la base de données dans Grails. (Le plugin database-migratation utilise la bibliothèque Liquibase.)

La migration est gérée à l'aide d'un ou plusieurs fichiers journal des modifications écrits en Groovy DSL ou en XML Liquibase natif.

Les fichiers journaux des modifications ont un ID unique au niveau mondial. (L'ID contient le nom d'utilisateur de l'utilisateur qui a créé le journal des modifications)

Préparation préalable

Modifiez bundle.gradle comme suit pour utiliser le plug-in database-migration. (Modifiez la version de la fiche selon le cas)

Notez que bundle.gradle est un fichier de configuration pour Gradle, et Gradle est un système d'automatisation de construction open source basé sur les concepts d'Apache Ant et d'Apache Maven. (Référence: Wikipedia> Gradle)

buildscript {
  repositories {
    : <snip>
  }
  dependencies {
    : <snip>
    classpath 'org.grails.plugins:database-migration:3.0.4' # database-Ajout du plug-in de migration
  }
}

dependencies {
    : <snip>
  compile 'org.grails.plugins:database-migration:3.0.4'
  compile 'org.liquibase:liquibase-core:3.5.5'
    : <snip>
}

  : <snip>

sourceSets {
  main {
    resources {
      srcDir 'grails-app/migrations'
    }
  }
}

Définissez ensuite la base de données «dbCreate» sur «none». Sinon, vous n'obtiendrez pas la différence lors de la création d'un fichier de migration avec l'option dbm-gorm-diff.

grails-app/conf/application.yml


environments:
  development:
    dataSource:
      dbCreate: none
      url: jdbc:h2:./devDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
  : <snip>

Créer un fichier journal des modifications

Ensuite, créez un fichier journal des modifications.

Vous pouvez choisir le format de description, mais j'utiliserai Groovy DSL.

Il existe deux façons de créer un journal des modifications, l'une provient de la base de données et l'autre de la classe de domaine. À l'heure actuelle, je pense que la base de données est automatiquement créée à partir de la classe de domaine au démarrage, alors sélectionnez la méthode pour créer le journal des modifications à partir de la base de données.

Comment créer un journal des modifications à partir d'une base de données au format Groovy DSL


$ grails dbm-generate-changelog changelog.groovy
  : <snip>Les plugins seront téléchargés au besoin
:compileJava NO-SOURCE
:compileGroovy UP-TO-DATE
:buildProperties UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:findMainClass
:dbmGenerateChangelog

BUILD SUCCESSFUL

Total time: 16.478 secs

Ensuite, le journal des modifications suivant sera créé.

Journal des modifications(grails-app/migrations/changelog.groovy)


databaseChangeLog = {

  changeSet(author: "tatsurou (generated)", id: "1537102923747-1") {
    createTable(tableName: "EMPLOYEE") {
      column(autoIncrement: "true", name: "ID", type: "BIGINT(19)") {
        constraints(primaryKey: "true", primaryKeyName: "CONSTRAINT_7")
      }

      column(name: "VERSION", type: "BIGINT(19)") {
        constraints(nullable: "false")
      }

      column(name: "NAME", type: "VARCHAR(255)") {
        constraints(nullable: "false")
      }
    }
  }
}

Vous pouvez maintenant créer la base de données en fonction du journal des modifications. (Seul le schéma de base de données est construit, aucune donnée n'est migrée)

À partir de maintenant, après avoir modifié la classe de domaine, vous créerez un journal des modifications manuellement ou automatiquement et exécuterez dbm-update.

  1. Modifiez la classe de domaine
  2. Modifiez le journal des modifications --Comment créer la différence automatiquement (Si vous ajoutez le nom de fichier et l'option --add, le journal des modifications sera enregistré séparément avec le nom de fichier spécifié, et il sera ajouté afin qu'il soit lu par include de changelog.groovy) $ grails dbm-gorm-diff add_birth_column_to_employee.groovy --add
  3. Sauvegardez la base de données (en cas de problème)
  4. Exécutez grails dbm-update pour n'importe quel environnement (développement, test, production)

Console Groovy

La console Groovy est une application qui vous permet d'entrer et d'exécuter du code source Groovy. Vous pouvez également charger une classe de domaine implémentée dans une application Grails ou utiliser l'ORM de cette classe de domaine pour manipuler la base de données.

Vous pouvez démarrer la console Grails en exécutant la commande grails console.

Lancez la console groovy


$ cd ${répertoire des applications}
$ grails console

image.png

package grails.sample.app

def id = 1
def e = Employee.get(id)
if (e == null) {
  println("Not found employee id " + id)
  exit()
}
println("employee: " + e)
println("name: " + e.name)
e.name = 'test99'
e.save(flush: true)

La gestion des erreurs

Décrit les erreurs qui se produisent lors de l'exécution de la commande Grails et comment les traiter.

Could not acquire change log lock

Command execution error: Could not acquire change log lock.  Currently locked by ${COMPUTER_NAME} (${IP_ADDRESS}) since 18/09/17 0:09

--Supprimer grails dbm-release-locks ou DB

Recommended Posts

Grails pour commencer
Premiers pas avec Android!
1.1 Premiers pas avec Python
Premiers pas avec apache2
Premiers pas avec Python
Premiers pas avec Django 1
Introduction à l'optimisation
Premiers pas avec Spark
Premiers pas avec Python
Premiers pas avec Pydantic
Premiers pas avec Jython
Premiers pas avec Django 2
Traduire Premiers pas avec TensorFlow
Introduction aux fonctions Python
Introduction à Tkinter 2: Button
[Linux] [Configuration initiale] Introduction
Premiers pas avec PKI avec Golang ―― 4
Django Getting Started: 2_ Créer un projet
Django Premiers pas: 1_Construction de l'environnement
Premiers pas avec Python Django (1)
Django Getting Started: intégration 4_MySQL
Premiers pas avec Python Django (3)
Introduction à Python Django (6)
Premiers pas avec Django avec PyCharm
Premiers pas avec Python responder v2
Introduction à Git (1) Stockage d'historique
Premiers pas avec Sphinx. Générer docstring avec Sphinx
Premiers pas avec les applications Web Python
Premiers pas avec Python pour les classes PHPer
Premiers pas avec Sparse Matrix avec scipy.sparse
Premiers pas avec Julia pour Pythonista
Premiers pas avec Python Bases de Python
Premiers pas avec Cisco Spark REST-API
Premiers pas avec les algorithmes génétiques Python
Premiers pas avec Python 3.8 sous Windows
Premiers pas avec Python pour les fonctions PHPer
Premiers pas avec CPU Steal Time
Premiers pas avec python3 # 1 Apprenez les connaissances de base
Premiers pas avec Python Web Scraping Practice
Premiers pas avec Python pour PHPer-Super Basics
Premiers pas avec Python Web Scraping Practice
Premiers pas avec Dynamo de Python boto
a commencé python
Mise en route: 30 secondes de traduction en japonais Keras
Premiers pas avec Lisp pour Pythonista: Supplément
Premiers pas avec Heroku, déploiement de l'application Flask
Premiers pas avec TDD avec Cyber-dojo chez MobPro
Démarrer avec Python avec 100 coups sur le traitement du langage
Principes de base de MongoDB: Premiers pas avec CRUD avec JAVA
Premiers pas avec le dessin avec matplotlib: écrire des fonctions simples
Premiers pas avec la traduction japonaise du modèle séquentiel Keras
[Français] Premiers pas avec Rust pour les programmeurs Python
Django Getting Started Part 2 avec eclipse Plugin (PyDev)
Premiers pas avec AWS IoT facilement en Python
Premiers pas avec le module ast de Python (à l'aide de NodeVisitor)