@@ -6,182 +6,170 @@ import org.jetbrains.exposed.sql.StdOutSqlLogger
6
6
import org.jetbrains.exposed.sql.Transaction
7
7
import org.jetbrains.exposed.sql.addLogger
8
8
import org.jetbrains.exposed.sql.exposedLogger
9
- import org.jetbrains.exposed.sql.statements.api.ExposedConnection
10
- import org.jetbrains.exposed.sql.statements.jdbc.JdbcConnectionImpl
11
- import org.jetbrains.exposed.sql.transactions.TransactionInterface
12
9
import org.jetbrains.exposed.sql.transactions.TransactionManager
13
- import org.springframework.jdbc.datasource.ConnectionHolder
14
- import org.springframework.jdbc.datasource.DataSourceTransactionManager
10
+ import org.jetbrains.exposed.sql.transactions.transactionManager
15
11
import org.springframework.transaction.TransactionDefinition
16
12
import org.springframework.transaction.TransactionSystemException
17
- import org.springframework.transaction.support.DefaultTransactionDefinition
13
+ import org.springframework.transaction.support.AbstractPlatformTransactionManager
18
14
import org.springframework.transaction.support.DefaultTransactionStatus
19
- import org.springframework.transaction.support.TransactionSynchronizationManager
15
+ import org.springframework.transaction.support.SmartTransactionObject
20
16
import javax.sql.DataSource
21
17
22
18
class SpringTransactionManager (
23
19
dataSource : DataSource ,
24
- databaseConfig : DatabaseConfig = DatabaseConfig { },
20
+ databaseConfig : DatabaseConfig = DatabaseConfig {},
25
21
private val showSql : Boolean = false ,
26
- @Volatile override var defaultReadOnly : Boolean = databaseConfig.defaultReadOnly,
27
- @Volatile override var defaultRepetitionAttempts : Int = databaseConfig.defaultRepetitionAttempts,
28
- @Volatile override var defaultMinRepetitionDelay : Long = databaseConfig.defaultMinRepetitionDelay,
29
- @Volatile override var defaultMaxRepetitionDelay : Long = databaseConfig.defaultMaxRepetitionDelay
30
- ) : DataSourceTransactionManager(dataSource), TransactionManager {
22
+ ) : AbstractPlatformTransactionManager() {
31
23
32
- init {
33
- this .isRollbackOnCommitFailure = true
34
- }
35
-
36
- private val db = Database .connect(
37
- datasource = dataSource,
38
- databaseConfig = databaseConfig
39
- ) { this }
24
+ private var _database : Database
40
25
41
- @Volatile
42
- override var defaultIsolationLevel: Int = - 1
43
- get() {
44
- if (field == - 1 ) {
45
- field = Database .getDefaultIsolationLevel(db)
46
- }
47
- return field
48
- }
26
+ private var _transactionManager : TransactionManager
49
27
50
- private val transactionStackKey = " SPRING_TRANSACTION_STACK_KEY"
28
+ private val threadLocalTransactionManager: TransactionManager
29
+ get() = _transactionManager
51
30
52
- private fun getTransactionStack (): List <TransactionManager > {
53
- return TransactionSynchronizationManager .getResource(transactionStackKey)
54
- ?.let { it as List <TransactionManager > }
55
- ? : listOf ()
31
+ init {
32
+ _database = Database .connect(
33
+ datasource = dataSource, databaseConfig = databaseConfig
34
+ ).apply {
35
+ _transactionManager = this .transactionManager
36
+ }
56
37
}
57
38
58
- private fun setTransactionStack (list : List <TransactionManager >) {
59
- TransactionSynchronizationManager .unbindResourceIfPossible(transactionStackKey)
60
- TransactionSynchronizationManager .bindResource(transactionStackKey, list)
61
- }
39
+ override fun doGetTransaction (): Any {
40
+ val outerManager = TransactionManager .manager
41
+ val outer = threadLocalTransactionManager.currentOrNull()
62
42
63
- private fun pushTransactionStack (transaction : TransactionManager ) {
64
- val transactionList = getTransactionStack()
65
- setTransactionStack(transactionList + transaction)
43
+ return ExposedTransactionObject (
44
+ manager = threadLocalTransactionManager,
45
+ outerManager = outerManager,
46
+ outerTransaction = outer,
47
+ )
66
48
}
67
49
68
- private fun popTransactionStack () = setTransactionStack(getTransactionStack().dropLast(1 ))
69
-
70
- private fun getLastTransactionStack () = getTransactionStack().lastOrNull()
71
-
72
50
override fun doBegin (transaction : Any , definition : TransactionDefinition ) {
73
- super .doBegin(transaction, definition)
51
+ val trxObject = transaction as ExposedTransactionObject
52
+
53
+ val currentTransactionManager = trxObject.manager
54
+ TransactionManager .resetCurrent(currentTransactionManager)
74
55
75
- if (TransactionSynchronizationManager .hasResource(obtainDataSource())) {
76
- currentOrNull() ? : initTransaction(transaction)
56
+ currentTransactionManager.currentOrNull() ? : currentTransactionManager.newTransaction(
57
+ isolation = definition.isolationLevel,
58
+ readOnly = definition.isReadOnly,
59
+ ).apply {
60
+ if (showSql) {
61
+ addLogger(StdOutSqlLogger )
62
+ }
77
63
}
64
+ }
78
65
79
- pushTransactionStack(this @SpringTransactionManager)
66
+ override fun doCommit (status : DefaultTransactionStatus ) {
67
+ val trxObject = status.transaction as ExposedTransactionObject
68
+ TransactionManager .resetCurrent(trxObject.manager)
69
+ trxObject.commit()
80
70
}
81
71
82
- override fun doCleanupAfterCompletion ( transaction : Any ) {
83
- super .doCleanupAfterCompletion( transaction)
84
- if ( ! TransactionSynchronizationManager .hasResource(obtainDataSource())) {
85
- TransactionSynchronizationManager .unbindResourceIfPossible( this )
86
- }
72
+ override fun doRollback ( status : DefaultTransactionStatus ) {
73
+ val trxObject = status. transaction as ExposedTransactionObject
74
+ TransactionManager .resetCurrent(trxObject.manager)
75
+ trxObject.rollback( )
76
+ }
87
77
88
- popTransactionStack()
89
- TransactionManager .resetCurrent(getLastTransactionStack())
78
+ override fun doCleanupAfterCompletion ( transaction : Any ) {
79
+ val trxObject = transaction as ExposedTransactionObject
90
80
91
- if ( TransactionSynchronizationManager .isSynchronizationActive() && TransactionSynchronizationManager .getSynchronizations().isEmpty()) {
92
- TransactionSynchronizationManager .clearSynchronization( )
81
+ trxObject.cleanUpTransactionIfIsPossible {
82
+ closeStatementsAndConnections(it )
93
83
}
94
- }
95
84
96
- override fun doSuspend (transaction : Any ): Any {
97
- TransactionSynchronizationManager .unbindResourceIfPossible(this )
98
- return super .doSuspend(transaction)
85
+ trxObject.setCurrentToOuter()
99
86
}
100
87
101
- override fun doCommit (status : DefaultTransactionStatus ) {
88
+ private fun closeStatementsAndConnections (transaction : Transaction ) {
89
+ val currentStatement = transaction.currentStatement
102
90
@Suppress(" TooGenericExceptionCaught" )
103
91
try {
104
- currentOrNull()?.commit()
105
- } catch (e: Exception ) {
106
- throw TransactionSystemException (e.message.orEmpty(), e)
92
+ currentStatement?.let {
93
+ it.closeIfPossible()
94
+ transaction.currentStatement = null
95
+ }
96
+ transaction.closeExecutedStatements()
97
+ } catch (error: Exception ) {
98
+ exposedLogger.warn(" Statements close failed" , error)
107
99
}
108
- }
109
100
110
- override fun doRollback (status : DefaultTransactionStatus ) {
111
101
@Suppress(" TooGenericExceptionCaught" )
112
102
try {
113
- currentOrNull()?.rollback ()
114
- } catch (e : Exception ) {
115
- throw TransactionSystemException (e .message.orEmpty(), e )
103
+ transaction.close ()
104
+ } catch (error : Exception ) {
105
+ exposedLogger.warn( " Transaction close failed: ${error .message} . Statement: $currentStatement " , error )
116
106
}
117
107
}
118
108
119
- override fun newTransaction (isolation : Int , readOnly : Boolean , outerTransaction : Transaction ? ): Transaction {
120
- val tDefinition = DefaultTransactionDefinition ().apply {
121
- isReadOnly = readOnly
122
- isolationLevel = isolation
123
- }
124
-
125
- val transactionStatus = (getTransaction(tDefinition) as DefaultTransactionStatus )
126
- return currentOrNull() ? : initTransaction(transactionStatus.transaction)
109
+ override fun doSetRollbackOnly (status : DefaultTransactionStatus ) {
110
+ val trxObject = status.transaction as ExposedTransactionObject
111
+ trxObject.setRollbackOnly()
127
112
}
128
113
129
- private fun initTransaction (transaction : Any ): Transaction {
130
- val connection = (TransactionSynchronizationManager .getResource(obtainDataSource()) as ConnectionHolder ).connection
114
+ private data class ExposedTransactionObject (
115
+ val manager : TransactionManager ,
116
+ val outerManager : TransactionManager ,
117
+ private val outerTransaction : Transaction ? ,
118
+ ) : SmartTransactionObject {
131
119
132
- @Suppress(" TooGenericExceptionCaught" )
133
- val transactionImpl = try {
134
- SpringTransaction (JdbcConnectionImpl (connection), db, defaultIsolationLevel, defaultReadOnly, currentOrNull(), transaction)
135
- } catch (e: Exception ) {
136
- exposedLogger.error(" Failed to start transaction. Connection will be closed." , e)
137
- connection.close()
138
- throw e
139
- }
120
+ private var isRollback: Boolean = false
121
+ private var isCurrentTransactionEnded: Boolean = false
140
122
141
- TransactionManager .resetCurrent(this )
142
- return Transaction (transactionImpl).apply {
143
- TransactionSynchronizationManager .bindResource(this @SpringTransactionManager, this )
144
- if (showSql) {
145
- addLogger(StdOutSqlLogger )
123
+ fun cleanUpTransactionIfIsPossible (block : (transaction: Transaction ) -> Unit ) {
124
+ val currentTransaction = getCurrentTransaction()
125
+ if (isCurrentTransactionEnded && currentTransaction != null ) {
126
+ block(currentTransaction)
146
127
}
147
128
}
148
- }
149
129
150
- override fun currentOrNull (): Transaction ? = TransactionSynchronizationManager .getResource(this ) as Transaction ?
151
- override fun bindTransactionToThread (transaction : Transaction ? ) {
152
- if (transaction != null ) {
153
- bindResourceForSure(this , transaction)
154
- } else {
155
- TransactionSynchronizationManager .unbindResourceIfPossible(this )
130
+ fun setCurrentToOuter () {
131
+ manager.bindTransactionToThread(outerTransaction)
132
+ TransactionManager .resetCurrent(outerManager)
156
133
}
157
- }
158
134
159
- private fun bindResourceForSure (key : Any , value : Any ) {
160
- TransactionSynchronizationManager .unbindResourceIfPossible(key)
161
- TransactionSynchronizationManager .bindResource(key, value)
162
- }
163
-
164
- private inner class SpringTransaction (
165
- override val connection : ExposedConnection <* >,
166
- override val db : Database ,
167
- override val transactionIsolation : Int ,
168
- override val readOnly : Boolean ,
169
- override val outerTransaction : Transaction ? ,
170
- private val currentTransaction : Any ,
171
- ) : TransactionInterface {
172
-
173
- override fun commit () {
174
- connection.commit()
135
+ private fun hasOuterTransaction (): Boolean {
136
+ return outerTransaction != null
175
137
}
176
138
177
- override fun rollback () {
178
- connection.rollback()
139
+ @Suppress(" TooGenericExceptionCaught" )
140
+ fun commit () {
141
+ try {
142
+ if (hasOuterTransaction().not ()) {
143
+ isCurrentTransactionEnded = true
144
+ manager.currentOrNull()?.commit()
145
+ }
146
+ } catch (error: Exception ) {
147
+ throw TransactionSystemException (error.message.orEmpty(), error)
148
+ }
179
149
}
180
150
181
- override fun close () {
182
- if (TransactionSynchronizationManager .isActualTransactionActive()) {
183
- this @SpringTransactionManager.doCleanupAfterCompletion(currentTransaction)
151
+ @Suppress(" TooGenericExceptionCaught" )
152
+ fun rollback () {
153
+ try {
154
+ if (hasOuterTransaction().not ()) {
155
+ isCurrentTransactionEnded = true
156
+ manager.currentOrNull()?.rollback()
157
+ }
158
+ } catch (error: Exception ) {
159
+ throw TransactionSystemException (error.message.orEmpty(), error)
184
160
}
185
161
}
162
+
163
+ fun getCurrentTransaction (): Transaction ? = manager.currentOrNull()
164
+
165
+ fun setRollbackOnly () {
166
+ isRollback = true
167
+ }
168
+
169
+ override fun isRollbackOnly () = isRollback
170
+
171
+ override fun flush () {
172
+ // Do noting
173
+ }
186
174
}
187
175
}
0 commit comments