question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Document @Transaction annotation

See original GitHub issue

It would be great to have some documentation on the @Transaction annotation. I just lost several hours of debugging to find @Transaction handling was behaving exceedingly unintuitively.

My end goal was to hand-write a dynamic query but join an existing wrapping transaction started explicitly by a unit test:

@Before
h.begin()

@After
h.rollback

@Test
userDao.getUserMoreComplicated(...)

public interface MyDAO implements GetHandle {
  @SqlQuery("thought this was transactional...")
  @Transaction // nope.
  public User getUser(@Bind("id") String id);

  @Transaction // does not join existing transaction either...
  public User getUserMoreComplicated(String arg1, Integer arg2, etc.) {
    return withHandle(new HandleCallback<User>() {
      public User withHandle(Handle handle) throws Exception
      {
        return  handle.createQuery("my super duper dynamically constructed query").map(User.class).list(); 
      }
    }
  }
}

What I found was that getUserMoreComplicated was always committing, while getUser was apparently correctly rolling back (not committing). Similarly, explicitly using handle.withTransaction resulted in commit. And leaving them all off naturally resulted in no transactionn and no rollback if no wrapping transaction was explicitly set up.

There are several things wrong here. To start getUser was not working as I assumed. It turns out that @Transaction is mutually exclusive with every other annotation. See below in SqlObject:

// these else ifs are all mutually exclusive...
for (final ResolvedMethod method : d.getMemberMethods()) {
    final Method raw_method = method.getRawMember();

    if (raw_method.isAnnotationPresent(SqlQuery.class)) {
        handlers.put(raw_method, new QueryHandler(sqlObjectType, method, ResultReturnThing.forType(method)));
    }
    else if (raw_method.isAnnotationPresent(SqlUpdate.class)) {
        handlers.put(raw_method, new UpdateHandler(sqlObjectType, method));
    }
    else if (raw_method.isAnnotationPresent(SqlBatch.class)) {
        handlers.put(raw_method, new BatchHandler(sqlObjectType, method));
    }
    else if (raw_method.isAnnotationPresent(SqlCall.class)) {
        handlers.put(raw_method, new CallHandler(sqlObjectType, method));
    }
    else if(raw_method.isAnnotationPresent(CreateSqlObject.class)) {
        handlers.put(raw_method, new CreateSqlObjectHandler(raw_method.getReturnType()));
     }
     else if (method.getName().equals("close") && method.getRawMember().getParameterTypes().length == 0) {
         handlers.put(raw_method, new CloseHandler());
     }
     else if (raw_method.isAnnotationPresent(Transaction.class)) {
         handlers.put(raw_method, new PassThroughTransactionHandler(raw_method, raw_method.getAnnotation(Transaction.class)));
     }
     else if (mixinHandlers.containsKey(raw_method)) {
         handlers.put(raw_method, mixinHandlers.get(raw_method));
     }
     else {
         handlers.put(raw_method, new PassThroughHandler(raw_method));
     }
}

You can’t have @Transaction @SqlUpdate or @Transaction @SqlQuery, it just doesn’t work like Spring transactions. Secondly, this explains why getUserMoreComplicated was always committing. @Transaction is the only annotation present on that method, so @Transaction functionality is enabled, and what this actually does is unilaterally commit:

 try {
     handle.begin();
     returnValue = callback.inTransaction(handle, status);
     if (!failed.get()) {
         handle.commit();
     }
}

Could it be I missed critical docs on @Transaction? At the very least its scope of functionality seems very counter-intuitive to me, and probably anybody coming from a declarative transaction background. Is the intent really for @Transaction to not apply to SQL Object methods, and for it to forcefully commit? If so, is there any way at all to write reusable code that joins an existing transaction (e.g. unit tests) or starts a new one on demand (production)?

Issue Analytics

  • State:closed
  • Created 10 years ago
  • Reactions:3
  • Comments:10 (6 by maintainers)

github_iconTop GitHub Comments

2reactions
Manikandan-Kcommented, May 10, 2015

I have documented it here. http://manikandan-k.github.io/2015/05/10/Transactions_in_jdbi.html See whether it helps.

1reaction
bilalaslamcommented, Oct 25, 2014

👍 on documenting this better.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Transactional (Spring Framework 6.0.2 API)
This annotation commonly works with thread-bound transactions managed by a PlatformTransactionManager , exposing a transaction to all data access operations ...
Read more >
Spring Data MongoDB Transactions - Baeldung
After we finished the configuration, all we need to do to use native MongoDB transactions – is to annotate our method with @Transactional....
Read more >
Spring Transaction Management: @Transactional In-Depth
You can use this guide to get a simple and practical understanding of how Spring's transaction management with the @Transactional annotation ...
Read more >
Using Transactions in Quarkus
The @TransactionConfiguration annotation allows to set a timeout property, in seconds, that applies to transactions created within the annotated method. This ...
Read more >
Transactional (Java(TM) EE 7 Specification APIs)
transaction.Transactional annotation provides the application the ability to declaratively control transaction boundaries on CDI managed beans, as well as ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found