diff --git a/docs/src/main/asciidoc/mongodb-panache.adoc b/docs/src/main/asciidoc/mongodb-panache.adoc index 2bf957cecf485..8c8eefb33b614 100644 --- a/docs/src/main/asciidoc/mongodb-panache.adoc +++ b/docs/src/main/asciidoc/mongodb-panache.adoc @@ -768,6 +768,8 @@ MongoDB offers ACID transactions since version 4.0. To use them with MongoDB with Panache you need to annotate the method that starts the transaction with the `@Transactional` annotation. +Inside methods annotated with `@Transactional` you can access the `ClientSession` with `Panache.getClientSession()` if needed. + In MongoDB, a transaction is only possible on a replicaset, luckily our xref:mongodb.adoc#dev-services[Dev Services for MongoDB] setups a single node replicaset so it is compatible with transactions. diff --git a/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/common/runtime/MongoOperations.java b/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/common/runtime/MongoOperations.java index c2285c29c4356..93f411727938b 100644 --- a/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/common/runtime/MongoOperations.java +++ b/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/common/runtime/MongoOperations.java @@ -333,9 +333,8 @@ ClientSession getSession(Object entity) { return getSession(entity.getClass()); } - ClientSession getSession(Class entityClass) { + public ClientSession getSession(Class entityClass) { ClientSession clientSession = null; - MongoEntity mongoEntity = entityClass.getAnnotation(MongoEntity.class); InstanceHandle instance = Arc.container() .instance(TransactionSynchronizationRegistry.class); if (instance.isAvailable()) { @@ -343,6 +342,7 @@ ClientSession getSession(Class entityClass) { if (registry.getTransactionStatus() == Status.STATUS_ACTIVE) { clientSession = (ClientSession) registry.getResource(SESSION_KEY); if (clientSession == null) { + MongoEntity mongoEntity = entityClass == null ? null : entityClass.getAnnotation(MongoEntity.class); return registerClientSession(mongoEntity, registry); } } @@ -350,6 +350,10 @@ ClientSession getSession(Class entityClass) { return clientSession; } + public ClientSession getSession() { + return getSession(null); + } + private ClientSession registerClientSession(MongoEntity mongoEntity, TransactionSynchronizationRegistry registry) { TransactionManager transactionManager = Arc.container().instance(TransactionManager.class).get(); diff --git a/extensions/panache/mongodb-panache-kotlin/runtime/src/main/kotlin/io/quarkus/mongodb/panache/kotlin/Panache.kt b/extensions/panache/mongodb-panache-kotlin/runtime/src/main/kotlin/io/quarkus/mongodb/panache/kotlin/Panache.kt new file mode 100644 index 0000000000000..817c55f54bd43 --- /dev/null +++ b/extensions/panache/mongodb-panache-kotlin/runtime/src/main/kotlin/io/quarkus/mongodb/panache/kotlin/Panache.kt @@ -0,0 +1,27 @@ +package io.quarkus.mongodb.panache.kotlin + +import com.mongodb.session.ClientSession +import io.quarkus.mongodb.panache.kotlin.runtime.KotlinMongoOperations + +object Panache { + /** + * Access the current MongoDB ClientSession from the transaction context. Can be used inside a + * method annotated with `@Transactional` to manually access the client session. + * + * @return ClientSession or null if not in the context of a transaction. + */ + val session: ClientSession + get() = KotlinMongoOperations.INSTANCE.session + + /** + * Access the current MongoDB ClientSession from the transaction context. + * + * @param entityClass the class of the MongoDB entity in case it is configured to use the + * non-default client. + * @return ClientSession or null if not in the context of a transaction. + * @see [session] + */ + fun getSession(entityClass: Class<*>?): ClientSession { + return KotlinMongoOperations.INSTANCE.getSession(entityClass) + } +} diff --git a/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/Panache.java b/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/Panache.java new file mode 100644 index 0000000000000..2e1f900dee367 --- /dev/null +++ b/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/Panache.java @@ -0,0 +1,30 @@ +package io.quarkus.mongodb.panache; + +import com.mongodb.session.ClientSession; + +import io.quarkus.mongodb.panache.runtime.JavaMongoOperations; + +public class Panache { + + /** + * Access the current MongoDB ClientSession from the transaction context. + * Can be used inside a method annotated with `@Transactional` to manually access the client session. + * + * @return ClientSession or null if not in the context of a transaction. + */ + public static ClientSession getSession() { + return JavaMongoOperations.INSTANCE.getSession(); + } + + /** + * Access the current MongoDB ClientSession from the transaction context. + * + * @see #getSession() + * + * @param entityClass the class of the MongoDB entity in case it is configured to use the non-default client. + * @return ClientSession or null if not in the context of a transaction. + */ + public static ClientSession getSession(Class entityClass) { + return JavaMongoOperations.INSTANCE.getSession(entityClass); + } +} diff --git a/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/transaction/TransactionPersonResource.java b/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/transaction/TransactionPersonResource.java index 27763f671c453..74f5174f12475 100644 --- a/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/transaction/TransactionPersonResource.java +++ b/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/transaction/TransactionPersonResource.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.List; +import io.quarkus.mongodb.panache.Panache; import jakarta.enterprise.event.Observes; import jakarta.inject.Inject; import jakarta.inject.Named; @@ -16,6 +17,8 @@ import io.quarkus.runtime.StartupEvent; +import static org.junit.jupiter.api.Assertions.assertNotNull; + @Path("/transaction") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) @@ -42,6 +45,7 @@ public List getPersons() { @Transactional public Response addPerson(TransactionPerson person) { person.persist(); + assertNotNull(Panache.getSession(TransactionPerson.class)); return Response.created(URI.create("/transaction/" + person.id.toString())).build(); }