I am trying to use a common beforeUpdate method by writing in BootStrap.groovy.
def init = { servletContext ->
for (domainClass in grailsApplication.domainClasses) {
if(domainClass.clazz.simpleName == domainName){
domainClass.metaClass.beforeUpdate = {
println "i am here "
def dirtyPropertyNames = this.getDirtyPropertyNames()
println(dirtyPropertyNames)
if(dirtyPropertyNames != null && dirtyPropertyNames.size() > 0) {
for (dirtyPropertyName in dirtyPropertyNames) {
def oldValue = (this.getPersistentValue((dirtyPropertyName)))
def newValue = (this."${dirtyPropertyName}")
}
}
}
}
}
}
But I cannot use this.getdirtyPropertyNames() as it gives an error.
groovy.lang.MissingMethodException: No signature of method:
If it's in the domain itself, this.getDirtyPropertyNames() works fine.
I tried using domainClass.getDirtyPropertyNames() too but it still gives an error.
I am using Grails 4.
I am not sure if you are asking how to accomplish what you want, or why you are getting the error you are getting.
If you want to know how to accomplish what you want, I would use an event listener instead of metaprogramming the method. There are lots of examples out there, https://github.com/jeffbrown/gorm-events-demo/blob/261f25652e5fead8563ed83f7903e52dfb37fb40/src/main/groovy/gorm/events/demo/listener/AuditListener.groovy#L22-L26 is one.
If you are asking why you are getting the error you are getting, the reason is this references the instance of BootStrap, not the instance of your domain class. If you really really want to use the dynamic metaprogramming approach (you shouldn't) then you can solve that particular part of the problem by referencing delegate instead of this.
I hope that helps.
Related
When I do custom rejectValue in a service method grails loses that error(s) between service method and return to controller. This seems to happen when updating a row instance, but not when creating one.
In service
def specialValidation(petInstance){
if(petInstance.petType.requiresStateId && !petInstance.StateId){
petInstance.errors.rejectValue('StateId','StateId required');
}
println petInstance.errors //shows 1 error
return petInstance;
}
In controller
...
petInstance.properties=params;
petInstance=petService.specialValidation(petInstance);
println petInstance.errors //shows 0 errors
How is the error being lost when the instance changes hands from service to controller?
It can be because of transactional service. Service opens separate transaction for each method and clears entities after method end. You can find this mentioned in docs(read the last paragraph of part )
I had the same problem. Than I've added NotTransactional annotation to validation method, and it helped. Errors were saved.
Well I did something simular :
orderService.validate(order, params)
if (order.hasErrors()) {
return render(view: 'create', model: [order: order])
}
In the Service I do some validation like this:
if (end.before(start)) {
order.errors.rejectValue("end", '', 'ERROR');
}
The different to yours is that i didn't set the errorCode but the message at itself, have a look at the rejectValue Methods:
void rejectValue(String field, String errorCode);
void rejectValue(String field, String errorCode, String defaultMessage);
You could also try to use the rejectValue method like me, maybe it helps.
I found you can also avoid this by using
MyDomain.read(id)
instead of
MyDomain.get(id)
We need to be able to rollback a complex transaction in a service, without throwing an exception to the caller. My understanding is that the only way to achieve this is to use withTransaction.
The question is:
why do I have to call this on a domain object, such as Books.withTransaction
What if there is no relevant domain object, what is the consequence of picking a random one?
Below is more or less what I am trying to do. The use case is for withdrawing from an account and putting it onto a credit card. If the transfer fails, we want to rollback the transaction, but not the payment record log, which must be committed in a separate transaction (using RequiresNew). In any case, the service method must return a complex object, not an exception.
someService.groovy
Class SomeService {
#NotTransactional
SomeComplexObject someMethod() {
SomeDomainObject.withTransaction{ status ->
DomainObject ob1 = new DomainObject.save()
LogDomainObject ob2 = insertAndCommitLogInNewTransaction()
SomeComplexObject ob3 = someAction()
if (!ob3.worked) {
status.setRollbackOnly() // only rollback ob1, not ob2!
}
return ob3
}
}
}
The above is flawed - I assume "return ob3" wont return ob3 from the method, as its in a closure. Not sure how to communicate from inside a closure to outside it.
To your primary question: you can pick a random domain object if you want, it won't do any harm. Or, if you prefer, you can find the current session and open a transaction on that instead:
grailsApplication.sessionFactory.currentSession.withTransaction { /* Do the things */ }
Stylistically I don't have a preference here. Others might.
Not sure how to communicate from inside a closure to outside it.
In general this could be hard; withTransaction could in principle return anything it wants, no matter what its closure argument returns. But it turns out that withTransaction returns the value returned by its closure. Here, watch:
groovy> println(MyDomainObject.withTransaction { 2 + 2 })
4
By convention, all withFoo methods which take a closure should work this way, precisely so that you can do the thing you're trying to do.
I'm assuming this question was from a grails 2 application and this problem from 2015 has been fixed before now.
I can't find this in any of the grails 2 documentation, but services have a magic transactionStatus variable injected into their methods. (at least in grails 2.3.11)
You can just leave all the annotations off and use that injected variable.
Class SomeService {
SomeComplexObject someMethod() {
DomainObject ob1 = new DomainObject.save()
LogDomainObject ob2 = insertAndCommitLogInNewTransaction()
SomeComplexObject ob3 = someAction()
if (!ob3.worked) {
transactionStatus.setRollbackOnly() // transactionStatus is magically injected.
}
return ob3
}
}
This feature is in grails 2, but not documented. It is documented in grails 3.
https://docs.grails.org/latest/guide/services.html#declarativeTransactions
search for transactionStatus.
As per Grails documentation
Grails also lets you write your domain model in Java or reuse an existing one that already has Hibernate mapping files. Simply place the mapping files into grails-app/conf/hibernate and either put the Java files in src/java or the classes in the project's lib directory if the domain model is packaged as a JAR. You still need the hibernate.cfg.xml though!
So This is exactley what i did.
I have used java domain model and hibernate.cfg.xml file for mapping. I also use
{DomainName}Constraints.groovy for adding Grails constraints. I also used to add functions to {DomainName}Constraints. For example, below is the content of my EmployeeConstraints.groovy
Employee.metaClass.static.findByDepartment = {depCode ->
createCriteria().list {
department{
inList ('code', depCode)
}
}
}
Now this works fine. But, when i add projection to it(code below), just to get the employee code.
Employee.metaClass.static.findByDepartment = {depCode ->
createCriteria().list {
projections { property('empCode', 'empCode') }
department { inList ('code', depCode) }
}
}
I get the below error..
" No signature of method: com.package.script142113.projections() is applicable for argument types.. "
Can someone point me to whats wrong with the code?
Thanks!
The property projection is used to return a subset of an object's properties. For example, to return just the foo and bar properties use:
projections {
property('foo')
property('bar')
}
You're getting an error because you've called the property method with 2 arguments instead of one.
By the way, I see another potential with your code. Grails will automatically create a dynamic finder findByDepartment that has the same name as the method your trying to add via the meta-class. I have no idea which one will take precendence, but I would suggest you avoid this potential problem and simplify your code, by adding this query using Grails' named query support, and call it something like getByDepartment so that the name doesn't class with a dynamic finder.
The answer by Dónal should be the correct one, but I found a strange behavior with grails 3.1. I got the same message using this call:
Announcement.createCriteria().list {
projections {
property('id')
property('title')
}
} .collect { [id: it['id'], title: it['title']] } // it['id'] not found
I fixed it by removing projections closure:
Announcement.createCriteria().list {
property('id')
property('title')
} .collect { [id: it['id'], title: it['title']] } // got the it['id']
Hope this help.
I'm trying to call a method on a grails service from a controller, but it looks like execution is just skipping the method call.
I've tried debugging the application with a breakpoint inside the method but it is never hit.
My service (generated with grails create-service) is:
class FormatterService {
static transactional = false
def formatList (List<Host>, String fmt) {
OutputObject somePOGO = new OutputObject();
(snip)
return somePOGO
}
}
Then on my controller I have:
class HostController {
def formatterService
def getHostsByLabels = {
(snip)
OutputObject o = formatterService.formatList(someHosts,params.format)
(snip)
}
}
When the formatterService.formatList method should be called in the controller, execution simply skips to the next line, no output is printed to the console and breakpoints within the method are not hit. The OutputObject o reference is null afterward.
What is wrong here? It could be a really basic mistake from my part, but I just can't put my finger on it...
To Me it seems a MetaProgramming Disaster..
Well there are 3 Tests to Debug:
_1) first try to do
println formatterService
println formatterService.getClass()
just to check if its injected bean is the desired one, some plugins sometimes inject beans which overrides the default.
_2) Make sure that the method with a name "formatList" is not injected in your services through metaprogramming by any plugin or core code.
How to test this is simple: Just change the name of the method to some Unrealistic One, ex: "formatListabcdewdw" and then call that one. If it works then its method overriden issue.
and if you are more enthusiastic you can see the metaMethods by
println formatterService.metaClass.methods
_3) just try to do "params.format as String" as the last argument in the method call.\
.
Hope any of these helps, please Do let me know of the findings, i am curious to know.. :)
I found the issue. It has to do with the method signature.
Printing out the thrown exception's message, it says:
No signature of method: hms.FormatterService.formatList() is applicable for argument types: (java.util.TreeSet, java.lang.String) values: (...)
Possible solutions: formatList(java.util.List, java.lang.String)
So, a rookie mistake (wanting to pass a TreeSet for a List) aided by weak typing in Groovy... :P
I've changed the method signature to
def formatList ( items, String fmt) {
and call it as
def activeHosts = ...
OutputObject o = formatterService.formatList(activeHosts, params.format as String)
and now it works.
I'm writing a service that shouldn't have to save anything. It gets some values. Looks up a few things in the database, and then coughs back a response. Is there anything I should do to make the service faster/less overhead? Also whats the best way to pass it something. I usually pass the id and get it again; is that good/bad/dumb?
example
class DoStuffController {
def ExampleProcessingService
def yesDoIt = {
def lookup = "findme"
def theObject = ExampleThing.findByLookie(lookup)
def lolMap = ExampleProcessingService.doYourThing(theObject.id)
if(lolMap["successBool"]){
theObject.imaString = "Stuff"
theObject.save()
}
[]
}
}
service
class ExampleProcessingService{
static transactional = true //???????? false? not-a?
def doYourThing = {theID ->
def returnMap = [:]
def myInstance = ExampleThing.get(theID)
if(myInstance.something)returnMap.put "successBool", true
else returnMap.put "successBool", false
return returnMap
}
}
domain object
class ExampleThing {
String imaString
String lookie
static constraints = {
imaString(nullable:true)
}
def getSomething() {
return true
}
}
bootstrap
import learngrails.*
class BootStrap {
def init = { servletContext ->
def newThing = new ExampleThing(lookie:"findme")
newThing.save()
}
def destroy = {
}
}
Is the an advantage, disadvantage or standard to passing ID and doing get vs. passing the object? Does this change given my case of not going to save anything in the service? Is there something I'm doing glaringly wrong? Do you have a better suggestion for the title?
You've asked a lot of questions, and should split this into several individual questions. But I'll address the overall issue - this approach is fine in general.
There's not a lot of overhead in starting and committing a transaction that doesn't do any database persistence, but it is wasteful, so you should add
static transactional = false
In this case you're using the class as an easily injected singleton helper class. It's convenient to do transactional work in services because they're automatically transactional, but it's far from a requirement.
One thing though - do not use closures in services. They're required in controllers and taglibs (until 2.0 anyway) but should always be avoided in services and other classes. If you're not using the fact that it's a closure - i.e. passing it as an object to a method as a parameter, or setting its delegate, etc. - then you're just being way too groovy. If you're calling it like a method, make it a method. The real downside to closures in services is that when you want them to be transactional, they cannot be. This is because Spring interceptors intercept method calls, not closure calls that Groovy pretends are method calls. So there won't be any interception for transactions, security, etc.