11package no.nav.hjelpemidler.database
22
33import io.github.oshai.kotlinlogging.KotlinLogging
4+ import kotlinx.coroutines.Dispatchers
5+ import kotlinx.coroutines.currentCoroutineContext
46import kotlinx.coroutines.withContext
57import no.nav.hjelpemidler.database.kotliquery.SessionJdbcOperations
68import no.nav.hjelpemidler.database.kotliquery.SessionProperties
79import no.nav.hjelpemidler.database.kotliquery.createSession
810import javax.sql.DataSource
11+ import kotlin.coroutines.AbstractCoroutineContextElement
12+ import kotlin.coroutines.CoroutineContext
913
1014private val log = KotlinLogging .logger {}
1115
@@ -14,23 +18,26 @@ interface Transaction<T : Any> {
1418}
1519
1620/* *
17- * Opprett [JdbcOperations] og start transaksjon eller gjenbruk eksisterende.
21+ * Opprett [JdbcOperations] og start transaksjon eller gjenbruk eksisterende [JdbcOperations] .
1822 *
1923 * Tilsvarer `PROPAGATION_REQUIRED` i Spring.
2024 *
2125 * Tillater suspending functions i transaksjonen for nettverkskall etc.
2226 *
23- * NB! Ikke gjør parallelle kall med [JdbcOperations] i [block] som f.eks.:
27+ * NB! Ikke gjør parallelle kall med samme [JdbcOperations] i [block] som f.eks.:
2428 * ```kotlin
25- * transaction { tx ->
29+ * transaction { operations ->
2630 * coroutineScope {
27- * launch { tx ... }
28- * val deferred = async { tx ... }
31+ * launch { operations ... }
32+ * val deferred = async { operations ... }
2933 * }
3034 * }
3135 * ```
3236 *
33- * @see [JdbcTransactionContext]
37+ * NB! Parametre som [readOnly] etc. kan ikke endres i nestede transaksjoner siden transaksjonen gjenbrukes.
38+ * Eventuelle endringer vil bli ignorert.
39+ *
40+ * @see [TransactionJdbcOperations]
3441 * @see <a href="https://docs.spring.io/spring-framework/reference/data-access/transaction/declarative/tx-propagation.html#tx-propagation-required">Understanding PROPAGATION_REQUIRED</a>
3542 */
3643suspend fun <T > transaction (
@@ -47,20 +54,27 @@ suspend fun <T> transaction(
4754 strict = strict,
4855 queryTimeout = queryTimeout,
4956 )
50- val context = currentTransactionContext() ? : return withTransactionContext(
51- closeable = createSession(dataSource, properties),
52- ) { session ->
53- log.trace { " Establishing new database transaction, $properties " }
54- session.transaction {
55- val tx = SessionJdbcOperations (it)
56- withContext( JdbcTransactionContext (properties, tx)) {
57- block(tx)
57+ val outer = currentCoroutineContext()[ TransactionJdbcOperations ] ? : return withContext( Dispatchers . IO ) {
58+ log.trace { " Oppretter ny databasetransaksjon, $ properties" }
59+ createSession(dataSource, properties).use { session ->
60+ session. transaction { transactionalSession ->
61+ val operations = SessionJdbcOperations (transactionalSession)
62+ withContext( TransactionJdbcOperations (properties, operations)) {
63+ block(operations)
64+ }
5865 }
5966 }
6067 }
61- log.trace { " Existing database transaction is reused , ${context .properties} " }
62- if (properties != context .properties) {
63- log.debug { " Session properties changed. Ignoring modifications in nested transaction because the outer transaction is reused, outer : (${context .properties} ), nested : ($properties )" }
68+ log.trace { " Gjenbruker eksisterende databasetransaksjon , ${outer .properties} " }
69+ if (properties != outer .properties) {
70+ log.debug { " Transaksjonen ble forsøkt endret, men endringene ignoreres siden transaksjonen gjenbrukes, ytre : (${outer .properties} ), indre : ($properties )" }
6471 }
65- return block(context.tx)
72+ return block(outer.operations)
73+ }
74+
75+ internal class TransactionJdbcOperations (
76+ val properties : SessionProperties ,
77+ val operations : JdbcOperations ,
78+ ) : AbstractCoroutineContextElement(TransactionJdbcOperations ) {
79+ companion object Key : CoroutineContext.Key<TransactionJdbcOperations>
6680}
0 commit comments