How can I more easily use Jooq transactions in Kotlin

I have Jooq code written in Kotlin using transactions, and sometimes I want the method to work autonomously as a top-level action that will have its own transactions, and in other cases it should work with other methods in the same transaction. For example, I have two lower-level functions actionAbcand actionXyzthat I want to compose into different higher-level data methods and inherit their transaction, if any, otherwise they have their own.

I know that in Spring or in other frameworks there are annotations that can be added that check the functionality of "transaction required" or "create transaction, if not". But how can I do the same with Jooq + Kotlin without using these libraries?

The closest I came up with is to pass the transaction as an optional parameter and by default use a new transaction if it is missing. But if someone forgets to transfer a transaction, then he has a subtle refusal to use a new top-level transaction and an unrelated transaction, I do not want this.

fun tx(ctx: DSLContext = rootContext, codeBlock: DSLContext.() -> Unit): Unit {
        ctx.transaction { cfg ->
            DSL.using(cfg).codeBlock()
        }
    }
}

// and used as:

fun actionAbc(parm1: String, parm2: Int, ctx: DSLContext = rootContext) {
   tx(ctx) { ... }
}

fun actionXyz(parm: Date, ctx: DSLContext = rootContext) {
   tx(ctx) { ... }
}

// composed:

fun higherLevelAction(parm1: String, parm2: Date) {
  tx {
    actionAbc(parm1, 45, this) // if you forget `this` you are doing the wrong thing
    actionXyz(parm2, this)

    tx(this) {
       // nested transaction, also dangerous if forgetting `this` parameter
    }
  }
}

How can I make this more natural and less dangerous?

: (), SO.

+4
1

, , . -, , : - , - .

fun <T : Any?> tx(codeBlock: DSLContext.() -> T): T {
    return rootContext.txWithReturn(codeBlock)
}

fun <T : Any?> DSLContext.tx(codeBlock: DSLContext.() -> T): T {
    var returnVal: T? = null
    this.transaction { cfg ->
        returnVal = DSL.using(cfg).codeBlock()
    }
    return returnVal as T
}

. Kotlin , .

fun foo() {
   tx { // calls the outer function that creates a transaction
     ...
     tx { // calls the extension on DSLContext because our code block has receiver of DSLContext
       ...
       tx { // calls the extension function, further nesting correctly
         ...
       }
     }
   }
}

actionAbc actionXyz, .

fun DSLContext.actionAbc(parm1: String, parm2: Int) {
   ...
}

fun DSLContext.actionXyz(parm: Date) {
   ...
}

, . :

fun higherLevelAction(parm1: String, parm2: Date) {
  tx {
    actionAbc(parm1, 45)
    actionXyz(parm2)

    tx {
       // nesting naturally
       ...
    }
  }
}

actionAbc actionXyz . , , , . , actionAbc:

fun DSLContext.actionAbc(parm1: String, parm2: Int) {
   ...
}

fun actionAbc(parm1: String, parm2: Int) {
   tx { actionAbc(parm1, parm2) } // delegates to one above but with a new transaction
}

actionAbc , , , .

, , , .

:

  • ( , )
  • ( , )
  • , ( , )

, , , :

@Deprecated("Only call these without an existing transaction!", 
            level = DeprecationLevel.ERROR)
fun DSLContext.actionAbc(parm1: String, parm2: Int) {
   throw IllegalStateException("Only call these without an existing transaction!")
}

fun actionAbc(parm1: String, parm2: Int) {
   tx { 
      ...
   } 
}

- @Deprecation , ERROR. WARNING, , @Suppress("DEPRECATION") .

+6

All Articles