Grails transactional behavior

In the Grails application, the default behavior of the default service methods is that they are transactional and the transaction is automatically rolled back if an unchecked exception is thrown. However, Groovy cannot force (or reverse) exceptions, so there is a risk that if the service method throws an exception, the transaction will not be rolled back. In this regard, it seems appropriate to annotate each Grails service class.

@Transactional(rollbackFor = Throwable.class) class MyService { void writeSomething() { } } 

Suppose I have other methods in MyService , one of which only reads the database, and the other does not concern the database, are the following annotations correct?

 @Transactional(readOnly = true) void readSomething() {} // Maybe this should be propagation = Propagation.NOT_SUPPORTED instead? @Transactional(propagation = Propagation.SUPPORTS) void dontReadOrWrite() {} 

To answer this question, I think you need to know what my intention is:

  • If an exception is thrown from any method and a transaction is executed there, it will be rolled back. For example, if writeSomething() calls dontReadOrWrite() , and an exception is thrown from the last, the transaction started first will be rolled back. I assume that the attribute of the rollbackFor class is inherited by separate methods, unless they explicitly override it.
  • If transactions fail, no one starts for methods like dontReadOrWrite
  • If the transaction fails when calling readSomething() , the read-only transaction will be started. If a read and write transaction is in progress, it will participate in this transaction.
+6
source share
2 answers

Your code is right as far as possible: you want to use the Spring @Transactional annotation for individual methods in your service class to get the necessary granularity, you are right that you want to SUPPORT for dontReadOrWrite (NOT_SUPPORTED suspends an existing transaction that will not buy you anything depending on what you have described, and will require your software to cycle, so there is no pain for profit), and you are correct that you want the default behavior (REQUIRED) for readSomething.

But it’s important to remember that Spring’s transactional behavior is that Spring implements transaction management by transferring your class to a proxy server that performs the appropriate transaction setup, calls your method, and then does the corresponding -down transaction when control returns. And (critically), this transaction control code is called only when the method is called on the proxy server, which does not happen if writeSomething () directly calls dontReadOrWrite (), as in your first brand.

If a method called by another method requires a different transactional behavior, you have two options that I know of if you want to use Spring @Transactional annotations to manage transactions:

  • Move the method called by another to a different service class, which will be accessible from the original class of service through the Spring proxy.
  • Leave the method it is in. Declare the member variable in your class of service the same type as your class of service interface and make it @Autowired, which will give you a reference to your proxy object of the Spring class. Then, when you want to call your method using different transactional behaviors, do it only for this member variable, not directly, and the Spring transaction code will fire as you want.

Approach # 1 is great if both methods are really unrelated to each other, because it solves your problem without confusing those who finish supporting your code, and there is no way to accidentally forget to call a method with transaction support.

Approach No. 2 is usually the best option, assuming that your methods are in the same service for some reason and that you do not want to separate them. But this is confused with an attendant who does not understand this Spring transaction wrinkle, and you must remember to refer to it this way in every place you call it, so there is a price for it. Usually I’m ready to pay this price so as not to tear my classes of service unnaturally, but, as always, it will depend on your situation.

+4
source

I think you are looking for more competent transaction management, and using @Transactional annotation is the right direction for this. However, there is a Grails transaction processing plugin that can give you the behavior you are looking for. The caveat is that you will need to wrap the service method calls in closing DomainClass.withTransaction and provide the custom behavior that you are looking for as a parameter map for the withTransaction () method.

As a side note, on the backend, this does exactly what you are talking about above, using the @Transactional annotation to change the behavior of the transaction at runtime. The plugin's documentation is excellent, so I don’t think you will find yourself without enough guidance.

Hope this is what you are looking for.

+1
source

Source: https://habr.com/ru/post/906649/


All Articles