mirror of
https://github.com/corda/corda.git
synced 2025-06-14 13:18:18 +00:00
ENT-5264 Synchronise schema on the command line (#6353)
* Decouple DatabaseConfig and CordaPersistence etc. * Add schema sync to schema migration + test * Add command line parameters for synchronising schema
This commit is contained in:
@ -88,7 +88,7 @@ fun <T> withoutDatabaseAccess(block: () -> T): T {
|
||||
val contextDatabaseOrNull: CordaPersistence? get() = _contextDatabase.get()
|
||||
|
||||
class CordaPersistence(
|
||||
databaseConfig: DatabaseConfig,
|
||||
exportHibernateJMXStatistics: Boolean,
|
||||
schemas: Set<MappedSchema>,
|
||||
val jdbcUrl: String,
|
||||
cacheFactory: NamedCacheFactory,
|
||||
@ -106,7 +106,7 @@ class CordaPersistence(
|
||||
val hibernateConfig: HibernateConfiguration by lazy {
|
||||
transaction {
|
||||
try {
|
||||
HibernateConfiguration(schemas, databaseConfig, attributeConverters, jdbcUrl, cacheFactory, customClassLoader, allowHibernateToManageAppSchema)
|
||||
HibernateConfiguration(schemas, exportHibernateJMXStatistics, attributeConverters, jdbcUrl, cacheFactory, customClassLoader, allowHibernateToManageAppSchema)
|
||||
} catch (e: Exception) {
|
||||
when (e) {
|
||||
is SchemaManagementException -> throw HibernateSchemaChangeException("Incompatible schema change detected. Please run schema migration scripts (node with sub-command run-migration-scripts). Reason: ${e.message}", e)
|
||||
|
@ -19,7 +19,7 @@ import javax.persistence.AttributeConverter
|
||||
|
||||
class HibernateConfiguration(
|
||||
schemas: Set<MappedSchema>,
|
||||
private val databaseConfig: DatabaseConfig,
|
||||
private val exportHibernateJMXStatistics: Boolean,
|
||||
private val attributeConverters: Collection<AttributeConverter<*, *>>,
|
||||
jdbcUrl: String,
|
||||
cacheFactory: NamedCacheFactory,
|
||||
@ -65,10 +65,10 @@ class HibernateConfiguration(
|
||||
fun sessionFactoryForSchemas(key: Set<MappedSchema>): SessionFactory = sessionFactories.get(key, ::makeSessionFactoryForSchemas)!!
|
||||
|
||||
private fun makeSessionFactoryForSchemas(schemas: Set<MappedSchema>): SessionFactory {
|
||||
val sessionFactory = sessionFactoryFactory.makeSessionFactoryForSchemas(databaseConfig, schemas, customClassLoader, attributeConverters, allowHibernateToManageAppSchema)
|
||||
val sessionFactory = sessionFactoryFactory.makeSessionFactoryForSchemas(schemas, customClassLoader, attributeConverters, allowHibernateToManageAppSchema)
|
||||
|
||||
// export Hibernate JMX statistics
|
||||
if (databaseConfig.exportHibernateJMXStatistics)
|
||||
if (exportHibernateJMXStatistics)
|
||||
initStatistics(sessionFactory)
|
||||
|
||||
return sessionFactory
|
||||
|
@ -93,6 +93,32 @@ open class SchemaMigration(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronises the changelog table with the schema descriptions passed in without applying any of the changes to the database.
|
||||
* This can be used when migrating a CorDapp that had its schema generated by hibernate to liquibase schema migration, or when
|
||||
* updating from a version of Corda that does not use liquibase for CorDapps
|
||||
* **Warning** - this will not check if the matching schema changes have been applied, it will just generate the changelog
|
||||
* It must not be run on a newly installed CorDapp.
|
||||
* @param schemas The set of schemas to add to the changelog
|
||||
* @param forceThrowOnMissingMigration throw an exception if a mapped schema is missing its migration resource
|
||||
*/
|
||||
fun synchroniseSchemas(schemas: Set<MappedSchema>, forceThrowOnMissingMigration: Boolean) {
|
||||
val resourcesAndSourceInfo = prepareResources(schemas, forceThrowOnMissingMigration)
|
||||
|
||||
// current version of Liquibase appears to be non-threadsafe
|
||||
// this is apparent when multiple in-process nodes are all running migrations simultaneously
|
||||
mutex.withLock {
|
||||
dataSource.connection.use { connection ->
|
||||
val (runner, _, _) = prepareRunner(connection, resourcesAndSourceInfo)
|
||||
try {
|
||||
runner.changeLogSync(Contexts().toString())
|
||||
} catch (exp: LiquibaseException) {
|
||||
throw DatabaseMigrationException(exp.message, exp)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Create a resource accessor that aggregates the changelogs included in the schemas into one dynamic stream. */
|
||||
protected class CustomResourceAccessor(val dynamicInclude: String, val changelogList: List<String?>, classLoader: ClassLoader) :
|
||||
ClassLoaderResourceAccessor(classLoader) {
|
||||
|
@ -3,7 +3,6 @@ package net.corda.nodeapi.internal.persistence.factory
|
||||
import net.corda.core.schemas.MappedSchema
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.core.utilities.toHexString
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import net.corda.nodeapi.internal.persistence.HibernateConfiguration
|
||||
import net.corda.nodeapi.internal.persistence.TransactionIsolationLevel
|
||||
import org.hibernate.SessionFactory
|
||||
@ -26,7 +25,7 @@ abstract class BaseSessionFactoryFactory : CordaSessionFactoryFactory {
|
||||
private val logger = contextLogger()
|
||||
}
|
||||
|
||||
open fun buildHibernateConfig(databaseConfig: DatabaseConfig, metadataSources: MetadataSources, allowHibernateToManageAppSchema: Boolean): Configuration {
|
||||
open fun buildHibernateConfig(metadataSources: MetadataSources, allowHibernateToManageAppSchema: Boolean): Configuration {
|
||||
val hbm2dll: String =
|
||||
if (allowHibernateToManageAppSchema) {
|
||||
"update"
|
||||
@ -82,7 +81,6 @@ abstract class BaseSessionFactoryFactory : CordaSessionFactoryFactory {
|
||||
}
|
||||
|
||||
final override fun makeSessionFactoryForSchemas(
|
||||
databaseConfig: DatabaseConfig,
|
||||
schemas: Set<MappedSchema>,
|
||||
customClassLoader: ClassLoader?,
|
||||
attributeConverters: Collection<AttributeConverter<*, *>>,
|
||||
@ -91,7 +89,7 @@ abstract class BaseSessionFactoryFactory : CordaSessionFactoryFactory {
|
||||
val serviceRegistry = BootstrapServiceRegistryBuilder().build()
|
||||
val metadataSources = MetadataSources(serviceRegistry)
|
||||
|
||||
val config = buildHibernateConfig(databaseConfig, metadataSources, allowHibernateToMananageAppSchema)
|
||||
val config = buildHibernateConfig(metadataSources, allowHibernateToMananageAppSchema)
|
||||
schemas.forEach { schema ->
|
||||
schema.mappedTypes.forEach { config.addAnnotatedClass(it) }
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package net.corda.nodeapi.internal.persistence.factory
|
||||
|
||||
import net.corda.core.schemas.MappedSchema
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import org.hibernate.SessionFactory
|
||||
import org.hibernate.boot.Metadata
|
||||
import org.hibernate.boot.MetadataBuilder
|
||||
@ -11,7 +10,6 @@ interface CordaSessionFactoryFactory {
|
||||
val databaseType: String
|
||||
fun canHandleDatabase(jdbcUrl: String): Boolean
|
||||
fun makeSessionFactoryForSchemas(
|
||||
databaseConfig: DatabaseConfig,
|
||||
schemas: Set<MappedSchema>,
|
||||
customClassLoader: ClassLoader?,
|
||||
attributeConverters: Collection<AttributeConverter<*, *>>,
|
||||
|
@ -14,7 +14,7 @@ class HibernateConfigurationFactoryLoadingTest {
|
||||
val cacheFactory = mock<NamedCacheFactory>()
|
||||
HibernateConfiguration(
|
||||
emptySet(),
|
||||
DatabaseConfig(),
|
||||
false,
|
||||
emptyList(),
|
||||
jdbcUrl,
|
||||
cacheFactory)
|
||||
|
Reference in New Issue
Block a user