grails - tell me if anything is dirty? - grails

Grails provides an isDirty method that can be called on domain objects. How would one modify the Grails domain model system, such that one could simply call a method, to find out if any domain objects are dirty.
I'm struggling with some "unsaved transient instance" errors that I haven't been able to nail down, and it'd be great to know what's dirty. Is there an elegant way to do this with groovy?

Add this to BootStrap.groovy:
import org.hibernate.Session
Session.metaClass.isDirty = { ->
delegate.persistenceContext.entitiesByKey.values().any { it.isDirty() }
}
This adds an isDirty() method to Hibernate sessions that checks that top-level instances or instances in collections are dirty and you can use it with withSession, e.g.
boolean dirty = SomeDomainClass.withSession { session -> session.isDirty() }
or if you have access to the sessionFactory bean (e.g. from a def sessionFactory dependency injection)
boolean dirty = sessionFactory.currentSession.isDirty()

Based on Burt's answer, one might also do:
Session.metaClass.whatsDirty = { ->
def everythingDirty = []
delegate.persistenceContext.entitiesByKey.values().each { if (it.isDirty()) everythingDirty.add(it) }
return everythingDirty
}

Related

Grails Domain Object Includes Class Attribute

I'm using a domain object to interface with a database in Grails.
When I use the list() method on a domain object to get all of the rows from a database it works great except for one thing. The object that comes back for each row also includes an attribute called "class". I've read some things about creating a custom marshaller that would allow me to remove that attribute from the object. Is that really the best way to not have to return the class attribute?
Thanks!
You can also use JSON.registerObjectMarshaller as below:
// BootStrap.groovy
JSON.registerObjectMarshaller( YourDomain ) {
it.properties.findAll { it.name != 'class' }
}
Refer here for a similar example.
Here's a link to change the way Grails renders JSON by default:
http://grails.org/doc/2.4.4/guide/single.html#defaultRenderers
Just change "NameOfDomainClass" to the class you want to render differently. In this case, the Domain Class.
import grails.rest.render.json.*
beans = {
bookRenderer(JsonRenderer, NameOfClass) {
excludes = ['class']
}
}

Redefine domain class mapping at runtime

I was wondering if there is a way in groovy to change the static mapping section of a grails class at runtime. As of now my domain class looks like this:
class Result {
ObjectId id
String url
def Result(){
}
void addObjectProperty(String key, value){
this[key]=value
}
//No constrains defined yet.
static constraints = {
}
static mapWith="mongo"
static mapping = {
collection "results"
database "test"
}
}
Now lets just say I want to change the mapping section at runtime to:
static mapping = {
collection "xyz"
database "mydb"
}
Now when I call save() on an object it saves the result to mydb in the collection xyz. I bet there is a way in groovy to accomplish just that but since I'm new to groovy I'm having a hard time here ... it would be nice if someone could point me into the right direction.
Thanks a lot...
Note my comment above about the wisdom of doing this. That said, you can replace your mappings at runtime with Groovy's metaclassing functionality.
Result.metaClass.'static'.mapping = {
collection "myCollection"
database "myDatabase"
}
In Grails, the mapping block is a Groovy closure, so you're free to replace it with any other closure object whenever you'd like. This may have crazy unpredictable Hibernate side-effects or do nothing at all, as I do not know when the mapping closure is used to configure Hibernate in the Grails app lifecycle.

How to override the DomainClass.list() in GORM (Grails)

People, I'm facing a problem with grails GORM, my Application is totally dependent of the DomainClass.list() method, it is in all of my create/edit GSPs, but now I need a particular behavior for listing objects. Being more specific I need to filter these lists (All of them) by one attribute.
The problem is I'm hoping not to change all the appearances of these methods calling, so is there a way to customize the behavior of the default list() method ? I need it to function just the way it does, but adding an ending filter.
Maybe you can use hibernate filter plugin (see here). This will allow you to filter all finder methods (including list()) based on a property:
static hibernateFilters = {
enabledFilter(condition: 'deleted=0', default: true)
}
Have you considered using names queries? You could always do something like this:
class DomainClass {
// ... class members
static namedQueries = {
myList { params->
// put your complicated logic here
}
}
}
Then you can just replace your calls to DomainClass.list() with DomainClass.myList.list().

Is it possible to metaprogram named queries onto a grails domain class?

Is it possible to metaprogram named queries onto a grails domain class? If so, how?
Thanks
Domain classes have a namedQueries property that you can use to add your own named queries. If you want to do this using metaprogramming from within a plugin (rather than by editing the domain class directly), you should do it in the doWithDynamicMethods closure of the plugin's descriptor file.
Something like this should work:
class MyPlugin {
def doWithDynamicMethods = { applicationContext ->
application.domainClasses.each { domainClass ->
boolean domainClassFilter = domainClass as Boolean
if (domainClassFilter) {
domainClass.metaClass.static.myNamedQuery = {->
// implementation of your named query goes here. Here is an example implementation
// that returns all instances with status == 'ready'
String simpleClassName = domainClass.simpleName
domainClass.findAll("from $simpleClassName where status = ?", ['ready'])
}
}
}
}
}
This will add myNamedQuery to every domain class in the application that the plugin is installed in. If you only want to add it to some domain classes, then replace the value of domainClassFilter with a more appropriate test.

Grails Plugins Requiring External Relationships

I posted this on the Grails mailing list yesterday and haven't had any hits. Figured I'd try here as well today.
I'm considering writing a grails plugin but this plugin would require some sort of relationship to an account / user object. However, I don't want to force a particular security model on the plugin. For example, say was writing a comment system plugin (I'm not). I'd have a comment object...
class Comment {
String comment
Date dateCreated
// etc etc
}
The comment is missing a couple of things:
Who added the comment
What the comment was added to.
I'd like to first focus on #1. So someone might be using the Spring security plugin and use the default Person object, or maybe they changed that to User. Who knows. Is there any way that anyone can think of to configure that relationship without hard coding it in the plugin?
One thing I've thought about was to have the grails app extend the plugin's domain classes to add this relationship. so I might do something like...
class ArticleComment extends Comment {
static belongsTo = [user:User]
}
But in a larger plugin, that might be a lot of inheritance requirements. Not the end of the world, but just looking for other possible options.
You can use the same technique employed by the Commentable plugin:
The user of your plugin will need to declare a closure in Config.groovy to evaluate the logged user:
grails.myplugin.user.evaluator = { session.user }
And you can use something like this in your plugin's code to call the user configured closure:
def evaluateUser() {
def evaluator = grailsApplication.config.grails.myplugin.user.evaluator
def user
if(evaluator instanceof Closure) {
evaluator.delegate = this
evaluator.resolveStrategy = Closure.DELEGATE_ONLY
user = evaluator.call()
}
if(!user) {
throw new Exception("No [grails.myplugin.user.evaluator] setting defined or the evaluator doesn't evaluate to an entity. Please define the evaluator correctly in grails-app/conf/Config.groovy")
}
if(!user.id) {
throw new Exception("The evaluated user is not a persistent instance.")
}
return user
}
I think you can do it like SpringSecurity do. Instead of let people extend your Comment class, You can write 2 class CommentUser & CommentPlace; then let others extends them. I think it's more simple.

Resources