Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add methods for managing Session/Transaction lifecycle to SessionFactory #410

Closed
gavinking opened this issue May 19, 2023 · 5 comments · Fixed by #414
Closed

add methods for managing Session/Transaction lifecycle to SessionFactory #410

gavinking opened this issue May 19, 2023 · 5 comments · Fixed by #414

Comments

@gavinking
Copy link
Contributor

gavinking commented May 19, 2023

The transaction management idiom in JPA is not so trivial:

var entityManager = factory.createEntityManager();
var transaction = entityManager.getTransaction();
try {
    transaction.begin();
    // do some work...
    transaction.commit();
}
catch (Exception e) {
    if (transaction.isActive()) {
        try {
            transaction.rollback();
        }
        catch (Exception x) {
            e.addSuppressed(x);
        }
   }
   throw e; 
}
finally {
    entityManager.close();
} 

I think we should add a withTransaction() method to EntityManagerFactory declared as follows:

<R> R withTransaction(Function<EntityManager,R> work);

The work would be performed within the scope of a resource-local transaction.

@rbygrave
Copy link

I presume the alternative of using try-with-resources blocks is not workable? Seems it could be done for EntityManager but problematic to get a transaction.close() in place?

@gavinking
Copy link
Contributor Author

gavinking commented May 19, 2023

@rbygrave I don't think we would have a good way to detect that an exception had been thrown from code inside AutoCloseable.close(). So no, I don't see a way to do it, though I have not thought deeply about it.

@gavinking
Copy link
Contributor Author

gavinking commented May 19, 2023

Folks, any feedback on #414?

One issue here is whether we need two flavors of withTransaction(), one void, and the other returning a value.

I think we do, but I don't know what to call them.

@gavinking
Copy link
Contributor Author

gavinking commented Aug 20, 2023

@hantsy @Tomas-Kraus @lukasj and everyone else:

  1. Are there any votes for renaming to executeInTransaction() + computeInTransaction()?
  2. Alternatively, would anyone be in favor of just calling these methods run() and call() or execute() and compute(), and letting you pass in a TxType.REQUIRED, TxType.REQUIRES_NEW, etc.

So according to 2, you would write:

entityManagerFactory.execute(TxType.REQUIRED, entityManager -> {
    entityManager.persist(book);
});

which is obviously quite a lot harder to type than:

entityManagerFactory.executeInTransaction(entityManager -> {
    entityManager.persist(book);
});

But it's not actually that much worse to look at, and it's quite a lot more flexible.

Another argument agains 2 is that the way jakarta.transaction actually defines this enum is IMO pretty nasty:

  • for some reason I cannot for the life of me understand, the enum TxType as an inner type of the annotation type @Transactional, making it much less naturally-reusable, and
  • TxType doesn't read well at all ... not to mention that it's not a "transaction type", but a transaction propagation type.

We could of course define our own TransactionPropagationType enum, perhaps justifiable on the basis that it's not just for JTA but also for resource-local transactions, but I would really prefer not to go there.

@lukasj
Copy link
Contributor

lukasj commented Aug 21, 2023

For now, let's go without direct dependency on JTA API which would bring in CDI API (transitively)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants