Creating One-To-Many Records In A Grails Service

A parent can have many children. This article explains how to write a service such that, if after adding a parent there is an error when adding a child, the entire transaction is rolled back. For example, add parent p1, successfully add child c1, then when adding child c2 an error occurs, both p1 and c1 should be rolled back.

Note that this requires Grails 1.2-M4.

Here are the domain objects:

1
2
3
4
5
6
7
8
9
10
class Parent {
 
static hasMany = [ children : Child ]
 
String  name
 
static constraints = {
name(blank:false,unique:true)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Child {
 
static belongsTo = Parent
 
String name
 
String pet
 
Parent parent
 
static constraints = {
name(blank:false,unique:true)
pet(blank:false)
}
}

Now if you do:

1
grails generate-all *

You’ll get separate forms that do work. But if we were talking about an invoice application, for example, you’d really want to have 1 data entry form. That’s what we do here:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
 
<%@ page contentType="text/html;charset=UTF-8" %>
 
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta name="layout" content="main" />
    <title>Sample title</title>
  </head>
  <body>
    <h1>Add A Record</h1>
 
  <g:if test="${flash.message}">
    <div class="message">${flash.message}</div>
  </g:if>
  <g:if test="${flash.errors}">
    <div class="errors">
      <ul>
      <g:render template="renderflasherrors" collection="${flash.errors}"  as="list"/>
      </ul>
    </div>
  </g:if>
 
 
  <g:form action="add" name="doAdd">
    <table>
      <tr>
        <td>
          Parent Name
        </td>
        <td>
          Child Name
        </td>
        <td>
          Pet Name
        </td>
      </tr>
      <tr>
        <td>
      //<g:textField name="parentName" value="${flash.params?.parentName}" />
       <g:textField name="parentName" value="${params?.parentName}" />
      </td>
      <td>
      //<g:textField name="childName" value="${flash.params?.childName}" />
       <g:textField name="childName" value="${params?.childName}" />
      </td>
      <td>
      //<g:textField name="petName" value="${flash.params?.petName}" />
      <g:textField name="petName" value="${params?.petName}" />
      </td>
      </tr>
      <tr><td><g:submitButton name="update" value="Update" /></td></tr>
    </table>
  </g:form>
</body>
</html>

The tricky part in this was figuring out how to create both records in the controller and return any errors and the entered data back to client. More on that later, but for now just notice lines 13-16 where we render the errors. This is a little different than in the generated forms. There you can check if the object has errors and display them.

But in this example, all of the objects are created in a service, so the .gsp (and the controller) don’t know anything about the objects. So we end up passing the errors back in a collection flash.errors.

Now look at lines 37, 40 and 43 and compare those to similar lines in the generated forms. There you use a bean to get the value from. But here, we just redisplay the params which contains everything the user originally entered. Actually, the params are being stored in flash. For some reason, the form fields end up getting dropped from the params by the time we get back to action:show.

Here’s the controller:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class AddrecordController {
 
    def addRecordsService
 
    def index = {
        redirect action:"show", params:params
    }
 
    def add = {
        println "do add"
 
        try {
            addRecordsService.addAll(params)
        } catch (java.lang.RuntimeException re){
 
          def errorMessages = re.errors.allErrors.collect { g.message(error:it) }
            println  errorMessages
            flash.message = g.message(code:"addRecord.save.failed")
            flash.errors = errorMessages
           //flash.params = params
            //render action:"show",  params:params
            render view: "show", params:params
            return
        }
        redirect action:"show"
 
    }
 
    def show = {}
 
}

Make sure you render the view not the action and follow it with a return.

If there’s an error in the service a RunTimeError get’s thrown. As of Grails 1.2-M4, the exception thrown is a grails.validation.ValidationException, which has the list of errors, so at line 18 we pass each error to a message tag to get the readable messages [1].

Finally, here’s the service:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class AddRecordsService {
 
    static transactional = true
 
    def addAll(params) {
        println "add all"
        println params
        def Parent theParent =  addParent(params.parentName)
        def Child theChild   = addChild(params.childName,params.petName,theParent)
 
        println theParent
        println theChild
 
    }
 
    def addParent(pName) {
        println "add parent: ${pName}"
        def theParent = new Parent(name:pName)
        theParent.save(failOnError:true)
 
        return theParent
    }
 
    def addChild(cName,pName,Parent theParent) {
        println "add child: ${cName}"
        def theChild = new Child(name:cName,pet:pName,parent:theParent)
 
       theChild.save(failOnError:true)
 
        return theChild
    }
 
}

Grails 1.2-M4 added failOnError:true as an option to save(). So now the cause of an error is returned.

Thanks to everyone who answered my questions on this ([1],[2],[3],[4]).

[1]Re: How To Parse Runttime Exception Error (GRAILS-5146)

[2]How To Make Transactions Work In Grails

[3]How To Know The Cause Of A Validation Error

[4]Render Errors From A Service

Download transtest

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Using Google Docs For Software Localization

Google provides some truly amazing automatic translation tools. And now translation is included in Google Docs. This turns out to be a powerful tool for software localization.

My current and probably long-term favorite development environment is Grails. Grails has great localization support build into it. An important piece of that is the messages.properties file, where you store all of your values that you want to be localized.

Actually, there can be lots of properties files. Grails ships with:

  • messages_de.properties
  • messages_es.properties
  • messages_fr.properties
  • messages_it.properties
  • messages_ja.properties
  • messages_nl.properties
  • messages_pt_BR.properties
  • messages_ru.properties
  • messages_th.properties
  • messages_zh_CN.properties

For example, messages_fr.properties is for French.

All the base files have translations of commonly used words. As you add more functionality to your application, you of course need more labels and buttons, etc. So you add them to your messages.properties file. Then all you have to do is translate your file into all these and other languages. The good news is that you can use Google Docs to get the job done. Or at least it can give you a head start on the translation process.

Just load your properties file into Google Docs and select Tools|Translate.

translate

translate

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Calling A Service From The Grails Console

I’m doing my first project in Grails and intend to blog some of thing things I’m learning.

During development, the grails console is very useful for testing specific pieces of code without having to go through your whole system to get to that particular bit of code.

For the most part, you can just enter grails commands and hit execute. But apparently dependency inject doesn’t happen in the console. So there’s a bit of a trick to execute a service from the console.

If you had a service named MySecretService with a function named secretSquirell, you could access it via dependency injection in a domain class, a controller or another service by first adding

def mySecretService

then later you can say
def result = mySecretService.secretSquirell()

But to test in the console you need to do this:

def service = ctx.getBean("mySecretService")
def result = service.secretSquirell()

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Great Expectations

I’m been meaning to write this since we got into our new home here in Nampa, ID. I see it’s been months since I’ve posted anything of my thoughts here. I do update Twitter just about every day though…

When we first got here we thought we’d have to rent something. Then with how the market was it looked like we might be able to buy something small after all. Then God totally blew us away with the house he provided for us. It’s actually the nicest home we’ve ever had. It totally exceeded my expectations. I don’t think I’ve ever been so thankful.

Then I realized something. The reason I was so thankful is because my expectations were blown away. And I also realized that the reason I’m not normally so thankful is that I really expect way too much.

William Carey, the Father of Modern Missions said, “Expect great things from God, attempt great things for God.” I want to have the kind of expectations that Carey had. If you know about his life as the first missionary to India, his expectations of God were just that God would use him in the circumstances he was in. From what I can tell, he had no expectations about what those circumstances would be. I think Carey learned the secret to contentment that Paul talks about. Perhaps that’s also the secret to thankfulness.

From the fullness of his grace we have all received one blessing after another.  — John 1:16

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

If I Had Another Son, I’d Name Him Peter

I love Peter. He’s such an encouragement. Or rather what God did in his life is an encouragement. If God gave us another son, I’d probably name him Peter.

Playwright George Bernard Shaw said, “A life spent making mistakes is not only more honorable, but more useful than a life spent doing nothing.” He easily could have been talking about Peter. Peter was never afraid to make mistakes, and that led to a number of important learning experiences throughout his days with Jesus.

Read more here.

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

God Can Get ‘er Done

From today’s devos:

Jonah didn’t reach the pinnacle of godly behavior or attitude, but God still used him to deliver His message to Nineveh, and to us. His saving grace was that he recognized his need for God, and he called on Him for help. If you ever doubt your ability to accomplish what God has called you to do, call out to Him for help. Don’t rely on your strength alone to achieve great things for God. If He can send a fish to save a reluctant prophet, He can provide for you.

Relying on God instead of on myself is a major thing He’s been teaching me through deputation. I’ve been accused of arrogance and pride from time to time. Hopefully I’m making progress, but I really don’t know. It’s hard for me to see specifics in my life that I need to change in this area. But I am learning that at least that “without him I can do nothing.”

Please pray for me. I’ve been discouraged lately and this is seems to be some how part of that.

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

More Important Matters

This verse was part of my devotions this morning:

“Woe to you, teachers of the law and Pharisees, you hypocrites! You give a tenth of your spices—mint, dill and cummin. But you have neglected the more important matters of the law—justice, mercy and faithfulness. You should have practiced the latter, without neglecting the former” (Matthew 23:23).

I had never noticed before that God calls “justice, mercy and faithfulness” to be “more important matters” than tithing.

We’re going to have to find a new church once we move to Idaho. I’d really like to be part of a church that was a “sending” church for us; that is a church that provides in the neighborhood of 20% of our support. But now I’m going to be looking for a church that emphases the “more important matters”.

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Core Group Video

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]