grails create-service asia.grails.sample.TestYou can also create a service class inside the GGTS IDE.
Below is the resulting class created. A default method is provided as an example. This is where we will write business logic.
package asia.grails.sample class TestService { def serviceMethod() { } }Just add as many functions as needed that pertains to business logic and GORM operations. Here is an example:
package asia.grails.sample class StudentService { Student createStudent(String lastName, String firstName) { Student student = new Student() student.lastName = lastName student.firstName = firstName student.referenceNumber = generateReferenceNumber() student.save() return student } private String generateReferenceNumber() { // some specific logic for generating reference number return referenceNumber } }
package asia.grails.sample class MyController { def studentService def displayForm() { } def handleFormSubmit() { def student = studentService.createStudent(params.lastName, params.firstName) [student:student] } }If you are not familiar with Spring and the concept of injection, what it means above is that you don't need to do anything special. Just declare the variable studentService and the Grails framework will automatically assign an instance to it. Just declare and use right away.
You can also inject a service to another service. For example:
package asia.grails.sample class PersonService { Person createPerson(String lastName, String firstName) { Person p = new Person() p.lastName = lastName p.firstName = firstName p.save() return p } } package asia.grails.sample class EmployeeService { def personService Employee createEmployee(String lastName, String firstName) { Person person = personService.createPerson(lastName, firstName) Employee emp = new Employee() emp.person = person emp.employmentDate = new Date() emp.save() return emp } }
You can also inject a service inside Bootstrap.Grovy. For example:
class BootStrap { def studentService def init = { servletContext -> if ( Student.count() == 0 ) { // if no students in the database, create some test data studentService.createStudent("Doe", "John") studentService.createStudent("Smith", "Jame") } } }
You can also inject a service inside a tag library. For example:
package asia.grails.sample class StudentService { def listStudents() { return Student.list() } } class MyTagLib { StudentService studentService static namespace = "my" def renderStudents = { def students = studentService.listStudents() students.each { student -> out << "<div>Hi ${student.firstName} ${student.lastName}, welcome!</div>" } } }
If you are are new to working with databases, transaction is a very important concept. Usually, we wish certain sequence of database changes to be all successful. If not possible, we want no operation to happen at all. For example, consider this code to transfer funds between two bank accounts:
class AccountService { def transferFunds(long accountFromID, long accountToID, long amount) { Account accountFrom = Account.get(accountFromID) Account accountTo = Account.get(accountToID) accountFrom.balance = accountFrom.balance - amount accountTo.balance = accountTo.balance + amount } }This code deducts money from one account (accountFrom.balance = accountFrom.balance - amount), and adds money to another account (accountTo.balance = accountTo.balance + amount). Imagine if something happened (an Exception) after deducting from the source account and the destination account was not updated. Money will be lost and not accounted for. For this type of codes, we want an "all or nothing" behavior. This concept is also called atomicity.
class CountryService { }
class CountryService { static transactional = true }
@Transactional class CountryService { }For readability, I suggest declaring the static transactional at the top of each service.
class CountryService { static transactional = false }How To Force A Rollback
One of the most important thing to remember is what code to write to force Grails to rollback a current succession of operations. To do that, just raise a RuntimeException or a descendant of it. For example, this will rollback the operation accountFrom.balance = accountFrom.balance - amount
class AccountService { def transferFunds(long accountFromID, long accountToID, long amount) { Account accountFrom = Account.get(accountFromID) Account accountTo = Account.get(accountToID) accountFrom.balance = accountFrom.balance - amount throw new RuntimeException("testing only") accountTo.balance = accountTo.balance + amount } }
But this code will not:
class AccountService { def transferFunds(long accountFromID, long accountToID, long amount) { Account accountFrom = Account.get(accountFromID) Account accountTo = Account.get(accountToID) accountFrom.balance = accountFrom.balance - amount throw new Exception("testing only") accountTo.balance = accountTo.balance + amount } }Meaning the line with subtraction will be committed, but the line with addition will not be reached.