micronaut-data: javax.transaction.Transactional does not work at the individual method level

Expected Behavior

That methods carrying the javax.transaction.Transactional annotation run in a transaction with a configuration matching the parameters specified in the annotation and that methods not carrying the javax.transaction.Transactional successfully run outside a transaction context.

javax.transaction.Transactional is clearly intended to be a method level annotation, so it must be possible to non-transactional methods on a class.

Actual Behaviour

Methods not carrying the javax.transaction.Transactional annotation fail with:

io.micronaut.transaction.exceptions.NoTransactionException: No current transaction present. Consider declaring @Transactional on the surrounding method

Steps To Reproduce

As an example, I modified the test case: data-tx:io.micronaut.transaction.jdbc.TransactionAnnotationSpec as follows:

dhcp-10-39-204-9:data-tx GK4J0B$ git diff 
diff --git a/data-tx/src/test/groovy/io/micronaut/transaction/jdbc/TransactionAnnotationSpec.groovy b/data-tx/src/test/groovy/io/micronaut/transaction/jdbc/TransactionAnnotationSpec.groovy
index 86bf641a3..5d8510202 100644
--- a/data-tx/src/test/groovy/io/micronaut/transaction/jdbc/TransactionAnnotationSpec.groovy
+++ b/data-tx/src/test/groovy/io/micronaut/transaction/jdbc/TransactionAnnotationSpec.groovy
@@ -60,6 +60,11 @@ class TransactionAnnotationSpec extends Specification {
         testService.readTransactionally() == 2
         testService.lastEvent
 
+        when:"A non-transactional method is called"
+        def z = testService.readNonTransactionally()
+
+        then:
+        z == 2
 
         when:"Test that connections are never exhausted"
         int i = 0
@@ -133,6 +138,19 @@ class TransactionAnnotationSpec extends Specification {
 
         }
 
+        int readNonTransactionally() {
+            def ps2 = connection.prepareStatement("select count(*) as count from book")
+            def rs = ps2.executeQuery()
+            try {
+                rs.next()
+                rs.getInt("count")
+            } finally {
+                ps2.close()
+                rs.close()
+            }
+
+        }
+

and get this when running:

  io.micronaut.transaction.exceptions.NoTransactionException: No current transaction present. Consider declaring @Transactional on the surrounding method
      at app//io.micronaut.transaction.jdbc.TransactionalConnectionInterceptor.intercept(TransactionalConnectionInterceptor.java:65)
      at app//io.micronaut.aop.chain.MethodInterceptorChain.proceed(MethodInterceptorChain.java:137)
      at app//io.micronaut.transaction.jdbc.TransactionAnnotationSpec$TestService.readNonTransactionally(TransactionAnnotationSpec.groovy:142)
      at io.micronaut.transaction.jdbc.TransactionAnnotationSpec.test transactional annotation handling(TransactionAnnotationSpec.groovy:64)
  Caused by: io.micronaut.transaction.jdbc.exceptions.CannotGetJdbcConnectionException: No current JDBC Connection found. Consider wrapping this call in transactional boundaries.
      at app//io.micronaut.transaction.jdbc.DataSourceUtils.doGetConnection(DataSourceUtils.java:135)
      at app//io.micronaut.transaction.jdbc.DataSourceUtils.getConnection(DataSourceUtils.java:93)
      at app//io.micronaut.transaction.jdbc.TransactionalConnectionInterceptor.intercept(TransactionalConnectionInterceptor.java:63)
      ... 3 more

Environment Information

Mac OS JDK 11

Example Application

No response

Version

micronaut parent 3.2.1, but I tried version 3.2.7 of the parent and got the same result

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 1
  • Comments: 24 (12 by maintainers)

Most upvoted comments

@matt-snider Given that annotating the non-transactional methods with io.micronaut.transaction.annotation.ReadOnly avoids this trouble, it shows that spring tx like behavior is possible using native micronaut tx. My suggestion for a solution would be to have methods on transactional classes that don’t carry @Transactional see behavior that equivalent to what they get if annotated with @ReadOnly. In other words, transactional behavior should apply only to methods that have declared their desire for it by being annotated with @Transactional.