with latest spring boot and kotlin I'd like to hav...
# spring
c
with latest spring boot and kotlin I'd like to have something like this:
Copy code
@Component class MyComponent(...) {
  fun someFun() {
    /* do something non-transactional before the transaction */
    inTransaction(txType = TxType.REQUIRED, readOnly = false) {
      /* do something inside a transaction */
    }
    /* do something non-transactional after the transaction */
  }
}
What would be the best way to implement the
inTransaction
fun?
t
Copy code
@Component
class TransactionHelper {

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    fun <T> runOnSeparateTx(handler: () -> T): T {
        return handler()
    }

    @Async("limitedExecutor")
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    fun <T> runOnSeparateTxAsync(handler: () -> T?): CompletableFuture<T?> {
        return CompletableFuture.completedFuture(handler())
    }
}
d
or checkout
TransactionTemplate
c
Currently I have this:
Copy code
@Component
class TransactionSupport : InitializingBean {

	@Autowired
	private lateinit var m: PlatformTransactionManager

	override fun afterPropertiesSet() {
		manager = this.m
	}

	companion object {

		private lateinit var manager: PlatformTransactionManager

		fun <R> inTransaction(txType: TxType = TxType.REQUIRED, readOnly: Boolean = false, block: () -> R): R? {
			val template = TransactionTemplate(manager, buildTransactionDefinition(txType, readOnly))
			return template.execute {
				block.invoke()
			}
		}

		private fun buildTransactionDefinition(txType: TxType, readOnly: Boolean): TransactionDefinition {
			val def = RuleBasedTransactionAttribute()
			def.isReadOnly = readOnly
			def.setPropagationBehaviorName(PREFIX_PROPAGATION + txType.toString())
			return def
		}
	}
}
I can easily rewrite this to have
inTransaction
as top-level function instead of a
companion object
one, but both seem dirty. What I'm asking is if there is a better way to have nice API like in my usage example where I can just write
inTransaction { /* stuff */ }
and not inject a helper manually
t
You could create a
@Transactional
method on a superclass and extend that wherever you want to use that
inTransaction { }
, or creating an Extension
fun <T> Any.inTransaction()
calling that "static" implementation.
c
when we're talking inheritance vs injection, I'll take injection any day 🙂 So that's not an option.
Any.inTransaction
does not make sense to me, why would I need a receiver there? It also doesn't improve on the main problem - static stuff. I guess it's either static or inheritance or injection?
t
Any.inTransaction
makes the
inTransaction
method available for any class - even String. I don't see any other option.
d
I would suggest using
Configuration
instead of
Component
- you can setup it later to use as Auto-Configuration
Copy code
@Configuration
@ConditionalOnBean(PlatformTransactionManager::class)
class TransactionSupport(private val m: PlatformTransactionManager) {
    
    @PostConstruct
    fun setup() {
        manager = this.m
    }
and consider a case when there can be more than one
TransactionManager
if you want to release it as library 🙂
j
Out of curiosity what you currently have is, why dont you just do
Copy code
@Component
    class TransactionSupport(
        @Autowired transactionTemplate: TransactionTemplate
    ) {
        ...
    }
(this doesn't answer your original question though)
Oh, is it because you want to expose it by companion object?
c
That's because it needs to construct
TransactionTemplate
using supplied parameters, like isolation for example. I actually have configuration based setup too for other stuff, the @Component here was to try this approach out. Here's my configuration based Command.execute():
Copy code
private lateinit var STATIC_COMMAND_SERVICE: CommandService

interface Command<T> : Serializable {
	fun execute(): T {
		return STATIC_COMMAND_SERVICE.execute(this)
	}
}

@Configuration
internal class CommandServiceKotlinInitializer(
	commandService: CommandService
) {
	init {
		STATIC_COMMAND_SERVICE = commandService
	}
}
with this I can do
MyCommand(/* stuff */).execute()
I ended up doing
inTransaction
in a similar way to command service, via configuration and private top-level var and public top level function
👍 1
170 Views