Skip to content

Commit fa70322

Browse files
author
Dries Samyn
authored
R3SOL-0 Remove hibernate dependency from ledger persistence lib (#6368)
Removed the dependency on Hibernate and JPA from the ledger persistence library. Most ledger operations used "Native Queries", so were not leveraging hibernate. This means that he code dependent on this library does not need to reference JPA or Hibernate.
1 parent 0766222 commit fa70322

File tree

18 files changed

+998
-600
lines changed

18 files changed

+998
-600
lines changed

Diff for: components/ledger/ledger-persistence/src/integrationTest/kotlin/net/corda/ledger/persistence/utxo/tests/UtxoPersistenceServiceImplTest.kt

+25-9
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ import net.corda.v5.ledger.utxo.observer.UtxoTokenPoolKey
7777
import net.corda.v5.ledger.utxo.transaction.UtxoLedgerTransaction
7878
import org.assertj.core.api.Assertions.assertThat
7979
import org.assertj.core.api.Assertions.assertThatThrownBy
80+
import org.hibernate.Session
81+
import org.hibernate.internal.SessionImpl
8082
import org.junit.jupiter.api.AfterAll
8183
import org.junit.jupiter.api.Assertions.assertNotNull
8284
import org.junit.jupiter.api.BeforeAll
@@ -98,12 +100,14 @@ import java.security.KeyPairGenerator
98100
import java.security.MessageDigest
99101
import java.security.PublicKey
100102
import java.security.spec.ECGenParameterSpec
103+
import java.sql.Connection
101104
import java.time.Duration
102105
import java.time.Instant
103106
import java.time.temporal.ChronoUnit
104107
import java.util.Random
105108
import java.util.UUID
106109
import java.util.concurrent.atomic.AtomicInteger
110+
import javax.persistence.EntityManager
107111
import javax.persistence.EntityManagerFactory
108112

109113
@ExtendWith(ServiceExtension::class, BundleContextExtension::class)
@@ -180,7 +184,7 @@ class UtxoPersistenceServiceImplTest {
180184
filteredTransactionFactory = ctx.getSandboxSingletonService()
181185

182186
persistenceService = UtxoPersistenceServiceImpl(
183-
entityManagerFactory,
187+
{ getConnection(entityManagerFactory.createEntityManager()) },
184188
repository,
185189
serializationService,
186190
digestService,
@@ -293,13 +297,17 @@ class UtxoPersistenceServiceImplTest {
293297
assertThat(retval).isEqualTo(expectedRetval)
294298
}
295299

300+
private fun getConnection(em: EntityManager): Connection {
301+
return (em.unwrap(Session::class.java) as SessionImpl).connection()
302+
}
303+
296304
@Test
297305
fun `find unconsumed visible transaction states`() {
298306
val entityFactory = UtxoEntityFactory(entityManagerFactory)
299307
val transaction1 = createSignedTransaction()
300308
val transaction2 = createSignedTransaction()
301-
entityManagerFactory.transaction { em ->
302-
309+
entityManagerFactory.createEntityManager().transaction { em ->
310+
val conn = getConnection(em)
303311
em.createNativeQuery("DELETE FROM {h-schema}utxo_visible_transaction_output").executeUpdate()
304312

305313
createTransactionEntity(entityFactory, transaction1, status = VERIFIED).also { em.persist(it) }
@@ -334,9 +342,9 @@ class UtxoPersistenceServiceImplTest {
334342
)
335343
)
336344

337-
repository.persistVisibleTransactionOutputs(em, transaction1.id.toString(), Instant.now(), outputs)
338-
repository.persistVisibleTransactionOutputs(em, transaction2.id.toString(), Instant.now(), outputs2)
339-
repository.markTransactionVisibleStatesConsumed(em, listOf(StateRef(transaction2.id, 1)), Instant.now())
345+
repository.persistVisibleTransactionOutputs(conn, transaction1.id.toString(), Instant.now(), outputs)
346+
repository.persistVisibleTransactionOutputs(conn, transaction2.id.toString(), Instant.now(), outputs2)
347+
repository.markTransactionVisibleStatesConsumed(conn, listOf(StateRef(transaction2.id, 1)), Instant.now())
340348
}
341349

342350
val stateClass = TestContractState2::class.java
@@ -1302,7 +1310,11 @@ class UtxoPersistenceServiceImplTest {
13021310

13031311
val visibleOutputIndexes = listOf(0)
13041312
// prove that the output of filtered tx with index 0 is used as an input
1305-
val indexes = repository.findConsumedTransactionSourcesForTransaction(em, transactionIdString, visibleOutputIndexes)
1313+
val indexes = repository.findConsumedTransactionSourcesForTransaction(
1314+
getConnection(em),
1315+
transactionIdString,
1316+
visibleOutputIndexes
1317+
)
13061318
assertThat(indexes).contains(0)
13071319
}
13081320

@@ -1404,7 +1416,7 @@ class UtxoPersistenceServiceImplTest {
14041416

14051417
val visibleOutputIndexes = listOf(0, 1)
14061418
// prove that the output of unverified tx is not consumed in other tx
1407-
val indexes = repository.findConsumedTransactionSourcesForTransaction(em, txAId, visibleOutputIndexes)
1419+
val indexes = repository.findConsumedTransactionSourcesForTransaction(getConnection(em), txAId, visibleOutputIndexes)
14081420
assertThat(indexes).isEmpty()
14091421
}
14101422

@@ -1435,7 +1447,11 @@ class UtxoPersistenceServiceImplTest {
14351447

14361448
val visibleOutputIndexes = listOf(0)
14371449
// prove that the output of filtered tx with index 0 is used as an input
1438-
val indexes = repository.findConsumedTransactionSourcesForTransaction(em, transactionIdString, visibleOutputIndexes)
1450+
val indexes = repository.findConsumedTransactionSourcesForTransaction(
1451+
getConnection(em),
1452+
transactionIdString,
1453+
visibleOutputIndexes
1454+
)
14391455
assertThat(indexes).contains(0)
14401456
}
14411457

Diff for: components/ledger/ledger-persistence/src/main/kotlin/net/corda/ledger/persistence/utxo/impl/UtxoRequestHandlerSelectorImpl.kt

+6-1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ import net.corda.persistence.common.getSerializationService
4747
import net.corda.sandboxgroupcontext.SandboxGroupContext
4848
import net.corda.sandboxgroupcontext.getSandboxSingletonService
4949
import net.corda.utilities.time.UTCClock
50+
import org.hibernate.Session
51+
import org.hibernate.internal.SessionImpl
5052
import org.osgi.service.component.annotations.Activate
5153
import org.osgi.service.component.annotations.Component
5254
import org.osgi.service.component.annotations.Reference
@@ -63,7 +65,10 @@ class UtxoRequestHandlerSelectorImpl @Activate constructor(
6365
@Suppress("LongMethod")
6466
override fun selectHandler(sandbox: SandboxGroupContext, request: LedgerPersistenceRequest): RequestHandler {
6567
val persistenceService = UtxoPersistenceServiceImpl(
66-
entityManagerFactory = sandbox.getEntityManagerFactory(),
68+
connectionFactory = {
69+
val emf = sandbox.getEntityManagerFactory()
70+
(emf.createEntityManager().unwrap(Session::class.java) as SessionImpl).connection()
71+
},
6772
repository = sandbox.getSandboxSingletonService(),
6873
serializationService = sandbox.getSerializationService(),
6974
sandboxDigestService = sandbox.getSandboxSingletonService(),

Diff for: components/ledger/ledger-persistence/src/test/kotlin/net/corda/ledger/persistence/utxo/UtxoComponentGroupMapperTest.kt

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class UtxoComponentGroupMapperTest {
1919
private fun mockTuple(values: List<Any>) =
2020
mock<Tuple>().apply {
2121
whenever(this.get(anyInt())).thenAnswer { invocation -> values[invocation.arguments[0] as Int] }
22+
whenever(this.toArray()).thenAnswer { values.toTypedArray() }
2223
}
2324

2425
@Test

Diff for: components/ledger/ledger-persistence/src/test/kotlin/net/corda/ledger/persistence/utxo/impl/UtxoPersistenceServiceImplTest.kt

+15-5
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import net.corda.v5.ledger.utxo.StateRef
2828
import net.corda.v5.ledger.utxo.TransactionState
2929
import net.corda.v5.ledger.utxo.query.json.ContractStateVaultJsonFactory
3030
import org.assertj.core.api.Assertions.assertThat
31+
import org.hibernate.Session
32+
import org.hibernate.internal.SessionImpl
3133
import org.junit.jupiter.api.Assertions.assertEquals
3234
import org.junit.jupiter.api.BeforeEach
3335
import org.junit.jupiter.api.Test
@@ -37,8 +39,8 @@ import org.mockito.kotlin.doAnswer
3739
import org.mockito.kotlin.doReturn
3840
import org.mockito.kotlin.mock
3941
import org.mockito.kotlin.whenever
40-
import java.lang.IllegalArgumentException
4142
import java.security.PublicKey
43+
import java.sql.Connection
4244
import javax.persistence.EntityManager
4345
import javax.persistence.EntityManagerFactory
4446

@@ -73,16 +75,24 @@ class UtxoPersistenceServiceImplTest {
7375
registerJsonFactory(InvalidStateJsonFactory() as ContractStateVaultJsonFactory<ContractState>)
7476
}
7577

78+
private val connectionMock = mock<Connection> {
79+
}
80+
81+
private val mockSession = mock<SessionImpl> {
82+
on { connection() } doReturn mock()
83+
}
84+
7685
private val mockEm = mock<EntityManager> {
7786
on { transaction } doReturn mock()
87+
on { unwrap(Session::class.java) } doReturn mockSession
7888
}
7989

8090
private val mockEmFactory = mock<EntityManagerFactory> {
8191
on { createEntityManager() }.doReturn(mockEm)
8292
}
8393

8494
private val persistenceService = UtxoPersistenceServiceImpl(
85-
mockEmFactory,
95+
{ connectionMock },
8696
mockRepository,
8797
mock(),
8898
mockDigestService,
@@ -140,7 +150,7 @@ class UtxoPersistenceServiceImplTest {
140150
whenever(emptyDefaultContractStateVaultJsonFactory.create(any(), any())).thenReturn("")
141151

142152
val singlePersistenceService = UtxoPersistenceServiceImpl(
143-
mockEmFactory,
153+
{ connectionMock },
144154
mockRepository,
145155
mock(),
146156
mockDigestService,
@@ -236,7 +246,7 @@ class UtxoPersistenceServiceImplTest {
236246
@Test
237247
fun `Persisting a transaction while zero JSON factories are registered will result still store the default state json`() {
238248
val emptyPersistenceService = UtxoPersistenceServiceImpl(
239-
mockEmFactory,
249+
{ connectionMock },
240250
mockRepository,
241251
mock(),
242252
mockDigestService,
@@ -281,7 +291,7 @@ class UtxoPersistenceServiceImplTest {
281291
}
282292

283293
val persistenceService = UtxoPersistenceServiceImpl(
284-
mockEmFactory,
294+
{ connectionMock },
285295
mockRepository,
286296
mock(),
287297
mockDigestService,

Diff for: components/ledger/ledger-utxo-flow/src/main/kotlin/net/corda/ledger/utxo/flow/impl/flows/backchain/TransactionBackchainVerifierImpl.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class TransactionBackchainVerifierImpl @Activate constructor(
4646
val (transaction, status) = utxoLedgerPersistenceService.findSignedLedgerTransactionWithStatus(
4747
transactionId,
4848
UNVERIFIED
49-
) ?: throw CordaRuntimeException("Transaction does not exist locally")
49+
) ?: throw CordaRuntimeException("Transaction ($UNVERIFIED) with id: $transactionId does not exist locally")
5050
when (status) {
5151
INVALID -> {
5252
log.warn(

Diff for: libs/db/db-core/src/main/kotlin/net/corda/db/core/utils/ConnectionUtils.kt

+15-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package net.corda.db.core.utils
22

3+
import org.slf4j.LoggerFactory
34
import java.sql.Connection
45

6+
private val log = LoggerFactory.getLogger("ConnectionUtils")
7+
58
/**
69
* Executes [block] in a transaction using the [Connection].
710
*
8-
* Commits transaction if no exceptions were thrown by [block]. Otherwise rolls back the transaction.
11+
* Commits transaction if no exceptions were thrown by [block]. Otherwise, rolls back the transaction.
912
*
1013
* Finally closes the connection after committing or rolling back the changes.
1114
*
@@ -14,12 +17,21 @@ import java.sql.Connection
1417
*
1518
* @return The result of executing [block].
1619
*/
17-
inline fun <R> Connection.transaction(block: (Connection) -> R): R {
20+
fun <R> Connection.transaction(block: (Connection) -> R): R {
21+
return transactionWithLogging(null, block)
22+
}
23+
24+
fun <R> Connection.transactionWithLogging(name: String?, block: (Connection) -> R): R {
25+
if(null != name && log.isTraceEnabled) log.trace("Start transaction $name")
1826
autoCommit = false
1927
return try {
20-
block(this).also { commit() }
28+
block(this).also {
29+
commit()
30+
if(null != name && log.isTraceEnabled) log.trace("Transaction $name committed")
31+
}
2132
} catch (e: Exception) {
2233
rollback()
34+
if(null != name && log.isWarnEnabled) log.error("Transaction $name rolled back")
2335
throw e
2436
} finally {
2537
close()

Diff for: libs/ledger-lib-persistence/build.gradle

-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ dependencies {
1717
implementation project(':libs:ledger:ledger-common-data')
1818
implementation project(':libs:ledger:ledger-utxo-data')
1919
implementation project(":libs:db:db-core")
20-
implementation project(":libs:db:db-orm")
2120
implementation project(":libs:utilities")
2221
implementation project(":libs:serialization:json-validator-lib")
2322

@@ -26,7 +25,6 @@ dependencies {
2625
implementation 'net.corda:corda-ledger-common'
2726
implementation 'net.corda:corda-ledger-utxo'
2827
implementation libs.slf4j.api
29-
implementation "org.hibernate:hibernate-core:$hibernateVersion"
3028
implementation 'org.jetbrains.kotlin:kotlin-stdlib'
3129
implementation libs.jackson.core
3230
}

Diff for: libs/ledger-lib-persistence/src/main/kotlin/net/corda/ledger/libs/persistence/common/ComponentGroupMapper.kt

+6
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,10 @@ interface ComponentGroupMapper {
88
fun map(tuples: List<Tuple>): Map<Int, List<ByteArray>>
99
}
1010

11+
interface ComponentGroupArrayMapper {
12+
@Throws(SQLException::class)
13+
fun mapColumns(tuples: List<Array<Any?>>): Map<Int, List<ByteArray>>
14+
}
15+
1116
fun List<Tuple>.mapToComponentGroups(mapper: ComponentGroupMapper): Map<Int, List<ByteArray>> = mapper.map(this)
17+
fun List<Array<Any?>>.mapToComponentGroups(mapper: ComponentGroupArrayMapper): Map<Int, List<ByteArray>> = mapper.mapColumns(this)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package net.corda.ledger.libs.persistence.util
2+
3+
interface NamedParamQuery {
4+
companion object {
5+
fun from(sql: String): NamedParamQuery {
6+
val plainSql = StringBuilder()
7+
val fields = mutableMapOf<String, Int>()
8+
var marker: StringBuilder? = null
9+
var markerIndex = 0
10+
for (i in sql.indices) {
11+
val c = sql[i]
12+
if (c == ':' && (i < sql.length - 1 && sql[i + 1].isValidTokenChar())) {
13+
marker = StringBuilder()
14+
markerIndex++
15+
plainSql.append('?')
16+
continue
17+
}
18+
19+
if (null != marker) {
20+
if (!c.isValidTokenChar()) {
21+
fields[marker.toString()] = markerIndex
22+
marker = null
23+
} else {
24+
marker.append(c)
25+
continue
26+
}
27+
}
28+
plainSql.append(c)
29+
}
30+
31+
if (null != marker) {
32+
fields[marker.toString()] = markerIndex
33+
}
34+
35+
return NamedParamQueryImpl(plainSql.toString(), fields)
36+
}
37+
38+
private fun Char.isValidTokenChar(): Boolean = this.isLetterOrDigit() || this == '_' || this == '-'
39+
}
40+
41+
val sql: String
42+
val fields: Map<String, Int>
43+
44+
private class NamedParamQueryImpl(
45+
override val sql: String,
46+
override val fields: Map<String, Int>,
47+
) : NamedParamQuery
48+
}

0 commit comments

Comments
 (0)