Skip to content

Commit

Permalink
add-isolation-levels (#28)
Browse files Browse the repository at this point in the history
* add-isolation-levels

* document-it

* remove-overloading

* fix-README

* revert-to-overloading

---------

Co-authored-by: AlexKuznetsov <[email protected]>
Co-authored-by: chad-moller-target <[email protected]>
  • Loading branch information
3 people authored Oct 10, 2023
1 parent e130972 commit e7ad85a
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 4 deletions.
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -589,7 +589,7 @@ At the end of the withAutoCommit block, the AutoCommit ConnectionSession will be
## withTransaction

By using a Transaction ConnectionSession, changes will NOT be immediately committed to the database. Which allows for
multiple features listed below. If any of these features are required, use withTransaction.
multiple features listed below. If any of these features are required, use withTransaction. Also use withTransaction if you need to specify isolation level.

* Commit - Commits any existing changes to the database and clears any Savepoints and Locks
* Rollback - Reverts the changes since the most recent commit, or the beginning of the ConnectionSession if no commits
Expand All @@ -603,6 +603,18 @@ At the end of the withTransaction block, if the block is exited normally the Tra
exception is thrown, the Transaction will be rolled back. After the final commit/rollback, the Transaction ConnectionSession
will be closed.

### withTransaction - How to Specify Isolation levels

By default, all transactions run with `TRANSACTION_READ_COMMITTED`isolation level. The following shows how to specify a higher one:

```kotlin
db.withTransaction(isolationLevel = Db.IsolationLevel.TRANSACTION_REPEATABLE_READ)

db.withTransaction(isolationLevel = Db.IsolationLevel.TRANSACTION_SERIALIZABLE)
```

When the transaction is over, isolation level is restored to the default, TRANSACTION_READ_COMMITTED.

## DataSource configuration & AutoCommit

A dataSource has a default setting for the autocommit flag which can be configured. But the individual connections can
Expand Down
26 changes: 23 additions & 3 deletions src/main/kotlin/com/target/liteforjdbc/Db.kt
Original file line number Diff line number Diff line change
Expand Up @@ -118,20 +118,34 @@ open class Db(
* or rolls back if it throws an exception. This is required to perform any DB interactions that need transaction
* support.
*/
fun <T> withTransaction(block: (Transaction) -> T): T {
fun <T> withTransaction(
isolationLevel: IsolationLevel,
block: (Transaction) -> T
): T {
val transaction = Transaction(connection = dataSource.connection)
transaction.use {
val currentIsolationLevel = transaction.connection.transactionIsolation
return transaction.use {
try {
transaction.connection.transactionIsolation = isolationLevel.intCode
val result = block(transaction)
transaction.commit()
return result
result
} catch (t: Throwable) {
transaction.rollback()
throw t
} finally {
transaction.connection.transactionIsolation = currentIsolationLevel
}
}
}

/**
* Uses a com.target.liteforjdbc.Transaction, and commits it once to the block is executed successfully,
* or rolls back if it throws an exception. This is required to perform any DB interactions that need transaction
* support.
*/
fun <T> withTransaction(block: (Transaction) -> T): T = withTransaction(IsolationLevel.TRANSACTION_READ_COMMITTED, block)

/**
* Uses a com.target.liteforjdbc.AutoCommit and closes it once teh block is executed. This can be useful to use a
* single connection from the DataSource for a series of actions. Using other convenience query methods on this
Expand Down Expand Up @@ -169,5 +183,11 @@ open class Db(
*/
fun isDataSourceHealthy() = useConnection { !it.isClosed }

enum class IsolationLevel(val intCode: Int) {
TRANSACTION_READ_COMMITTED(Connection.TRANSACTION_READ_COMMITTED),
TRANSACTION_REPEATABLE_READ(Connection.TRANSACTION_REPEATABLE_READ),
TRANSACTION_SERIALIZABLE(Connection.TRANSACTION_SERIALIZABLE)
}

}

Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,34 @@ class PostgresSqlIntegrationTest {

finalCount shouldBe originalCount
}

@Test
fun setsReadCommittedIsolationLevel() {
checkIsolationLevel(Db.IsolationLevel.TRANSACTION_READ_COMMITTED, "read committed")
}

@Test
fun setsRepeatableReadIsolationLevel() {
checkIsolationLevel(Db.IsolationLevel.TRANSACTION_REPEATABLE_READ, "repeatable read")
}

@Test
fun setsSerializatableIsolationLevel() {
checkIsolationLevel(Db.IsolationLevel.TRANSACTION_SERIALIZABLE, "serializable")
}

private fun checkIsolationLevel(isolationLevel: Db.IsolationLevel, expected: String) {
val level = db.withTransaction(isolationLevel) { transaction ->
checkNotNull(
transaction.executeQuery(
"SELECT current_setting('transaction_isolation')"
) { resultSet: ResultSet ->
resultSet.getString("current_setting")
}
)
}
level shouldBe expected
}

private fun tableCount(): Int {
return checkNotNull(
Expand Down

0 comments on commit e7ad85a

Please sign in to comment.