简体   繁体   中英

grails how to disable transactional for a method

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)

  1. createPaymentTransaction()
  2. sendToPaymentSystem()
  3. 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, eg 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, eg

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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM