|
Hi,
I'm experimenting with Grails and I'm getting an error that I don't understand. I'm trying to create a model class with the following fields: class Foo { String name String lowerName public void setName(name) { lowerName = name?.toLowerCase() this.name = name } } I generate scaffolded controller and view for this class. When I try to create a new instance of Foo, I get this error: Property [name] of class [class Foo] cannot be null What am I doing wrong? How do I create this sort of functionality? Thanks, -jon |
|
Wouldn't this:
public void setName(name) { lowerName = name?.toLowerCase() this.name = name } be this: public void setName(name) { lowerName = name?.toLowerCase() this.name = lowerName } ??? On Jan 22, 2008 10:52 PM, JBodner <[hidden email]> wrote:
-- Michael Kimsal http://webdevradio.com |
|
No, the behavior that I want is a case-insensitive field (lowerName) to use as a unique key and a case-sensitive field (name) that displays the case that was originally entered. Think of a user account system, where you don't want to have two users, one named "BOB" and another named "Bob", but you want to display back the same case that the user originally entered.
I played around with this a bit more, and from the shell, it was working as I'd expect. But when I try it from the scaffolding, it fails.
|
|
On Tuesday 22 January 2008 10:05:28 pm JBodner wrote:
> No, the behavior that I want is a case-insensitive field (lowerName) to use > as a unique key and a case-sensitive field (name) that displays the case > that was originally entered. Think of a user account system, where you > don't want to have two users, one named "BOB" and another named "Bob", but > you want to display back the same case that the user originally entered. > > I played around with this a bit more, and from the shell, it was working as > I'd expect. But when I try it from the scaffolding, it fails. > Try this: void setName(name) { if (name == null) return lowerName = name?.toLowerCase() this.name = name } --------------------------------------------------------------------- To unsubscribe from this list please visit: http://xircles.codehaus.org/manage_email |
|
Nope, that didn't do it.
I did a bit of digging, and it seems there's a problem with the generated constructors and overriding setter methods on domain objects. The save() method uses: def foo = new Foo(params) So I tried this out in the grails shell ("go" lines removed for brevity): groovy> params = [name:'Foo', lowerName:''] ===> {name=Foo, lowerName=} groovy> x = new Foo(params) ===> Foo : null groovy> x.name ===> null groovy> x.lowerName ===> null groovy> y = new Foo() ===> Foo : null groovy> y.name = 'Foo' ===> Foo groovy> y.name ===> Foo groovy> y.lowerName ===> foo groovy> z = new Foo(name:'Bar') ===> Foo : null groovy> z.name ===> null groovy> z.lowerName ===> null So, is there an automatic way to get Grails to do the right thing here, or do I need to override the constructors, too? Is this considered a bug or a feature?
|
|
On Tuesday 22 January 2008 10:30:38 pm JBodner wrote:
> Nope, that didn't do it. > Sorry - I meant to put a println in there also - so that when you submitted via the scaffolding, you could watch your log and see what was going on if you hadn't already done so. void setName(name) { println "name: ${name}" if (name == null) return lowerName = name?.toLowerCase() this.name = name } Have you tried with an actual controller (vs. the scaffolded controller)? > I did a bit of digging, and it seems there's a problem with the generated > constructors and overriding setter methods on domain objects. The save() > method uses: > > def foo = new Foo(params) > > So I tried this out in the grails shell ("go" lines removed for brevity): > > groovy> params = [name:'Foo', lowerName:''] > ===> {name=Foo, lowerName=} > groovy> x = new Foo(params) > ===> Foo : null > groovy> x.name > ===> null > groovy> x.lowerName > ===> null > groovy> y = new Foo() > ===> Foo : null > groovy> y.name = 'Foo' > ===> Foo > groovy> y.name > ===> Foo > groovy> y.lowerName > ===> foo > groovy> z = new Foo(name:'Bar') > ===> Foo : null > groovy> z.name > ===> null > groovy> z.lowerName > ===> null > > So, is there an automatic way to get Grails to do the right thing here, or > do I need to override the constructors, too? Is this considered a bug or a > feature? > > Corey-24 wrote: > > Try this: > > > > void setName(name) { > > if (name == null) return > > lowerName = name?.toLowerCase() > > this.name = name > > } --------------------------------------------------------------------- To unsubscribe from this list please visit: http://xircles.codehaus.org/manage_email |
|
In reply to this post by JBodner
On Tuesday 22 January 2008 10:30:38 pm JBodner wrote:
> Nope, that didn't do it. > I set up a test app, and was able to reproduce what you were seeing. Here's the fix: void setName( String name ) { lowerName = name?.toLowerCase() this.name = name } Cheers! --------------------------------------------------------------------- To unsubscribe from this list please visit: http://xircles.codehaus.org/manage_email |
|
Thanks; that did it. Any idea why the type is needed? For a standard Groovy bean, it isn't. Is this documented somewhere? I haven't bought the Grails book yet; I'm waiting for the 2nd edition.
|
|
Looks to me like the original problem was down to constraints? By default your bean properties cannot be null.
Either add this to the domain static constraints = { name(nullable:true) } or specify a name when you create the object Foo myFoo = new Foo(name:"MyName") Jason On Jan 23, 2008 1:24 PM, JBodner <[hidden email]> wrote:
|
|
Nope, it isn't a constraints problem. It's a method dispatch bug, IMHO. I did try, from the shell, exactly the constructor you mention:
groovy> z = new Foo(name:'Bar') ===> Foo : null groovy> z.name ===> null groovy> z.lowerName ===> null When the type is specified for the setter, the GORM domain object dispatches correctly. When the type is not specified, it doesn't. For standard Groovy Beans, this will work whether or not the type is specified on the setter.
|
|
I have a service that updates the database. Of course, I have a test for that service. But, when I run the service test with: $ grails test-app MyServiceTest Grails rollbacks my transaction -- even if I use flush. Why? Anyway, here's what I did. import org.hibernate.SessionFactory import org.hibernate.Transaction class MyGrailsAppServiceTests extends GroovyTestCase { MyGrailsAppService myGrailsAppService SessionFactory sessionFactory Transaction tx void setUp() { tx = sessionFactory.openSession().beginTransaction() } void tearDown() { tx.commit() } void testSomething() { myGrailsAppService.doSomethingThatUpdatesDomainObjects() } } |
|
In tests, Grails always rollback if I am correct, but there is now a way to disable this - search the JIRA for something about "grails 7* slower", I remember reading that bug and this is what prompted GR to provide a way to run tests without rollbacks.
Jean-Noël On 1/23/08, [hidden email] <[hidden email]> wrote:
|
|
In reply to this post by JBodner
On Wednesday 23 January 2008 06:24:09 am JBodner wrote:
> Any idea why the type is needed? For a standard Groovy bean, it isn't. > Actually I don't know for certain why. Without specifying type, groovy of course defaults to Object: void setName(name) == void setName (Object name) I theorize that explicit/specific type is needed for whatever meta binding grails provides which is necessary for the hibernate interface. > Is this documented somewhere? I haven't bought the Grails book yet; > I don't know it it's documented somewhere else, but it's not explained in the Grails book, or the User Guide. --------------------------------------------------------------------- To unsubscribe from this list please visit: http://xircles.codehaus.org/manage_email |
|
In reply to this post by JBodner
On 23/01/2008, at 11:24 PM, JBodner wrote: > Any idea why the type is needed? Because groovy automatically generates getters/setters for you. Consider this ... class T { String s } Is really class T { private String s public void setS(String s) { this.s = s } public String getS() { return this.s } } So if you add an untyped setter, you end up with class T { private String s public void setS(String s) { this.s = s } public void setS(Object s) { /* whatever */ } public String getS() { return this.s } } So at despatch time,the closest match will be selected which if the value is a string will be the setter that takes a string. This is not a bug, just something to be wary of. LD. --------------------------------------------------------------------- To unsubscribe from this list please visit: http://xircles.codehaus.org/manage_email |
|
Thanks for the response, but I don't think your explanation is right.
Try this groovy code in the shell: class T{ String s void setS(s) { println "in here" this. s= s*2 } } t = new T() t.s = "Foo" t.s If you were correct, then you would not see the print statement in the custom setS, and the value for t.s would be Foo. Instead, you will see the print statement, and the value will be FooFoo. At least, I did on my computer with Java 6 and Groovy 1.5.1. That's because groovy does not generate a getter or setter if you provide one, types specified or not. Furthermore, if you were correct about the behavior in the GORM-backed Grails domain objects, then we would see the automatically generated methods invoked when the type isn't specified on the parameter. We're not; instead, we're seeing nothing invoked. The Grails/GORM behavior (type needs to be specified in overridden setters or no setter is invoked at all) feels like a bug to me, or at least a pothole. Time to start looking through the GORM source code...
|
|
On 24/01/2008, at 8:52 AM, JBodner wrote: > If you were correct, then you would not see the print statement in the > custom setS, and the value for t.s would be Foo. Instead, you will > see the > print statement, and the value will be FooFoo. At least, I did on my > computer with Java 6 and Groovy 1.5.1. That's because groovy does not > generate a getter or setter if you provide one, types specified or > not. Good points, statement retracted :) LD. --------------------------------------------------------------------- To unsubscribe from this list please visit: http://xircles.codehaus.org/manage_email |
|
My suspicion is, that this has nothing to do with GORM in itself but with Hibernate.
Coming from a strict Java-background, Hibernate will certainly search for a setter method with type-information like void setString(String s) Cheers Christian
|
|
On Wednesday 23 January 2008 04:05:12 pm Christian Laakmann wrote:
> My suspiscion is, that this has nothing to do with GORM in itself but with > Hibernate. > +1 --------------------------------------------------------------------- To unsubscribe from this list please visit: http://xircles.codehaus.org/manage_email |
|
In reply to this post by Christian Laakmann
Yeah, I was wondering about that, too, but I don't think Hibernate is involved when doing a
x = new Foo(name:"Test") That will fail to assign a value to name if you override setName in Foo, and don't specify a type on the parameter. And again, note that this will work correctly with a standard Groovy bean. I haven't checked the GORM source code yet to see what's going on, so I'm hesitant to guess, but it looks like there's a difference in behavior in the constructors of GORM beans and POGOs.
|
| Powered by Nabble | Edit this page |
