Is it a bad practice to use domain objects in Sets or as keys in Maps?
In the past I've done things like this a lot
Set<Book> someBooks = [] as Set
someBooks.addAll (Book.findAllByAuthorLike('%hofstadter%'))
someBooks.add (Book.findByTitleLike ('%eternal%'))
However I have noticed that I often encounter problems when findAllByAuthorLike might return a list of Hibernate Proxy objects com.me.Book_$$_javassist_128 but findByTitleLike will return a proper com.me.Book object. This causes duplicates in the set because the real object and the proxy are considered not equal.
I find I need to be extremely careful when using Sets of domain objects like this, and I get the feeling it might be something I should not be doing in the first place.
The alternative is of course to use a set/map of id's, but it makes my code verbose and prone to misunderstanding
Set<Integer> someBooks = [] as Set // a set of id's for books
#Burt: I thought Grails domain classes already did this, at least so that equals/compare was done on class/id's rather than the object instance. Do you mean a special comparator for hibernate proxies?
return (this.class == obj.class && this.id == obj.id) ||
(obj.class == someHibernateProxy && this.id == obj.id)
It's not bad practice at all, but just like in a non-Grails application you should override equals and hashCode if you'll be putting them in hash-based collections (HashSet, HashMap, etc.) and also implement Comparable (which implies a compareTo method) if you're going to use TreeSet/TreeMap/etc.
Proper implementation of equals() and hashcode() in a Hibernate-backed situation is far from trivial. Java Collections require that hashcodes of objects and behaviour of equals() don't change, but the id of an object can change when it's a newly created object, and other fields can change for a wide variety of reasons. Sometimes you have a good unchangeable business id that you can use, but quite often that's not the case. And obviously default Java behaviour is also not suitable in a Hibernate situation.
The best solution I've seen is described here: http://onjava.com/pub/a/onjava/2006/09/13/dont-let-hibernate-steal-your-identity.html?page=2
The solution it describes is: initialize the id as soon as the object is created. Don't wait for Hibernate to assign an id. Configure Hibernate to use version to determine whether it's a new object. This way, id in unchangeable, and can be safely used for hashcode() and equals().
Related
I've got a domain called Planning that has a hasMany of another domain called Employee included in it. I'm trying to do a findAll of these plannings where the plannings contain a particular employee and I can't get it to work.
I'm trying to do it like so, my print statements do print the contains as true
plannings = plannings.findAll{planning->
if(employee) {
log.info("find plannings with employee ${employee} ${planning.employees.contains(employee)}")
planning.employees.contains(employee)
}
}
I'm not doing this as a Hibernate query as this broke the application in another weird way. This code is executed in a for each and for whatever reason that causes some weird behavior with Hibernate.
The closure must return a boolean value - see http://docs.groovy-lang.org/latest/html/groovy-jdk/java/util/Collection.html#findAll(groovy.lang.Closure)
This should work (not tested):
plannings = plannings.findAll{planning-> planning.employees?.contains(employee)}
BTW: I wouldn't assign the filtered list to the origin plannings list. Extract a a new expressive variable like planingsOfEmployee or something similar.
Without more relevant details around your problem (what's the weird behavior? log traces? hibernate mappings?, etc.) all we can do is to speculate; and if I have to do so, I would say that most likely:
The employee object you are using for comparison is a detached one.
The employee object does not override meaningfully equals and hashCode
You use using this detached employee to do comparisons against against persisted employees (using planning.employees.contains(employee)) found inside planning
Under these circumstances the comparisons will never be true even when they may represent the same objects. If this is your case, you must either:
Use a persisted employee object to do the comparisons.
Or, implement equals and hashCode semantically meaningful for Employee
Hope this helps.
I understand how to apply Spring security annotations to methods that are passed the domain objects. For example, the following works:
#PreAuthorize("hasPermission(#node, 'write')")
void update(Node node);
How ever, I have another method shown below that I am trying to secure:
void delete(String nodeName)
Since domain object is not available to this method, I am not sure how to use "hasPermission" with this one. Any help is greatly appreciated. I am open to customizing the ACL implementation though in this particular case, any such customization should work with not just "Node" object but all other domain objects as well.
Thanks,
Raghu
In this case, you're deleting by name as opposed to by identity. The way you do that is you look up the object you wish to delete and then, once you have it, pass it to a real deletion method that follows the pattern of the first operation in your question. The check whether the object can be deleted by the user is done after lookup. You get a pattern a bit like this:
void delete(String name) {
Node node = nodeFinder.lookup(name);
if (node == null)
throw new NoSuchNodeException(name);
underlyingEngine.delete(node);
}
The nodeFinder and (especially) underlyingEngine should be injected beans so that Spring can wrap them with the required security checks. (The nodeFinder checks should be whether the user is allowed to know about the named node at all, the underlyingEngine should focus on whether the particular operation — deletion in this case — is permitted.)
Note that you can also apply the #PreAuthorize directly to the Node (provided you decide to make them into beans) through the use of #this in the expression.
I tried to serialize grails domains classes to Maps or similar in order to be able to store it in memcached.
I want to be able to read the objects only, I don't need gorm crud. Only to read them without breaking the kind of interfaces they have.
For instance: I could convert domains to maps, becouse it wouldn't break the interface for access like .<property> or .findall or similar
First I tried to do a manual serialization but it was very error prone. So I needed something more general.
Then I tried to serialize as a map with a grails codec.
Here is the testing repo.
Here is the snippet.
But I get StackOverFlowException.
I also tried to mark all the domains as Serializable but I need to reattach every domain when I bring them back from memcached to avoid hibernate errors like org.hibernate.LazyInitializationException: could not initialize proxy - no Session
Do you know a way to achieve this?
Is very frustrating to google search for something like this "storing domain classes in memcached" and find out is not a common problem.
I haven't see an out-of-the-box solution for doing this, but if you wanted to keep it generic you could do it manually (and consistently) like this:
def yourDomainInst = DefaultGrailsDomainClass(YourDomainClazz)
List listOfOnlyPersistantProperties = yourDomainInst.persistantProperties
def yourNewMap
yourObjects.each { obj ->
listOfOnlyPersistantProperties.each { prop ->
def propName = prop.name
yourNewMap.put(propName, obj."$propName")
}
}
Something like that should work. Note there's probably a hundred errors because I can't try it out right now, but that is the general idea.
Have a look at: Retrieving a list of GORM persistent properties for a domain
I need to choose a set of validation rules at save time. Ideally I would like to be able to define more than one set of constraints in the domain class:
class Account {
...
static constraints = { ... }
static constraints2 = { ... }
}
I know I can do my own validation in code (account.errors.rejectValue(...) etc) but I am looking to save some time by using the built in Grails validation if possible.
This is what Command Objects are for.
The reason you can't just swap out validation is that the validation also helps define the database structure, like setting a field to be non-null, or non-blank, or having a specific length. Turning off "non-null", for example, could break the database structure.
Command objects, on the other hand, are designed to have their own unique set of validation rules. It's not the cleanest method (now you are tracking the same structure in more than one situation), but it's better in a lot of ways.
It allows you to securely accept input parameters without worrying that something that shouldn't be processed gets set.
It creates cleaner controllers, since the validation logic can all be handled within the Command Object, and not sprinkled through the controller method.
They allow you to validate properties that don't actually exist on the object (such as password verification fields).
If you think you really need multiple sets of constraints, you are most likely either a) over-complicating the domain object to represent more information than it really should (maybe break it up into several one-to-one related objects), or b) incorrectly setting the constraints in the first place.†
In my experience, usually it's a that happens when you feel the need to swap out constraints.
† That being said, it can (rarely) make sense. But Command objects are the best solution in this situation.
you can use the validator constraint, see: http://www.grails.org/doc/latest/ref/Constraints/validator.html
within one validator constraint you can perform more checks than one and return diffrent error codes, e.g.
static constaints = {
name: validator { value, obj ->
if (value.size() > 10)
return [invalid.length]
else if (value.contains("test"))
return [invalid.content]
return true
}
in message.properties
domainClass.propName.invalid.content = ...
}
Just a few high-level, hopefully very quick questions:
1) If I have a class A with a single field x, is constructing it
def A = new A(x:someVal, y:someVal)
totally fine?
2) Related, is the following a good way to copy relevant parts of a command object into a domain object?
def domainObject = new DomainObject(commandObject.properties).
Where command object has extra properties. Or should it be done instead:
def domainObject = new DomainObject()
domainObject.properties['prop1', 'prop2', ...] = commandObject.properties
or ?
Thanks
For the first question, it's important to distinguish between a vanilla groovy object, and a grails domain object. Groovy objects with throw a MissingPropertyException. Grails domain objects will silently ignore extra properties.
Regarding the second question, initializing grails domain objects with a command object is a common pattern, and generally ok. Params can be a little bit more dangerous. A malicious user can put anything into params so it's best to explicitly spell out what properties you want to assign. Otherwise, things like timestamps and users, or even non-mapped columns like injected spring beans could be affected.