This lecture about Grails assumes that you have already done the first three individual programming assignments, so you should know that Grails is a web framework built on the Groovy programming language. You know that Grails uses the MVC design pattern and if you follow the conventions, much of code is scaffolded for you. Although a goal of Grails is to prefers convention over configuration, there a number of configurations that need to be made. Your programming assignments actually edited almost all the configuration files. You also realize that plugins can extend the basic Grails framework, and you have installed several plugins.
This lecture will not start from the beginning, rather it will introduce more advance features than what your programming assignments introduced. However, this lecture of Grails is not an exhaustive list of all the advance topics in Grails. The Grails documentation is the best source for all the features of Grails.
Much of the material for this lecture comes from “The Definitive Guide to Grails 2” by Jeff Scott Brown and Graeme Rocher, a very good book, but it is a little out of date now because Grails 2.4 uses the assest-pipeline plugin rather than the resource plugin and now Grails is on version 3. This lecture will proceed by going through some of the domain, controller and view advance features that I expect you will need for your projects.
Domain
Grails uses GORM for Object Relational Mapping to define the domain objects. Documentation for GORM is at
http://gorm.grails.org/latest/hibernate/manual/
Associations
Domain classes can have associations with each other much like tables in a database can be related to each other. In a database, tables can be related to each other by having a foreign key that points to a primary key in another table. Another way that tables can be associated to each other is by a third table called a join or junction table that pairs keys from each table.
An example of a domain class relation is the Author – Book domain classes in your programming class. An Author could have written many books. So an Author entry can be associated with many Book entries.
The simplest relationships are
• Many-to-one
• One-to-one
• One-to-many
• Many-to-many
Your programming assignment is an example of one-to-money. The Author domain has a one-to-many the Book domain.
Many-to-one
The simplest relation to implement in Grails is many-to-one. You just reference the “one” domain in the “many” domain.
class Face { Nose nose } class Nose { }
So a Nose can be referenced by many Faces. Grails implements this relation with a Nose “foreign key” in the Face table.
One-to-One
Nose and Face should really have a one-to-one association. To make a one-to-one relations the Nose domain must reference the Face domain.
class Face { Nose nose } class Nose { static belongsTo = [face: Face] }
The static property “belongsTo” puts a Nose foreign key in the Face table, just as above.
Cascading is the behavior of saves, update and delete on the domain classes when you perform an operation on related domains. In this case, the one-to-one association, when you save and delete Face, GROM will save and delete Nose. In other words, saves and delete will cascade from form Face to Nose.
To make a Face, you need to make the Nose first.
new Face(nose:new Nose()).save()
A way to make the one-to-one bidirectional is to use the “hasOne” property, and then the cascading can go both directions.
class Face { static hasOne = [nose:Nose] } class Nose { Face face }
This will put the Face foreign key in the Nose table. The cascades will go both directions.
One-to-many
To indicate a one to many association, add a static property “hasMany” to the “one” domain.
class Author { static hasMany = [books: Book] String name } class Book { String title }
Grails will implement this association with a join table. The join table is a third table probably called AuthorBook and will associate Author keys with Book keys.
When you retrieve from the “one” table Grails will automatically inject a Set of the “many.”
def a = Author.get(1) for (book in a.books) { println book.title }
In this case saving and updates on Author will cascade to the Book domain, but not delete. Adding the “belongsTo” property to the “many” domain will cascade also deletes from Author to Book.
class Author { static hasMany = [books: Book] String name } class Book { static belongsTo = [author: Author] String title }
Note sometimes associations can become complicated when the “many” domain class references more than one “one” domain. For example Airports can have many flights, and a flight is associated with two airports, the departure and destination airports. See the GORM documentation for how to handle this.
http://gorm.grails.org/latest/hibernate/manual/#gormAssociation
Many-to-Many
The “many-to-many” association is specified by using “hasMany” in both domain classes. One domain has to be owner by adding the “belongsTo” property to the other domain. Cascading is from the owner domain.
class Book { static belongsTo = Author static hasMany = [authors:Author] String title } class Author { static hasMany = [books:Book] String name }
So in this case you have to make the Books first.
new Author(name:"Stephen King") .addToBooks(new Book(title:"The Stand")) .addToBooks(new Book(title:"The Shining")) .save()
Documentation for GORM associations is at
http://gorm.grails.org/latest/hibernate/manual/#gormAssociation
Queries
Queries are how you retrieve data from a database. You have already seen how the “list” method in the domain class can retrieve all the table entries. That is a query. The response from the list method can be modified with map argument.
You can retrieve a single table entry if you know the id by using the “get” method.
def book = Book.get(23)
Dynamic Finders
GORM has “dynamic finders.”
http://gorm.grails.org/latest/hibernate/manual/#finders
Dynamic finders are methods that are created dynamically at run time. They are best explained by example.
def book = Book.findByTitle("The Stand") book = Book.findByTitleLike("Harry Pot") book = Book.findByReleaseDateBetween(firstDate, secondDate) book = Book.findByReleaseDateGreaterThan(someDate) book = Book.findByTitleLikeOrReleaseDateLessThan("Something", someDate)
See the findBy and findByAll documentation in the Domain class quick reference.
http://grails.org/doc/latest/ref/Domain%20Classes/findBy.html
http://grails.org/doc/latest/ref/Domain%20Classes/findAllBy.html
The general form of the method name is
Book.findBy([Property][Comparator][Boolean Operator])?[Property][Comparator]
The part of the expressions marked by “(…)? is optional, so the simplest expression is
def book = Book.findByTitle("The Stand")
Property is a domain field, in other words a table header.
Comparators include:
• InList – In the list of given values
• LessThan – less than a given value
• LessThanEquals – less than or equal a given value
• GreaterThan – greater than a given value
• GreaterThanEquals – greater than or equal a given value
• Like – Equivalent to a SQL like expression
• Ilike – Similar to a Like, except case insensitive
• NotEqual – Negates equality
• InRange – Between the from and to values of a Groovy Range
• Rlike – Performs a Regexp LIKE in MySQL or Oracle otherwise falls back to Like
• Between – Between two values (requires two arguments)
• IsNotNull – Not a null value (doesn’t take an argument)
• IsNull – Is a null value (doesn’t take an argument)
Where Querying
The dynamic finders are restricted to queries into a single table. Sometimes you want the query result to dependent on associated tables then you must use the “where” query.
http://gorm.grails.org/latest/hibernate/manual/#whereQueries
You can also use a where query to retrieve results dependent on the same table.
def query = Person.where { firstName == "Bart" } Person bart = query.find()
You first define the testing closure IN the “where” method. Finally, you retrieve the results by calling the “find” method. You can use all the Groovy Boolean tests on any property in the domain. You CANNOT define the testing closure outside the where method. In other words this will NOT work.
//This will not work! def callable = { lastName == "Simpson" } def query = Person.where(callable)
To generate results dependent on other tables, the table making the “where” clause must have a reference to the other table. For example.
def query = Pet.where { owner.firstName == "Joe" || owner.firstName == "Fred" }
In this case owner is a property of Pet and references the Owner table.
The above material is a very brief introduction of the material in the GORM and Grails documentation.
http://grails.org/doc/latest/guide/GORM.html
http://gorm.grails.org/latest/hibernate/manual/
Constraints and Validation
Grails uses the “constrain” closure for validation. This is appropriate because the domain class is the representation of data, so it should be expressed there.
class User { String login String password String email Integer age static constraints = { … } }
In the domain class “constraints” is a closure. You then use methods calls in the constraint closure to specify the constraint for each property of the domain.
class User { ... static constraints = { login size: 5..15, blank: false, unique: true password size: 5..15, blank: false email email: true, blank: false age min: 18 } }
Note that “login size: 5..15, blank: false, unique: true” could have been written
login([ size: 5..15, blank: false, unique: true ])
So “login” is a method and “size”, “blank” and “unique” are keys in a map argument. The methods are named after the properties in the domain class and are built for you by Grails, and the key are different constraints.
Note that “5..15” is using the groovy range operator, “…”, so the size of “login can be 5 to 15 characters long.
The “blank” key specifies if the property value can be empty. For login the string cannot be empty.
The “unique” key specifies that the login string must be unique in the database table. Note that the order of properties in the constraints closure will set the order of properties in the table and the return from the list method.
The possible key-constraints are found in the Quick Reference under the Constraints header.
• blank
• creditCard
• email
• inList
• matches
• max
• maxSize
• min
• minSize
• notEqual
• nullable
• range
• scale
• size
• unique
• url
• validator
In general, their use is obvious. Read the documentation on how to use them. For example “inList” constrains the property value to a list that you provide. It will be implemented as a dropdown in the scaffolded view.
Types, Nulls and Required Constraints
By default, domain properties are required to pass the validation (See below about validation). If you want the property not be required then in the domain’s closure the property’s “nullable” attribute must be set to true. For example
Class Item { Integer number String name int anotherNumber static constraints = { number (nullable: true) name (nullable: true, blank: true) } }
Strings need to be both nullable and blank true. Primitive types, such as anotherNumber above, cannot be nullable true. The scaffolding views will be made and there will be no error if you make a primitive type nullable, but the view will show a default value of zero and the database generated by Hibernate will show that the column is “NOT NULL.”
Vaildator
The validator constraint can be used to make custom constraints. For example suppose that the possible answers to a survey is listed in “Survey.answers.” Then to validate the Response.answer
class Response { Survey survey Answer answer static constraints = { survey blank: false answer blank: false, validator: { val, obj -> val in obj.survey.answers } } }
Note that “val” is the value of the form field and “obj” is the domain instance being validated, essentially “this.” There is also a third argument, “errors,” that you can use to attach a message to the Error object associated with the domain.
In the simplest form the validator closure returns a Boolean for the result of the validation.
even validator: { val -> return (val % 2) == 0 }
More typically, validators return true if the entry passes validation and a list if there is an error in validation.
Messages (a short diversion)
To understand the next validator example, we need to first understand messages.
Messages are the string/text of your website. Best practice is not to hard code string/text in the views. Rather you associate the string/text with a message. Messages are found in the grails-app/i18n/messages.properties file. Which is list of properties and values separated by “=”, for example:
default.paginate.prev=Previous default.paginate.next=Next default.boolean.true=True default.boolean.false=False
In the above example “default.paginate.prev” is a String variable that you can use in your code. The value of the string is “Previous.”
The message value can have “variable” sub-strings that are passed to it in an order list. The sub-strings are called parameters. The parameters in the list are indexed by {0}, {1}, {2} etc, For example the {0} below.
default.create.label=Create {0}
Messages are typically used in the view with the “g:message” tag and the sub-string list of parameters are passed by the “args” attribute of the tag.
<g:message code="default.create.label" args="[entityName]" />
Back to Validator returning a List
The constraint can also return a list. This is generally done when the entry is not valid and is used to help generate the error message. The validator will find the message by first mapping to the class name and property name that is being validated. The first string in the list returned by the validator will make the message more specific by mapping to the class name, property name and then the first string in the list in the message.property file. For example
className.propertyName.theFristSringInList
The example below test for the existence of an entry. If it does not exist then it creates an error message.
class Person{ String name static constraints = { name validator: { if (!it) return ['entryMissing'] } // This maps to the message: // person.name.entryMissing = Please enter a name in the field {0} }
The parameter list given to the error message from a validator has some parameters automatically added to the list. Parameters 0 to 2 are automatically mapped to 0: property name, 1: class name, 2: property value.
So in the example above, the list returned from the validator, indicates the message, “person.name entryMissing”. The 0 parameter for the message is the property, “name.”
The list returned by the validator can have addition strings. These are mapped from index 3 on in the parameter list for the message.
Controllers
In your programming assignments, you have learned to create a controller, send the model to the view and how to associate a view with the controller. In this section, you’ll learn some more tool to use with controllers. Many of the tools are properties that are injected into your controller when the controller is instantiated.
Logging
Logging prints messages to a log file or to the console. These messages are used to record errors, warnings, gather debugging info, etc. In your programming assignment, you probably used “println” to write debugging information, but that is not always the best technique, for example in the production environment, it is better to write to a file.
A logger writes messages at different levels. The priority of the levels are
1. off
2. fatal
3. error
4. warn
5. info
6. debug
7. trace
8. all
Note that “1. off” and “8.all” are not really levels. They exist only to specify the top and bottom of the priority list and to configure a logger.
Loggers are injected to all your grails app artifacts. The injected logger is accessed using the “log” object. To use the logger you call the method corresponding to level of priority of the message. For example
log.debug("My debugging message.")
will write a debug message.
If you do this in a controller, for example
class HomeController { def index { log.debug("In index method) … } }
The debug log message will probably not appear in the console. You need to configure the logger. Loggers are configured by the “log4j.main” closure in the Config.groovy file. To configure the logger you specify the level followed by a list of all the loggers who should print at that level or higher. For example, several loggers are already specified in the log4j closure.
log4j.main = { error 'org.codehaus.groovy.grails.web.servlet', // controllers 'org.codehaus.groovy.grails.web.pages', // GSP 'org.codehaus.groovy.grails.web.sitemesh', // layouts 'org.codehaus.groovy.grails.web.mapping.filter', // URL mapping 'org.codehaus.groovy.grails.web.mapping', // URL mapping 'org.codehaus.groovy.grails.commons', // core / classloading 'org.codehaus.groovy.grails.plugins', // plugins 'org.codehaus.groovy.grails.orm.hibernate', // hibernate integration 'org.springframework', 'org.hibernate', 'net.sf.ehcache.hibernate' }
In the above the “org.codehaus.groovy.grails.web.servlet” logger will print error and fatal messages.
The name of the logger that are injected into your classes are specified
grails.app.<type>.<className>
Where the <type> is “controllers”, “domains”, “views”, or “services” and <className> is the full class name including the package. So to configure the injected logger in your HomeController you would add to the log4j closure.
log4j.main = { debug 'grails.app.controllers.cs4760progassign.HomeController' }
Although this will work for logging in the HomeController, it will not enable logging for any other artifact. If you are logging from one artifact you probably want to log in all artifacts. You can specify a group of loggers. For example
log4j.main = { debug 'grails.app' }
This will enable the injected loggers to write debug messages and higher.
This is good enough if you only want to log to the console. More configurations are required to write to a file. You need to make an Appender and associate it with a level and a logger. There are four Appenders available.
- jdbc – Logs to a JDBC connection.
- console – Logs to the console.
- file – Logs to a single file.
- rollingFile – Logs to files, for example a new file each day.
The Appenders are created and configured in the Appenders closure in the log4j closure. For example to create a file Appender, you would add this Appender closure.
log4j.main = { appender { file name: 'myFileAppender', file: 'target/myLog.log' } }
This will create an Appender called ‘myFileAppender’ that writes to the file “target/myLog.log” which is adjacent to “stacktrace.log” in the “target directory.” Although this will create the file, it is still not sufficient for log messages to appear in the file. The Appender needs to be associated with the logger. You do this when you specify the level of logging in the log4j closure.
log4j.main = { appender { file name: 'myFileAppender', file: 'target/myLog.log' } debug myFileAppender: 'grails.app' }
Now it will work and log to the file and the console. There is much more to logging. Read about them in the documentation.
http://grails.org/doc/latest/guide/conf.html#logging
Scope Objects
You are familiar with namespace variable scoping in applications, meaning the namespace that a variable is defined in and can be used in. In a web application scoping is more complicated than the namespace. Variables also have time scope. Most objects only exist during a single request. Some objects can exist longer, these objects are
- params – a map of the request parameters in either the URL in a GET request or from a form submit in a POST request.
- request – a HttpServletRequest object, and exist for only a single request to the server
- flash – a object that exist with the current request and the next request
- session – a HttpSession object, and exist with a user’s visit to the domain
- servletContext –a ServletContext object, exist for the lifetime of the application
The scope objects params and request are created for every request to the server. A single session parameter is created when the user visit the domain and continues to navigate domain. Typically it expires when the user logouts or after some time limit. The servletContext exist for the lifetime of the web app, but you’ll seldom have need for this object.
All of these scope objects are available to all your artifacts, including controllers and views. You can set and get objects in these scope objects by using the dot format. For example
def user = session.username session.myObject = myObject
When a user fills out a form and hits the submit button, a request is made to the server. The form data consists of name-value pairs that are put into the params object. The controller can access them. For example when creating an Author named “Bob Jones” using the create form
In the Author/create view
<form action="/cs4760progassign/author/save" method="post" > <fieldset class="form"> … <div class="fieldcontain required"> <label for="name"> Name <span class="required-indicator">*</span> </label> <input type="text" name="name" required="" value="" id="name" /> </div> </fieldset> <fieldset class="buttons"> <input type="submit" name="create" class="save" value="Create" id="create" /> </fieldset> </form>
After clicking the submit button. A POST request to “/cs4760progassign/author/save” will be made and the params will contain
name="Bob Jones" create="Create" // a hidden form entry, see the submit button.
Grails also puts into the params entries
controller="author" action="save"
Data Binding
Data binding is the process of taking form data, populating the domain object and updating the database. For example in a the Author controller we could have
class AuthorController { def save(){ def author = new Author() author.name = params.name author.save() } }
As our Author domain class becomes more complicated with many more properties, the template code above will get more complicated and difficult to maintain. If the fields in the form data match those of the property names in the domain class then the binding can be automatic.
class AuthorController { def save(){ def author = new Author(params) author.save() } }
Of course, you cannot use the above technique when you are updating a table entry. In that case, you would get the domain instance from the table and set the properties map of the domain.
class AuthorController { def update(){ def author = Author.get(params.id) author.properites = params author.save() } }
Note that Grails will only fill in matching property names.
Validating Data
Validation has two phases. The first phase occurs when the request parameters are matched with the domain properties, such as in this line.
author.properites = params
In this phase, Grails must make type conversions from the params to the Author properties. If the types cannot be converted then Grails will set the database entry to read only and author cannot be updated.
The second phase occurs when the domain instance is saved, such as
author.save()
or explicitly validated
author.validate()
At this points, Grails will check the constraints for each property specified in the constraint closure. If there are errors, they are added to the “errors” property of the domain instance. You can test for errors using the hasErrors() method or cycle through the error.
author.errors.allErrors.each { println it.code }
You can also render the errors in the view.
<g: renderErrors bean="${author}" />
Data Binding to Multiple Domain Objects
In the previous example data binding of the author name, the name of the input field for the author’s name was just called “name” and that matched the domain class property, name. If we want to bind multiple domain objects then the names of the input fields need to be more explicit. For example consider a form that creates simultaneously a book and an author.
The view
... <input type="text" name="author.name" /> <input type="text" name="book.title" /> <input type="number" name="book.publishYear" /> ...
Then in the controller
def author = new Author( params["author"] ) .addToBooks( book = new Book( params["book"] ) ) .save()
There is much more to data binding. Read the data binding section in the Web Layer chapter.
http://grails.org/doc/latest/guide/theWebLayer.html#dataBinding
Views
Scriptlet Blocks
Any groovy code can be inserted into the GSP page using the scriptlet block <% … %>
<html> <body> <% 3.times { %> <p>I'm printed three times. </p> <% } %> </body> </html>
Scriptlets do not output to the page. You can use standard output to output to the page.
<% out << "Hello World!" %>
or the short hand
<%= "Hello World! %>
In practice, you normally do not use the above scriptlet tag. To output to the page use the G-string with curly brackets.
<p>${author.name}</p>
G-Tag
Also, you should not need to use the scriptlets tag for controlling output. A better alternative are the built in g-tags.
Setting Variables
You can set variables using the g:set tag.
<g: set var="bookTitle" value="${book.title}">
Then the variable “bookTitle” will have the value of book.title.
Logical Tags
G-tags include if-else tags.
<g:if test="${book?.publishYear > 2014}"> Year is too new. </g:if> </g:elseif test="${book?.publishYear < 1900} Year is too old. <g:else> Year is ${book?.publishYear} </g:else>
Iterative Tags
You can iterate over collections using the g:each tag.
<g:each in="${author.books}" <span class="tag">${it.title}</span> </g:each>
Or you can name the variable in the each-loop.
<g:each var="b" in="${author.books}" <span class="tag">${b.title}</span> </g:each>
There are also g:while, g:collect and g:findAll tags.
Grails Dynamic Tags
Grails dynamic tags are provided by the Grails tag libraries. You can use them as tags, but you can also call them with methods. This avoids nested tags. For example
<a href="<g:createLink action="list" />" > List </a>
is better written with a method call
<a href="${createLink(action: "list">}"> List </a>
Link Tags
The g:link tag will create an anchor tag. It has the following attributes
- controller: the controller name for the link
- action: the action name for the link
- id: the identifier for the link
- params: map of parameters to pass to the link.
For example
<g:link controller="book" action="show" id="{$book.id}"> Show Book </g:link>
will create
<a href="/cs4760progassin/book/show/1">Show Book</a>
Using params to generate multiple links.
<g:link controller="book" action="list" params=[max:10,order='title']">Show the first 10 books</g:link>
If you do not specify the controller then it defaults to the current controller. If you do not specify the action then it defaults to the index method.
There is also a url attribute which is map containing controller, action, id and resource. For example linking to the book with id=1.
<g:link url="[controller:'book', action:'show', id='1']"> Show Book</g:link>
But more likely, the view will have a instance of the book, bookInstance, then you can use
<g:link url="[resource:bookInstance, action:'show']" > Show Book</g:link>
Grails will find the id from bookInstance. There are many other attributes, see the reference manual.
http://grails.org/doc/latest/ref/Tags/link.html
Form Tags
The g:form tag has all the attributes of the HTML form tags. They are used the same
<g:form action="save"> … </gform>
But you can use all the attributes from the g:link tag if you wish.
<g:form url="[controller='book', action='save']"> … </gform>
The g:textField Tag
<g:form url="[controller='book', action='save']"> <g:textField name="title" value=""> </g:textField> </g:form>
The g:checkBox and g:radio Tages
<g:checkBox name="aBooleanValue" value="${true}" />
and
<fieldset> <g:radio name="myGroup" value="friction" checked="${bookInstance.genre = 'fiction'}" /> Fiction </g:radio> <g:radio name="myGroup" value="nonfriction" checked="${bookInstance.genre == 'nonfiction'}" /> Nonfiction </g:radio> </fieldset>
Select Tag
<g: select name="subject" from="${['math', 'cs', 'pyschology', 'humanities']}" value="${book.subject}" />
The g:datePicker Tag
<g:datePicker name="date" value="$(new Date()}" precision="day" />
Validation and Error
When a user inputs invalid entries, the errors should be shown in the view for the user to correct.
The hasErrors and eachError Tags
The hasError g-tags test for validation errors. Recall that validation occurs after an attempt to save data. The eachError tag iterates through the errors. Both the hasErrors tag and eachError have three attributes.
- bean: a bean instance to inspect for errors. This is typically the domain instance.
- field: the name of the field in the bean to check for errors. This is typically the property of domain instance.
- model: an alternative to specifying a bean.
The eachError tag is used with the hasError tag.
<g:hasErrors bean="${bookInstance}"> <ul class="errors"> <g:eachError bean="${bookInstance}"> <li>${it.defultMessage}</li> </g:eachError> </ul> </g:hasErrors>
A real life example from the book scaffolding.
<g:hasErrors bean="${authorInstance}"> <ul class="errors" role="alert"> <g:eachError bean="${authorInstance}" var="error"> <li <g:if test="${error in org.springframework.validation.FieldError}"> data-field-id="${error.field}" </g:if> > <g:message error="${error}"/> </li> </g:eachError> </ul> </g:hasErrors>
In the example above the “error” attribute in the message tag is the same as the “code” attribute, it just looks in a different file. You should not use the error attribute for your own messages.
Also note how the <g:if-tag is used inside the list tag to make the data-field-id attribute for the list tag. This used by Grails code to locate the entry with the error.
A nice example of modifying the styling of a field is using the hasError method call. See the first line.
<div class="fieldcontain ${hasErrors(bean: authorInstance, field: 'name', 'error')} required"> <label for="name"> <g:message code="author.name.label" default="Name" /> <span class="required-indicator">*</span> </label> <g:textField name="name" required="" value="${authorInstance?.name}"/> </div>
The div surrounding has the hasErrors method call. The method call will check authorInstance.name for errors. If there is an error it will output “error” which will add the error class to the div. In this case, the styling will change the border and background.