I have a service method to transfer funds to/from an external system.
it should create a transaction in our system first (so we have a transactionId)
Then we call the external system.
If the external system fails, we need to rollback the transaction, then write a new record in our payment audit log table, regardless of if the call failed or worked.
I cant figure out how to control the transaction in this case.
I understand services are transactional by default.
I assume I could create 3 methods (they are all 1 method now, which doesn't work as I have no control over what gets committed and what gets rolled back)
createPaymentTransaction()
sendToPaymentSystem()
createPaymentRecord()
I need to rollback 1 if 1 fails, and do nothing more.
I need to rollback 1 if 2 fails, but write 3.
I need to write 3 if 1 and 2 works.
I don't know how to annotate these, or how to structure a 4th request to manage the 3.
I'd go with something like this:
package com.myapp
import grails.transaction.Transactional
import org.springframework.transaction.annotation.Propagation
#Transactional
class MyService {
def createPaymentTransaction() {}
def sendToPaymentSystem() {}
#Transactional(propagation=Propagation.REQUIRES_NEW)
def createPaymentRecord() {}
def method4() {
try {
def transactionId = createPaymentTransaction()
sendToPaymentSystem(transactionId)
}
finally {
createPaymentRecord()
}
}
}
By annotating at the class level, we set the defaults for all methods, but can customize as needed, e.g. for createPaymentMethod.
So what will happen is that calling method4 will join an existing transaction, or start a new one if necessary. If there's a problem in either createPaymentTransaction or sendToPaymentSystem then the transaction will be rolled back, but the call to createPaymentRecord will happen because it's in the finally block, and it will run in a separate transaction so it isn't affected by a rollback in the main transaction, and a failure there doesn't affect the main transaction.
If you're not able to use the new grails.transaction.Transactional annotation, use the standard Spring org.springframework.transaction.annotation.Transactional annotation, but you need to make a small change. One of the motivations for the Grails annotation is to provide the same functionality as the Spring annotation, but avoid the problems with calling an annotated method from within the service. The Spring annotation triggers creation of a proxy at runtime which intercepts all calls, manages transactionality for the method, and then calls the real method in the service instance. But with the current code, the call to createPaymentRecord will bypass the proxy (the service instance is just calling itself) and there won't be a new transaction. The Grails annotation rewrites the bytecode to wrap each method in a transaction template which uses the applicable annotation settings (explicit or inferred from a class-scope annotation), so it works correctly internally and externally. If using the Spring annotation, you need to call the method on the proxy, which just involves accessing the Spring bean for this service. Add a dependency injection for the GrailsApplication as a field:
def grailsApplication
and then call createPaymentRecord via
grailsApplication.mainContext.myService.createPaymentRecord()
in the finally block.
By default all methods in a service are transactional, but you can change the behaviour on a method-by-method basis with annotations, e.g.
import grails.transaction.*
// By default all methods are transactional
#Transactional
class MyService {
#NotTransactional
def notTransactional() {
}
// inherits the class-level default
def transactional() {
}
}
See the Grails manual for more details about the transaction annotations.
If you need to manage transactions at a more fine-grained level than per-method, you can use the withTransaction domain class method to manage transactions programatically.
Related
I am trying to implement a search service where i want to do some initial data caching when the service is created. The service implements the spring framework InitializingBean interface, but when I try to use the afterPropertiesSet method to get data from my domains, I get an error stating that GORM is not ready for use. Is there any way to have domain objects available for use in the afterPropertiesSet method? Grails 2.5.0.
I'm not aware of an event or something similar to notify when domain classes have been initialized, but it does happen before BootStrap runs. For cases like this where afterPropertiesSet is too early to do something, you can dependency-inject the service in BootStrap and call your own init method, e.g.
class BootStrap {
def myService
def init = {
myService.init()
}
}
In MyService I have the following:
import groovy.sql.Sql
class MyService {
Sql groovySql
def serviceMethod(){
groovySql.firstRow("some query.....")
}
}
In resources.groovy groovySql inject as follows:
groovySql(groovy.sql.Sql, ref('dataSource'))
This is a Grails 2.4.5 application. Now, the question is when serviceMethod is called, is the connection closed automatically?
Every method in Sql creates and releases resources if necessary.
Under the covers the facade hides away details associated with getting
connections, constructing and configuring statements, interacting with
the connection, closing resources and logging errors.
If you create a Sql with a DataSource, it will get a new connection every time, and close it at the end of the operation.
If we are using a DataSource and we haven't enabled statement caching,
then strictly speaking the final close() method isn't required - as
all connection handling is performed transparently on our behalf;
however, it doesn't hurt to have it there as it will return silently
in that case.
We have decoupled certain events by implementing listeners in exploded plugins (i.e. outside of the core application). Then we can build different implementations. Also, the listeners work must be outside any transaction, as it can take a long time (think connecting to the internet and waiting a long time, and if if fails, it must not rollback anything). The app is a webapp and api running on tomcat.
The problem is setChanged().
Here is a service:
class someService extends Observable {
}
Here is a plugin bootstrap:
SomeService someService
:
someService.addObserver(listenerService)
Here is the listener:
class ListenerService implements Observer {
#NotTransactional
void update(Observable o, Object arg) {
DomainObject domainObject (DomainObject) arg
// do somethign which takes a very log time
}
Here it is being called somewhere (e.g inside a method of someService, or elsewhere)
someService.setChanged()
someService.notifyObservers(someEvent)
I see a big problem here - if the setChanged is called my multiple requests, Each will set changed=true, but the first notifyObserver will set it to false, so when the second notifyObserer executes, it will see changed == false and return.
I see 2 possible solutions:
Put the observer on a domain object, which is not static.
hook into the afterUpdate domain event (not idea as we want to be outside the transaction, but we could start anther thread and do the work asynchronously)
Does anyone know if using the observer on the domain object (and calling setChanged and nofityObservers in some session) is a good solution?
I add two annotations on the service method, after compiled, I found the method were compiled to a new class file, and I decompiled the generated class files and found the #CompileStatic were not work as wished.
Is is right or a bug of grails?
class FoobarService {
#grails.transaction.Transactional
#groovy.transform.CompileStatic
void foobar() {
....
}
}
The grails.transaction.Transactional annotation is a replacement for the traditional Spring org.springframework.transaction.annotation.Transactional annotation. It has the same attributes and features and works essentially the same, but it avoids an unfortunate side effect of using the Spring annotation.
The Spring annotation triggers the creation of a runtime proxy of the annotated class. Spring uses CGLIB to create a subclass of the target class (typically a Grails service) and an instance of the CGLIB proxy is registered as the Spring bean instead of registering a service instance directly. The proxy gets an instance of your service as a data variable.
Each method call is intercepted in the proxy, where it does whatever checks and/or setup is required based on the transaction settings, e.g. joining an existing transaction, creating a new one, throwing an exception because one isn't already running, etc. Once that's done, your real method is called.
But if you call another annotated method with different settings (e.g. the first method uses the default settings from #Transactional but the second should be run in a new separate transaction because it's annotated with #Transactional(propagation=REQUIRES_NEW)) then the second annotations settings will be ignored because you're "underneath" the proxy , inside the real instance of your service that the proxy is intercepting calls to. But it can't intercept direct calls like that.
The traditional workaround for this is to avoid direct calls and instead make the call on the proxy. You can't (at least not conveniently) inject the service bean into itself, but you can access the application context and access it that way. So the call that you would need in that situation would be something like
ctx.getBean('myService').otherMethod()
which works, but is pretty ugly.
The new Grails annotation works differently though. It triggers a reworking of the code via an AST transformation during compilation. A second method is created for each annotated method, and the code from the real method is moved inside there, in a GrailsTransactionTemplate that runs the code using the annotations settings. Once there, the code runs with the required transaction settings, but since every method is rewritten in this way, you don't have to worry about the proxy and where you're calling the methods from - there is no proxy.
Unfortunately there's a side effect that you're seeing - apparently the transformation happens in a way that isn't preserving the #CompileStatic annotation, so the code runs in dynamic mode. Sounds like a bug to me.
According to the official documentation, and the books I have read, services are transnational be default. however, we were getting records committed, even if we immediately throw a RuntimeException.
e.g:
class MyService {
def someMethod() {
new someDomainObject().save(failOnError:true)
throw new RuntimeException("rollback!")
}
}
and calling it thusly:
class myController{
MyService myService
def someMethod() {
myService.someMethod()
}
}
In the above case, after calling the controller which calls the service, then checking if the row was created by attaching to the DB using mysql workbench, the row was indeed committed and not rolled back.
So we next tried this:
class MyService {
static transactional = true
def someMethod() {
new someDomainObject().save(failOnError:true)
throw new RuntimeException("rollback!")
}
}
Same problem.
Next we tried this:
#Transactional
class MyService {
static transactional = true
def someMethod() {
new SomeDomainObject().save(failOnError:true)
throw new RuntimeException("rollback!")
}
}
Finally, this works. However, we dont understand why.
Note:
Grails 2.4.4 using MYSQL:
development {
dataSource {
dbCreate = "create-drop"
url = "jdbc:mysql://127.0.0.1:3306/db"
username = "user"
password = "***"
}
}
Is this normal behavior?
Is #Transactional different to static tranasctional=true?
The Service classes were generated by intellij 14 using the "new groovy class" option from the Services folder in the Grails view. The "new Grails Service" option does not work for us, it just does nothing, so we have to create all groovy classes "by hand" in the right place.
OK, found the cause, or Gotcha:
"Annotating a service method with Transactional disables the default Grails transactional behavior for that service"
So I happened to annotate one of the many methods in the service as #Transactional(propagation=Propagation.REQUIRES_NEW), thinking that the others will retain their default of transactional, but no, if you make any declarations, it removes the transactoinal behavior of all other methods silently, even if you say "static transactional = true"
This appears to be rather dangerous, and from now on, I will annotate every service class with #Transactional to avoid being caught out.
This doesn't make a lot of sense. All of the different variants of the service should function the same. The general logic used is to look for #Transactional at the class level or on at least one method. If you use org.springframework.transaction.annotation.Transactional then a transactional proxy will be created. If you use the newer grails.transaction.Transactional then an AST will rewrite methods to use a transaction template, but the net effect is basically the same. If there are no annotations, then unless you have static transactional = false, then the service is transactional and a Spring proxy is created (the same as if you had included the Spring #Transactional annotation at the class level). static transactional = true is never needed since it's the default; the only way for a service to be completely non-transactional is to include static transactional = false and have no #Transactional annotations.
One thing that could be happening is that the underlying table might not be transactional. Newer versions of MySQL default to InnoDB as the table type, but before 5.5 the default was MyISAM. Grails auto-detects the database and registers a Hibernate Dialect for you, and this works well in most cases except for MySQL + MyISAM. To ensure that you always use InnoDB, specify an appropriate Dialect in DataSource.groovy, e.g.
dataSource {
dialect = org.hibernate.dialect.MySQL5InnoDBDialect
}
This will only help with new tables that are created by Hibernate going forward. Be sure to convert any existing MyISAM tables to InnoDB (although in this case that wouldn't be needed since you're using create-drop).
For anyone coming to this question after years of asking, since Grails 3.1 this has been changed. Today at Grails 5.2.5 Services are NOT transactional by default. The "static transactional" property does nothing anymore. You can make a service transactional by annotating it with #Transactional
More has been said about this change here: https://docs.grails.org/latest/guide/services.html#declarativeTransactions