Grails is a full-stack framework developed to solve many of the challenges of web development through core technologies and related plugins.
While many web frameworks in the Java world are complex and do not embrace the DRY principle, Grails builds on the dynamic framework concept that led to modern thinking about web applications like Rails and Django. , Is built on existing Java technologies like Spring and Hibernate.
Reference http://docs.grails.org/latest/guide/single.html#requirements
In the article, I used the following version.
We have confirmed the operation only on Ubuntu 16. See the SDKMAN documentation (https://sdkman.io/) for more information.
Install SDKMAN (see SDKMAN Installation (https://sdkman.io/install))
Install Java
$ sdk install java
Install Grails
$ sdk install grails
C: \ grails
etc.)GRAILS_HOME
(GRAILS_HOME = C: \ grails
)% GRAILS_HOME% \ bin
to the environment variable PATH
grails -version
and if the Grails version is displayed, the installation is complete.When you create a Grails application, use the grails create-app command to create all the files you need.
$ cd ${Application save directory}
$ grails create-app grails-sample-app
# grails-sample-Change app to the application name as appropriate.
# "grails-sample-app"A directory is created,
#The package name of the application is"grails.sample.app"Will be.
You can also use a mode that allows you to execute each Grails command interactively. Interactive mode is available when you run the grails
command with no options, and you can complete the command with the Tab key in interactive mode.
Run the run-app
command to run the application.
Run a Grails application(without grails interactive mode)
$ cd ${create-app save destination directory}
$ grails run-app
Run a Grails application(When using grails interactive mode)
$ cd ${create-app save destination directory}
$ grails #Launch grails in interactive mode
grails> run-app
grails help
, and if you specify an option after help like grails help run-app
, help for executing that option is displayed.After running the application with run-app, access http: // localhost: 8080 / and you will see the Grails intro screen as shown below. (The screen shows the HelloController created in addition to the file created by create-app)
.gradle/
.idea/ ...Configuration file for IntelliJ IDE
build/ ...Build file(create-Does not exist immediately after running the app)
gradle/
grails-app/ ...Application source code
assets/ ...Asset resource storage(Files processed by the asset pipeline)
conf/ ...Runtime settings
controllers/ ...controller(MVC model C)
${app name}/ ...app name(If hyphens are included, the directory is separated for each hyphen)
UrlMappings.groovy ...URL mapping
domain/ ...Domain class(MVC model M)
i18n/ ... internationalization(i18n)Configuration support
init/ ...Processing at application startup
services/ ...Service layer
taglib/ ...Tag library(Definition of custom tags that can be used in View)
utils/ ...Grails-specific utility
views/ ... Groovy Server Page(GSP) or JSON Views (MVC model V)
src/
integration-test/ ...Integration test
main/ ...Static files that do not go through asset pipeline processing
groovy/ ...Domain class that you don't want to associate with the DB table
webapp/ ...Static file(Included in WAR, not in JAR)
resources/public ...Static file(/static/x/y/Access with z)
test/ ...Unit test
groovy/
${app name}/
${***Spec}.groovy
.gitignore ...VCS unmanaged file settings for Git
build.gradle ...Build settings
gradle.properties ...Gradle settings
gradlew ...Gradle startup script(UN*For X)
gradlew.bat ...Gradle startup script(For windows)
grails-wrapper.jar
grailsw ...Grails launch script(UN*For X)
grailsw.bat ...Grails launch script(For windows)
README.md ... README
Detailed information
Let's display "Hello World!" As a character string.
In Grails, when you create a controller and write an action in that class, it's mapped to a unique URL that you can access in your browser.
The mapping rule will be / <appname> / <controller> / <action>
, where <controller>
will be the controller class minus Class
.
For example, to create a HelloController
and have the ʻindex` action display" HelloWorld ", do the following:
Run a Grails application
grails> create-controller hello #HelloController Class is created
Edit the HelloController class created in grails-app / controllers / grails / sample / app / HelloController.groovy
to the following content.
grails-app/controllers/grails/sample/app/HelloController.groovy
package grails.sample.app
class HelloController {
def index() {
render "Hello World!"
}
}
When you run the application and access http: // localhost: 8080 / hello / index or http: // localhost: 8080 / hello /, the string "Hello World!" Is displayed. (The index action can be omitted on the URL)
The domain is M for MVC in Grails.
You can create individual controllers and views, as in the previous HelloWorld display example, but you can use the generate-all
option to create controllers and views with CRUD functionality for your domain (plus for testing). You can create a class).
Create Employee domain class and controller and view for CRUD, test
#Create an Employee domain class
$ grails create-domain-class employee
| Created grails-app/domain/grails/sample/app/Employee.groovy
| Created src/test/groovy/grails/sample/app/EmployeeSpec.groovy
#Create a controller, view, and test class to CRUD the Employee domain class
$ 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
Now you have the ability to CRUD the Employee domain class.
The domain class doesn't have any attributes yet, so edit the file grails-app / domain / grils / sample / app / Employee.groovy
and add the appropriate attributes.
grails-app/domain/grails/sample/app/Employee.groovy
package grails.sample.app
class Employee {
String name
static constraints = {
}
}
When I run run-app again to display the application's TOP screen in the browser, grails.sample.app.EmployeeController
is added to Available Controllers :.
The character string of grails.sample.app.EmployeeController is a link, and when you click it, you will be taken to the Employee list screen (http: // localhost: 8080 / employee / index).
Employee list screen |
---|
As you can see with some operation, the following routes have been added.
root | function |
---|---|
GET /employee/index | Display Employee list screen |
GET /employee/create | Display Employee new creation screen |
GET /employee/show/:id | Employee details screen(:id is the ID of the domain class)Show |
GET /employee/edit/:id | Employee edit screen(:id is the ID of the domain class)Show |
POST /employee/save | Employee new creation |
PUT /employee/update/:id | Employee update(:id is the ID of the domain class) |
DELETE /employee/delete/:id | Employee deleted(:id is the ID of the domain class) |
The DB used by the application can be changed for each environment variable such as development, test, and production. Grails is set to use H2 Database by default in all environments.
You will also notice that the DB is initialized when you finish all CRUD operations, exit run-app and run the application again. This is because H2 is an in-memory DB, and the development environment DB is configured to DROP the DB at application termination and create it at startup.
These settings are set in grails-app / conf / application.yml. (See The Grails Framework> The DataSource for details on the settings.)
This time, I will set not to destroy the database created in the development environment.
Rewrite grails-app / conf / application.yml
with the following content.
grails-app/conf/application.yml
environments:
development:
dataSource:
# create-Change from drop to update
dbCreate: update
# mem:from devDb./Change to devDb
url: jdbc:h2:./devDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
The values that can be set with dbCreate are as follows.
dbCreate settings | Description |
---|---|
create | Existing schema at startup(table,index,other)Drop and recreate |
create-drop | Same as create, but delete table when the application is closed |
update | Create or update a table or index that does not exist. Do not drop existing tables or data.(Note that old columns and data will remain because column name changes cannot be handled properly.) |
validate | Output a warning by comparing the existing schema and settings without making any changes to the DB |
any other value | do nothing |
def p = new Person(name: "Fred", age: 40, lastVisit: new Date())
p.save()
def p = Person.get(1)
println('name: ' + p.name)
def p = Person.get(1)
p.name = 'Bob'
p.save()
def p = Person.get(1)
p.delete()
Let's extend the Employee domain class a little more and make it more practical.
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
}
/**
*Length of service
*/
public int serviceYears() {
def today = new Date()
return Math.max(0, today.year - this.joinedDate.year)
}
}
For the time being, except for note, I set nullable: false
(since it is default, it is not specified in constraints) and blank: false
.
If you set blank: true
like note, you need to disable the process of converting to null when the data is blank.
The setting can be disabled by setting ʻapplication.groovyas follows. If the file does not exist, create it under
grails-app / conf /`.
grails-app/conf/application.groovy
// the default value for this property is true
grails.databinding.convertEmptyStringsToNull = false
Now, change the list screen and edit screen to display Employee as follows.
--List screen --Display ID, name, department, gender --You can sort by each item --Employee Display new button --You can press the new button at any time --Display Employee edit button --It can be pressed only when any Employee is selected. --Detail screen --In addition to the items displayed on the list screen, the birth, serviceYears () execution result, payment, and note are displayed.
List screen view(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">
/*Set the edit button status based on the radio button status*/
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…"/></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>
Detail screen view(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…"/></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>
That's a little more practical.
The view is described in Groovy Server Pages (GSP), and <g: link>
, <g: form>
, etc. are GSP tags. (See Groovy Server Pages (GSP) for more information)
By default, Grails 3.3.5 introduced Bootstrap 3.3.6.
There seems to be twitter-bootstrap-grails-plugin of Grails plugin, but the current version is 3.3.5 on September 18, '18. It looks like this, and it doesn't seem to be well maintained because the last commit was two years ago, so I'll try installing Bootstrap v4 manually.
grails-app / assets / javascripts / application.js
--Change from // = require jquery-2.2.0.min
to // = require jquery-3.3.1.min
--Change from // = require bootstrap
to // = require bootstrap.bundle
Rerun the application and you're done. (The navbar collapses, but I'm not using it, so ignore it)
Let's edit the index.gsp view we just wrote.
grails-app/views/layouts/main.gsp
<head>
: <snip>
<asset:stylesheet src="application.css"/>
<asset:javascript src="application.js"/> <!--Move the line written in the body into the head-->
: <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>
It's okay if the button has the Bootstrap 4 style applied.
Let's add a filter function to the Employee list screen to make it a little more practical.
I will use DataTables to add the search function to the table displayed on the list screen this time.
In addition, there was grails-datatables etc. as Grails plugin, but install the latest version of DataTables like bootstrap and jquery. I will decide.
From the DataTables Download Page (https://datatables.net/download/), select the item below to download the file.
Copy the following files from the downloaded file to grails-app / assets / (javascripts | stylesheets) / according to the type.
Finally, edit grails-app / assets / application. (Js | css). Since the require destination files described in application. (js | css) are read in order from the top, please be careful about the description order for packages with dependencies.
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>
*/
Now you're ready to use DataTables.
Apply DataTables to the table on the list screen.
To apply DataTables, apply the dataTable ()
method to the DOM that points to the table you want to apply in jQuery.
grails-app/views/employee/index.gsp
<!DOCTYPE html>
<html>
<head>
: <snip>
<script type="text/javascript">
/*Set the edit button status based on the radio button status*/
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>
Run the application and you'll see that DataTables has been applied.
Here you can see that the pagination features of DataTables and Grails have become redundant.
Which feature to enable depends on the design of the application, but this time I will leave Grails' pagination feature to reduce the amount of data processing.
To disable the DataTables pagination feature, add the paging: true
parameter to the dataTable () method as described on the Official Page (https://datatables.net/reference/option/paging). I will pass it.
#Apply DataTables to employeeindex DOM without pagination
$(document).ready(function() {
$('#employeeindex').dataTable({
"paging": false
});
} );
Finally, fix the Navbar at the top of the screen. Also, modify the style accordingly and add Font Awesome on the CDN to use 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…"/>
</div>
</body>
</html>
Large Display Size | Small Display Size |
---|---|
Until now, the database was updated by setting dbCreate: update
with reference to the model file when the application was started, but due to the limited conditions that can be changed and the old columns remain, multiple people When developing an application, different people may have different database structures.
So I decided to create a migration file and create a database from it.
Use the database-migration
plugin to do database migration in Grails. (The database-migratation
plugin uses the Liquibase library.)
Migrations are processed using one or more change log files written in Groovy DSL or native Liquibase XML.
The change log file has a globally unique ID. (ID contains the username of the user who created the changelog)
Edit bundle.gradle
as follows to use the database-migration
plugin. (Change the plug-in version as appropriate)
Note that bundle.gradle
is a configuration file for Gradle, and Gradle is an open source build automation system based on the concept of Apache Ant and Apache Maven. (Reference: Wikipedia> Gradle)
buildscript {
repositories {
: <snip>
}
dependencies {
: <snip>
classpath 'org.grails.plugins:database-migration:3.0.4' # database-Added migration plugin
}
}
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'
}
}
}
Then set the database dbCreate
to none
.
Otherwise, you will not get the difference when creating a migration file with the dbm-gorm-diff
option.
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>
Next, create a change log file.
You can choose the description format, but I will use Groovy DSL.
There are two ways to create a changelog, one is from the database and the other is from the domain class. Right now, I think that the database is automatically created from the domain class at startup, so select the method to create the change log from the database.
How to create a changelog from a database in Groovy DSL format
$ grails dbm-generate-changelog changelog.groovy
: <snip>Plugins will be downloaded as needed
: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
This will create the following changelog:
Changelog(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")
}
}
}
}
Now you can build the DB based on the changelog. (Only the database schema is built, no data is migrated)
From now on, after editing the domain class, you will have to manually or automatically create a changelog and run dbm-update
.
--add
option, the changelog will be saved separately with the specified file name, and it will be added so that it will be read by include from changelog.groovy)
$ grails dbm-gorm-diff add_birth_column_to_employee.groovy --add
grails dbm-update
for any environment (development, test, production)The Groovy console is an application that allows you to enter and run Groovy source code. You can also load a domain class implemented in your Grails application, or use the ORM of that domain class to work with your database.
You can start the Grails console by running the grails console command.
Launch the groovy console
$ cd ${application directory}
$ grails console
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)
Describes the errors that occur while executing Grails commands and how to deal with them.
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
--Delete grails dbm-release-locks
or DB
Recommended Posts