how to validate domain class fields during update in grails - grails

There is domain class with natural key defined as below
class InterestGroup {
String intGrp
String name
static constraints = {
intGrp(blank: false, maxSize: 4, unique: true)
name(blank: false, minSize: 10, maxSize: 50)
}
static mapping = {
id generator: "assigned", name: "intGrp", type: 'string'
}
String toString() { "${intGrp}"}
}
I try to modify standard scaffolding to make possible changes of name field.
In standard code there is save() method called and it checks all field, and of course record could not be updated because record with same key exists. When i just assign field value
interestGroupInstance.name = params?.name
name is updated but, not checked against domain class constaint.
What is the best way to realize CRUD operation with natural keys based tables?
Best regards
Krzysiek

I don't think I'm understanding you. What are you trying to do? You are trying to update your group's name and it doesn't seem to make any validation?
The reference documentaion says: "The save method informs the persistence context that an instance should be saved or updated. The save method returns null if validation failed and the instance was not saved and the instance itself if successful.". So it should be calling validate methos when you call save methos on your domain class object.
Could you please post an example?

Related

How to nullify the fields that fail validation and save the domain?

Suppose there is a domain as defined below.
class Book {
String title
Author author
}
Now, i save an instance of this domain. The author domain has some constraints. So, during save the validation for author fails now instead of not saving the whole domain i want to nullify the author(The author can be null) and save the title string as it was. In other words how do i nullify any number of fields whose validation failed and save rest of the properties values? Is there a convenient way to do so? Thanks!
This could be one of following :
In beforeInsert hook do something like below:
def beforeInsert() {
this.validate()
if(this.hasErrors()){
// get all errors and iterate through it and set the field to null for same
}
}
While saving the domain you could use
domain.save(validate:false)
Thanks!!

Validate instance of domain class is unique

I have a Grails application with a bunch of domain classes, some with many fields, some of which have a hasMany relationship with the domain class in question. For this particular application I only have one "constraint", that is that every instance of every domain class must be unique. I don't care if an arbitrary field has the same value across multiple instances of the same domain class, so long as each instance is made unique by the value of some other field in that domain class. So basically I want validation to take place at a domain class instance level instead of the domain class field level. Right now I am doing that by using the very groovy #EqualsAndHashCode annotation to generate my equals and hashCode methods, then calling equals in a custom validator on some arbitrary field of a domain class.
Two questions:
Is there a more efficient way of validating a domain class is unique?
If no, then is there a way I can call my custom validator code on the domain class instance itself instead of going through one of the fields of the domain class instance?
#groovy.transform.EqualsAndHashCode
class MyDomainClass {
String some
int arbitrary
boolean field
static constraints = {
// The field I chose to validate on is irrelivant, I just need to run the validation code **somewhere**
arbitrary validator: { val, obj ->
return !MyDomainClass.getAll().contains(obj)
}
}
}
I should also add I'm looking for a generic (hopefully efficient) way to do this. I realize calling getAll() is very inefficient and instead calling some variant of find or performing an HQL query on the exact fields of each domain class would be much more efficient... it just takes a lot longer to write!
Examples
assert MyDomainClass.getAll().isEmpty() // true
def myDomainClass1 = new MyDomainClass( some: "foo", arbitrary: 1, field: true)
assert MyDomainClass.getAll().contains(myDomainClass1); // false
myDomainClass1.save(flush:true)
def myDomainClass2 = new MyDomainClass( some: "bar", arbitrary: 1, field: true)
assert MyDomainClass.getAll().contains(myDomainClass2); // false. Even though this has the same `arbitrary` value as myDomianClass1, it has a different `some` value which makes it unique.
myDomainClass2.save(flush:true)
def myDomainClass3 = new MyDomainClass( some: "foo", arbitrary: 1, field: false)
assert MyDomainClass.getAll().contains(myDomainClass3); // false. Even though this has the same `some` value as myDomainClass1 and the same `arbitrary` value as myDomainClass1 and myDomainClass2, it has a different `field` value which makes it unique.
myDomainClass3.save(flush:true)
This will ensure the combination of the 3 fields in the domain are unique. This also ensures the constraint is on the database level, instead of just application level.
class MyDomainClass {
String some
int arbitrary
boolean field
static constraints = {
some(unique: ['arbitrary', 'field'])
}
}

How do I change the name of a Grails domain class id field?

I have a Grails 2.2.3 domain class called FundType that I am trying to map to a legacy database table. It has two fields: code and description. I would like the id to be called code anytime I use the domain class and preferably on any of the generated scaffolding. But every time I use the name key on id I get this exception:
| Error 2013-07-24 09:38:44,855 [localhost-startStop-1] ERROR context.GrailsContextLoader - Error initializing the application: Error evaluating ORM mappings block for domain [com.company.scholallow.FundType]: null
Message: Error evaluating ORM mappings block for domain [com.company.scholallow.FundType]: null
This is what my domain class consists of:
class FundType {
String id
String description
static mapping = {
id column: 'fund_code', generator: 'assigned', name: 'code'
description column: 'fund_desc'
}
}
And anytime I am using a FundType instance I would like to call code like fundTypeInstance.code and NOT fundTypeInstance.id. This will make it more user friendly for me because I'm dealing with something called code, not id.
So I would like to know is what I'd like to do possible? And what am I doing wrong in my domain class that is causing this ORM mappings error?
Edit:
Okay, so I changed my domain class to the following and I am getting a FundType not found with ID null error.
class FundType {
String code
String description
static mapping = {
id generator: 'assigned', name: 'code'
code column: 'fund_code'
description column: 'fund_desc'
}
}
I added some sql logging to see what Hibernate is doing and this is what was output: select * from ( select this_.FUND_CODE as RTVFTYP1_1_0_, this_.FUND_DESC as RTVFTYP2_1_0_ from RTVFTYP this_ ) where rownum <= ?
Use String code instead of String id in the domain class.
You are deliberately mentioning to the GORM that I want to use the property code which maps to table column fund_code whose value is assigned as the id (primary key). In that case, you just need to have the property codedefined in the domain class instead of the id.
(I'm answering the fix that worked for me for future use by other programmers)
#dmahapatro was right, I needed to add String code.
It looks like naming the id something different just doesn't play well with Grails dynamic scaffolding. I did some tests and I can still use FundType.get(code) and it will return the object just as if I passed in an id. I can also do FundType.findByCode(code).
It looks like I have to change the scaffolded controller to expect a String id instead of the default Long id. I also have to change the scaffolded list view to send fundTypeInstance.code instead of fundTypeInstance.id to the show controller, but I suspect that adding a getId() that just returns this.code will fix that.

Validation Method in Domain Class

I inherited an app from a consultant, and am trying to follow the code that finds a user from the database via two parameters - Providier and Identifier.
From what I gather, there is a controller method, which in turn calls a service method, and that service method calls what appears to be a validator in the domain class called Login.
I feel like I have reached a dead-end here. Where would I expect to find the actual code for Login.findByProviderAndIdentifier()?
class Login {
String id
String identifier
String password
boolean generated = false
Provider provider
Date lastUpdated
Date dateCreated
Boolean isActive = true
static constraints = {
identifier(blank:false,
validator: {val, obj, errs ->
if (val && obj.provider && !obj.generated) {
def dbLogin = Login.findByProviderAndIdentifier(obj.provider, val)
if (dbLogin && dbLogin.id != obj.id) {
errs.rejectValue("identifier", "unique", [obj.provider.name, obj.identifier] as Object[], "Identifier ${obj.identifier} already exists for provider ${obj.provider.name}")
}
}
}
}
This method is a dynamic finder. The "actual code" for it is generated on the fly by GORM. The way the documentation describes it is:
GORM supports the concept of dynamic finders. A dynamic finder looks
like a static method invocation, but the methods themselves don't
actually exist in any form at the code level.
Instead, a method is auto-magically generated using code synthesis at
runtime, based on the properties of a given class.
So in this case GORM creates a select statement with a where clause that takes a provider id and the identifier value that's being validated.
If you want to see the generated query you can turn on Hibernate SQL logging, or set up log4jdbc.

grails validate method on domain object

I wrote a very simple test case and found that Grails does only a shallow validation when i call validate on a domain object. Is it possible for me to do a deep validation in grails? Can anybody help me?
class Person {
Address address
}
class Address {
String city
}
When i do new Address().validate() it returns false but when i do new Person(address: new Address()).validate it returns true.
While "deep validation" currently isn't documented for the validate() and save() methods, it will be in future (the document states that the documentation has been missing, while being relevant for the complete 1.3.x tree). The documentation on these methods' deepValidate parameter then will state:
#deepValidate# (optional) - Determines
whether associations of the domain
instance should also be validated,
i.e. whether validation cascades or
not. This is #true# by default - set
to #false# to disable cascading
validation.
Tests, however, show that "deep validation" is not performed in any of these cases:
one-to-one associations
one-to-many associations
associated objects assigned using the matching setter
associated objects assigned using the matching addTo*(..) method, e.g., person.addToAddresses(..)
using both the validate() and save() methods,
and also, using both methods with an explicit deepValidate: true parameter
Similar findings have been published at another place, categorizing the "non-behavior" as a "known issue". My own, comprehensive, test cases can be downloaded from here.
The solution, finally, is to manually invoke validation on the child object:
class Person {
Address primaryAddress
static hasMany = [secondaryAddresses: Address]
static constraints = {
primaryAddress validator: {
it?.validate()
}
secondaryAddresses validator: {
it?.every { it?.validate() }
}
}
}

Resources