Grails z MySQL

Z Jacek Laskowski - Wiki Projektanta Java EE

Spis treści

Wprowadzenie

Domyślna konfiguracja Grails obejmuje uruchomienie aplikacji z wbudowaną bazą danych HSQL. Takie podejście pozwala na natychmiastowe budowanie aplikacji webowych bez konieczności zestawiania środowiska uruchomieniowego z bazą danych. Po prostu ją mamy. W tym artykule przedstawię, co jest potrzebne do uruchomienia aplikacji grailsowej z MySQL.

Instalacja i zarządzanie serwerem MySQL

Rozpoczynamy od pobrania MySQL z jego strony domowej mysql.org wraz ze sterownikiem JDBC - Connector/J. Instalacja sprowadza się do wykonania serii kroków z pomocą asystenta instalacji (baza danych) bądź rozpakowania do wybranego katalogu (baza danych i sterownik JDBC).

Instalacja na Linux/MacOS X/Unix

Po rozpakowaniu paczki instalacyjnej, np. mysql-5.5.16-osx10.6-x86_64.tar.gz, wykoujemy serię poleceń, które tworzą przykładową bazę i zakładają użytkownika administracyjnego root.

$ ./bin/mysqladmin version
./bin/mysqladmin  Ver 8.42 Distrib 5.5.16, for osx10.6 on i386
Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
 
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
 
Server version		5.5.16
Protocol version	10
Connection		Localhost via UNIX socket
UNIX socket		/tmp/mysql.sock
Uptime:			5 min 9 sec
 
Threads: 1  Questions: 5  Slow queries: 0  Opens: 33  Flush tables: 1  Open tables: 26  Queries per second avg: 0.016
 
jacek:~/apps/mysql
$ scripts/mysql_install_db --user=jacek
Installing MySQL system tables...
OK
Filling help tables...
OK
 
To start mysqld at boot time you have to copy
support-files/mysql.server to the right place for your system
 
PLEASE REMEMBER TO SET A PASSWORD FOR THE MySQL root USER !
To do so, start the server, then issue the following commands:
 
./bin/mysqladmin -u root password 'new-password'
./bin/mysqladmin -u root -h devmac.local password 'new-password'
 
Alternatively you can run:
./bin/mysql_secure_installation
 
which will also give you the option of removing the test
databases and anonymous user created by default.  This is
strongly recommended for production servers.
 
See the manual for more instructions.
 
You can start the MySQL daemon with:
cd . ; ./bin/mysqld_safe &
 
You can test the MySQL daemon with mysql-test-run.pl
cd ./mysql-test ; perl mysql-test-run.pl
 
Please report any problems with the ./bin/mysqlbug script!
 
$ ./bin/mysqld_safe &
111014 20:53:02 mysqld_safe Logging to '/Users/jacek/apps/mysql/data/devmac.local.err'.
111014 20:53:02 mysqld_safe Starting mysqld daemon with databases from /Users/jacek/apps/mysql/data
 
jacek:~/apps/mysql
$ ./bin/mysqladmin -u root password 'passw0rd'
 
$ ./bin/mysql -u root -p
Enter password: passw0rd

Uruchomienie

Na systemach uniksowych, np. MacOS X czy Linux, polecenie uruchomienia i zatrzymania serwera bazodanowego MySQL sprowadza się do następujących poleceń:

$ ./bin/mysqld_safe
110622 08:35:52 mysqld_safe Logging to '/Users/jacek/apps/mysql/data/devmac.local.err'.
110622 08:35:52 mysqld_safe Starting mysqld daemon with databases from /Users/jacek/apps/mysql/data

Poprawne uruchomienie powoduje pojawienie się następujących wpisów w dzienniku data/devmac.local.err.

$ tail -f /Users/jacek/apps/mysql/data/devmac.local.err
InnoDB: Doublewrite buffer not found: creating new
InnoDB: Doublewrite buffer created
InnoDB: 127 rollback segment(s) active.
InnoDB: Creating foreign key constraint system tables
InnoDB: Foreign key constraint system tables created
110622  8:35:52  InnoDB: Waiting for the background threads to start
110622  8:35:53 InnoDB: 1.1.7 started; log sequence number 0
110622  8:35:53 [Note] Event Scheduler: Loaded 0 events
110622  8:35:53 [Note] /Users/jacek/apps/mysql/bin/mysqld: ready for connections.
Version: '5.5.13'  socket: '/tmp/mysql.sock'  port: 3306  MySQL Community Server (GPL)

Zatrzymanie sprowadza się do wciśnięcia Ctrl-C w terminalu, w którym uruchomiono MySQL lub (preferowany sposób) wydaniu polecenia mysqladmin shutdown podając użytkownika z prawami SHUTDOWN (więcej w dokumencie 5.1.10. The Shutdown Process).

$ ./bin/mysqladmin -u root -p shutdown
Enter password: passw0rd

W dzienniku pojawi się wiadomość o zamknięciu serwera MySQL.

$ tail -f /Users/jacek/apps/mysql/data/devmac.local.err
110622  8:41:26 [Note] /Users/jacek/apps/mysql/bin/mysqld: Normal shutdown
 
110622  8:41:26 [Note] Event Scheduler: Purging the queue. 0 events
110622  8:41:26  InnoDB: Starting shutdown...
110622  8:41:27  InnoDB: Shutdown completed; log sequence number 1595675
110622  8:41:27 [Note] /Users/jacek/apps/mysql/bin/mysqld: Shutdown complete

Aplikacja grailsowa - grailsmysql

Stwórzmy przykładową aplikację, która początkowo korzysta z HSQL, a w kolejnym kroku zdefiniujemy konfigurację uruchomieniową z MySQL.

Utworzenie aplikacji - grails create-app

Zaczynamy od stworzenia struktury katalogowej projektu poleceniem grails create-app grailsmysql.

$ grails create-app grailsmysql
Welcome to Grails 1.4.0.M1 - http://grails.org/
Licensed under Apache Standard License 2.0
Grails home is set to: /Users/jacek/apps/grails

Base Directory: /Users/jacek/sandbox
Running script CreateApp_.groovy
Environment set to development
    [mkdir] Created dir: /Users/jacek/sandbox/grailsmysql/src
    ...
Created Eclipse project files.
Created Grails Application at /Users/jacek/sandbox/grailsmysql

Uruchomienie grails create-app jest stworzeniem zrębu aplikacji i już w tym momencie możliwe jest jej uruchomienie (poleceniem grails run-app). Nie łudźmy się jednak odnośnie jej funkcjonalności - owa aplikacja sprowadzi się do pojedynczej strony powitalnej, która wyświetla listę dostępnych kontrolerów, a skoro nie mamy jeszcze żadnego (za moment go stworzymy), pojawi się jedynie statyczny tekst.

Przechodzimy do katalogu projektu grailsmysql.

$ cd grailsmysql/

Utworzenie klasy dziedzinowej - grails create-domain-class

Kolejnym krokiem jest utworzenie klasy dziedzinowej pl.jaceklaskowski.grails.Ksiazka poleceniem grails create-domain-class pl.jaceklaskowski.grails.Ksiazka.

$ grails create-domain-class pl.jaceklaskowski.grails.Ksiazka
Welcome to Grails 1.4.0.M1 - http://grails.org/
Licensed under Apache Standard License 2.0
Grails home is set to: /Users/jacek/apps/grails

Base Directory: /Users/jacek/sandbox/grailsmysql
Running script CreateDomainClass.groovy
Environment set to development
    [mkdir] Created dir: /Users/jacek/sandbox/grailsmysql/grails-app/domain/pl/jaceklaskowski/grails
Created /Users/jacek/sandbox/grailsmysql/grails-app/domain/pl/jaceklaskowski/grails/Ksiazka.groovy for Ksiazka
    [mkdir] Created dir: /Users/jacek/sandbox/grailsmysql/test/unit/pl/jaceklaskowski/grails
Created /Users/jacek/sandbox/grailsmysql/test/unit/pl/jaceklaskowski/grails/KsiazkaTests.groovy for Ksiazka

Zmieniamy treść klasy dziedzinowej Ksiazka (plik grails-app/domain/pl/jaceklaskowski/grails/Ksiazka.groovy) na następującą:

package pl.jaceklaskowski.grails

class Ksiazka {

    String tytul
    String autor

    static constraints = {
        tytul(blank: false)
        autor(blank: false)
    }

}

Utworzenie kontrolera - grails create-controller

Dla stworzonej klasy dziedzinowej Ksiazka tworzymy odpowiedni kontroler poleceniem grails create-controller pl.jaceklaskowski.grails.Ksiazka.

$ grails create-controller pl.jaceklaskowski.grails.Ksiazka
Welcome to Grails 1.4.0.M1 - http://grails.org/
Licensed under Apache Standard License 2.0
Grails home is set to: /Users/jacek/apps/grails

Base Directory: /Users/jacek/sandbox/grailsmysql
Running script CreateController.groovy
Environment set to development
    [mkdir] Created dir: /Users/jacek/sandbox/grailsmysql/grails-app/controllers/pl/jaceklaskowski/grails
Created /Users/jacek/sandbox/grailsmysql/grails-app/controllers/pl/jaceklaskowski/grails/KsiazkaController.groovy for Ksiazka
    [mkdir] Created dir: /Users/jacek/sandbox/grailsmysql/grails-app/views/ksiazka
Created /Users/jacek/sandbox/grailsmysql/test/unit/pl/jaceklaskowski/grails/KsiazkaControllerTests.groovy for Ksiazka

Modyfikujemy klasę kontrolera KsiazkaController (plik grails-app/controllers/pl/jaceklaskowski/grails/KsiazkaController.groovy) tak, aby zawierała poniższą treść:

package pl.jaceklaskowski.grails

class KsiazkaController {

    def scaffold = Ksiazka
}

Istotnym elementem kontrolera jest wykorzystanie dynamicznego rusztowania (ang. dynamic scaffolding), gdzie widoki odpowiadające akcjom CRUD (ang. Create-Read-Update-Delete) są tworzone w pamięci, dynamicznie. Wystarczy zdefiniować statyczną zmienną scaffold z wartością, która wskazuje na obsługiwaną klasę dziedzinową (wartość to nazwa klasy, jednakże w Groovy Ksiazka == Ksiazka.class i dodawanie .class nie jest konieczne).

Uruchomienie aplikacji - grails run-app

W tym momencie możemy już uruchomić aplikację poleceniem grails run-app.

$ grails run-app
Welcome to Grails 1.4.0.M1 - http://grails.org/
Licensed under Apache Standard License 2.0
Grails home is set to: /Users/jacek/apps/grails

Base Directory: /Users/jacek/sandbox/grailsmysql
Running script RunApp.groovy
Environment set to development
...
Running Grails application..
Server running. Browse to http://localhost:8080/grailsmysql

Po jej uruchomieniu, wystarczy skierować przeglądarkę na adres http://localhost:8080/grailsmysql i cieszyć się "pięknem" naszej aplikacji grailsowej.

Grafika:grails-welcome.png

Wykonanie kilku czynności w aplikacji powinno upewnić nas, że domyślna konfiguracja bazodanowa z H2 działa poprawnie. Czas na włączenie konfiguracji MySQL.

Konfiguracja połączenia z MySQL

Zmiana konfiguracji aplikacji - grails-app/conf/DataSource.groovy

Jak mogliśmy zauważyć podczas uruchamiania poleceń grails, jednym z komunikatów była informacja z jakiego środowiska uruchomieniowego korzystamy, np.:

Environment set to development

W tym przykładzie, konfiguracja środowiska to środowisko rozwojowe development. W Grails istnieją 3 predefiniowane konfiguracje środowiska - development, test oraz production. Wskazanie, które powinno być bieżące to uruchomienie polecenia grails z parametrem JVM grails.env lub po prostu z podaniem nazwy skróconej konfiguracji jako drugi parametr grails, np.

$ grails test run-app
...
Environment set to test

Wykorzystajmy tę wiedzę do zdefiniowania własnej konfiguracji mysql z bazą MySQL w pliku grails-app/conf/DataSource.groovy. Plik DataSource.groovy jest skryptem Groovy, który odpowiada za konfigurację bazodanową i tam właśnie znajdziemy konfigurację domyślną z H2.

Plik grails-app/conf/DataSource.groovy powinien zawierać następującą treść:

dataSource {
    pooled = true
    driverClassName = "org.h2.Driver"
    username = "sa"
    password = ""
}
hibernate {
    cache.use_second_level_cache = true
    cache.use_query_cache = true
    cache.provider_class = 'net.sf.ehcache.hibernate.EhCacheProvider'
}
// environment specific settings
environments {
    development {
        dataSource {
            dbCreate = "create-drop" // one of 'create', 'create-drop','update'
            url = "jdbc:h2:mem:devDb"
        }
    }
    test {
        dataSource {
            dbCreate = "update"
            url = "jdbc:h2:mem:testDb"
        }
    }
    production {
        dataSource {
            dbCreate = "update"
            url = "jdbc:h2:prodDb"
        }
    }
    mysql {
        dataSource {
            dbCreate = "create-drop"
            url = "jdbc:mysql://localhost/grailsmysqldb"
            driverClassName = "com.mysql.jdbc.Driver"
            dialect ="org.hibernate.dialect.MySQLDialect"
            username = "root"
            password = "passw0rd"
        }
    }
}

Zwróćmy uwagę na ostatnią "sekcję" mysql, w której definiujemy źródło danych wskazujące na MySQL (w zasadzie to mysql oraz dataSource to wywołania metod akceptujących pojedynczy parametr, który jest de facto domknięciem).

Dostęp do bazy danych w Javie wymaga właściwego sterownika JDBC. W przypadku MySQL będzie to Connector/J jako plik mysql-connector-java-5.1.15-bin.jar, który umieszczamy w katalogu lib projektu. Konieczne jest ponowne uruchomienie aplikacji.

Utworzenie bazy danych

Ostatnim krokiem jest stworzenie bazy danych w MySQL. W naszej konfiguracji mysql w DataSource.groovy założyliśmy nazwę bazy grailsmysqldb. Stworzenie bazy to wykonanie polecenia create database grailsmysqldb (przez użytkownika z odpowiednimi uprawnieniami).

$ ./bin/mysql -u root -p
Enter password: passw0rd
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 13
Server version: 5.5.16 MySQL Community Server (GPL)

Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> create database grailsmysqldb;
Query OK, 1 row affected (0.01 sec)

Uruchomienie aplikacji - grails -Dgrails.env=mysql run-app

Po tych zmianach uruchamiamy aplikację ponownie poleceniem grails -Dgrails.env=mysql run-app.

$ grails -Dgrails.env=mysql run-app
Welcome to Grails 1.4.0.M1 - http://grails.org/
...
Environment set to mysql
...
Server running. Browse to http://localhost:8080/grailsmysql

Uruchomienie aplikacji http://localhost:8080/grailsmysql i stworzenie książki...

Grafika:grails-ksiazka1created.png

...to gwarancja, że aplikacja korzysta poprawnie z bazy MySQL.

Na zakończenie sprawdźmy struktury bazodanowe w MySQL.

$ ./bin/mysql -u root -p
Enter password: passw0rd
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 13
Server version: 5.5.16 MySQL Community Server (GPL)

Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> use grailsmysqldb;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> show tables;
+-------------------------+
| Tables_in_grailsmysqldb |
+-------------------------+
| ksiazka                 |
+-------------------------+
1 row in set (0.00 sec)

mysql> describe ksiazka;
+---------+--------------+------+-----+---------+----------------+
| Field   | Type         | Null | Key | Default | Extra          |
+---------+--------------+------+-----+---------+----------------+
| id      | bigint(20)   | NO   | PRI | NULL    | auto_increment |
| version | bigint(20)   | NO   |     | NULL    |                |
| autor   | varchar(255) | NO   |     | NULL    |                |
| tytul   | varchar(255) | NO   |     | NULL    |                |
+---------+--------------+------+-----+---------+----------------+
4 rows in set (0.04 sec)

mysql> select * from ksiazka;
+----+---------+-----------------+----------------+
| id | version | autor           | tytul          |
+----+---------+-----------------+----------------+
|  1 |       0 | Jacek Laskowski | Grails z MySQL |
+----+---------+-----------------+----------------+
1 row in set (0.00 sec)

mysql> quit
Bye

Działa jak należy. Gratulacje!

Osobiste