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.
Related
I was using a grails CommandObject to store all the information for a search page. Something Like
class SomeSearchCommand {
User user
...
Integer max=100
Integer offset=0
}
And then in the controller
class SomeThingController {
def search(SomeSearchCommand c) {
// build a query based SomeSearchCommand
Something.where {
if (c.user) user==c.user
...
}.list(max: c.max, offset: c.offset)
}
}
This appears to work well, and when you call the action with
/someThing/search?user=3&offset=100
It finds all the "SomeThing" objects that belong to user 3.
But while playing around with the code I found out that if you change the url to
/someThing/search?user.id=3&user.name=ichangedyourname&offset=100
Then the User object inside of the domain object would be loaded with id=3 from the database, and the user.name property would be changed. The User object is now marked as dirty and has been added to the session. If you happen to execute some code that calls a flush (or have flushMode set to Auto), then the change to the user object is persisted.
I found a workaround by adding a #BindUsing around the user object. This seems to disable the deep binding on the user object and only allows binding by the parameter of user and not user.id
class SomeSearchCommand {
#BindUsing({obj, source ->
if (source['user'] instanceOf User) return source['user']
if (source['user'] instanceOf serializable) return User.get(source['user'])
})
User user
...
Integer max=100
Integer offset=0
}
Another workaround is to just not use the User Domain Object in the CommandObject.
class SomeSearchCommand {
String userId
User getUser() {
return User.get(userId)
}
...
Integer max=100
Integer offset=0
}
This means the URL request has to change to /some/search?userId=3&offset=100
But user is still a property on the command object that returns the typed object.
I first noticed this using grails 2.3.11 (where flushMode is set to auto), so that the inner domain object was always being written to the database.
In grails version after 2.4 the flush mode is set to Manual, so the modified domain object only gets persisted of the session is flushed.
I don't see any examples in the grails docs about putting a domainObject inside a commandObject. It always seems to be basic types.
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'])
}
}
The #Query on the property retrieves the values only if I retrieve the entity from the DB.
#NodeEntity
public class Team
{
#GraphId
private Long nodeId;
#RelatedTo (type = "PREVIOUSLY_KNOWN_AS")
private Team previouslyKnownAs;
#Query ("START t=node({self}) MATCH t-[:PREVIOUSLY_KNOWN_AS]-other RETURN other")
private Iterable<Team> aliases;
}
The below test works only if I uncomment the line to read it explicitly from the db. Why is it necessary? I see the query being run after the save(t) but the alias field is null if I doesn't read it from DB by uncommenting the line
#Test
public void alias()
{
Team t = new Team();
t.setName("Alpharetta One");
Team prev = new Team();
prev.setName("Previous Name");
teamRepo.save(prev);
t.setPreviouslyKnownAs(prev);
teamRepo.save(t);
//t = teamRepo.findOne(t.getNodeId());//only works if I uncomment
assertNotNull(t.getAliases());
}
Try
t=teamRepo.save(t);
I dont think that the save operation will update the POJO which you give to it, while the returned Object should be a managed enttiy.
The key lies in the reference documentation
The #Query annotation leverages the delegation infrastructure supported by Spring Data Neo4j.It provides dynamic fields which, when accessed, return the values selected by the provided query language expression.
Since it is a dynamic field, the value isnt instanciated but instead fetched from the DB every time the get method is called. To do this, a proxy object has to be used. However there is no way for SDN to change your t Object reference to the proxy object, and thats why its not working, if you are not using the entity returned by save().
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() }
}
}
}
In Domain class FooReward I added a method
int getQuantity() {
FooRewardAssignment.countByReward(this)
}
Usage in GSP should be fooRewardInstance.quantity, but that errors on startup (in bootstrap) with message that it has no setter method found.
If I change it to
Set<FooRewardAssignment> getListOfFoos() {
FooRewardAssignment.findAllByReward(this)
}
and change the usage in GSP to be badeRewardInstance.listOfFoos.size(), it works and shows me how often a special FooReward is assigned.
Any idea what is wrong in version 1?
Two workarounds for this problem:
Changing the return value to def:
def getQuantity() {
FooRewardAssignment.countByReward(this)
}
Or by adding transients list:
static transients = ['quantity']
int getQuantity() {
FooRewardAssignment.countByReward(this)
}
Sometimes GORM create column in tabel on the basis of setters and getters method inside domain class. And in this situation GORM want add column like 'quantity' but the error occur because GORM see only getter, and don't see setter. So we can say that we don't want create this column in database (static transients) or we can set return value as def - but don't ask me why 'def getters' are not taken into account by GORM ;)