mirror of
https://github.com/corda/corda.git
synced 2024-12-20 21:43:14 +00:00
ENT-5134 Discoverable Hibernate Session Factory Factory (#6091)
* Introduce CordaSessionFactoryFactory interface and the H2 implememntation * Load SessionFactoryFactory via service loader * Add Postgres SessionFactoryFactory * Add extraConfiguration function for SessionFactoryFactory implementations to expose special config values.
This commit is contained in:
parent
678fb6eb94
commit
ccca605865
@ -5,24 +5,15 @@ import net.corda.core.internal.NamedCacheFactory
|
||||
import net.corda.core.internal.castIfPossible
|
||||
import net.corda.core.schemas.MappedSchema
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.core.utilities.toHexString
|
||||
import net.corda.nodeapi.internal.persistence.factory.CordaSessionFactoryFactory
|
||||
import org.hibernate.SessionFactory
|
||||
import org.hibernate.boot.Metadata
|
||||
import org.hibernate.boot.MetadataBuilder
|
||||
import org.hibernate.boot.MetadataSources
|
||||
import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder
|
||||
import org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl
|
||||
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService
|
||||
import org.hibernate.cfg.Configuration
|
||||
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider
|
||||
import org.hibernate.service.UnknownUnwrapTypeException
|
||||
import org.hibernate.type.AbstractSingleColumnStandardBasicType
|
||||
import org.hibernate.type.MaterializedBlobType
|
||||
import org.hibernate.type.descriptor.java.PrimitiveByteArrayTypeDescriptor
|
||||
import org.hibernate.type.descriptor.sql.BlobTypeDescriptor
|
||||
import org.hibernate.type.descriptor.sql.VarbinaryTypeDescriptor
|
||||
import java.lang.management.ManagementFactory
|
||||
import java.sql.Connection
|
||||
import java.util.ServiceLoader
|
||||
import javax.management.ObjectName
|
||||
import javax.persistence.AttributeConverter
|
||||
|
||||
@ -30,35 +21,38 @@ class HibernateConfiguration(
|
||||
schemas: Set<MappedSchema>,
|
||||
private val databaseConfig: DatabaseConfig,
|
||||
private val attributeConverters: Collection<AttributeConverter<*, *>>,
|
||||
private val jdbcUrl: String,
|
||||
jdbcUrl: String,
|
||||
cacheFactory: NamedCacheFactory,
|
||||
val customClassLoader: ClassLoader? = null
|
||||
) {
|
||||
companion object {
|
||||
private val logger = contextLogger()
|
||||
|
||||
// register custom converters
|
||||
fun buildHibernateMetadata(metadataBuilder: MetadataBuilder, jdbcUrl:String, attributeConverters: Collection<AttributeConverter<*, *>>): Metadata {
|
||||
metadataBuilder.run {
|
||||
attributeConverters.forEach { applyAttributeConverter(it) }
|
||||
// Register a tweaked version of `org.hibernate.type.MaterializedBlobType` that truncates logged messages.
|
||||
// to avoid OOM when large blobs might get logged.
|
||||
applyBasicType(CordaMaterializedBlobType, CordaMaterializedBlobType.name)
|
||||
applyBasicType(CordaWrapperBinaryType, CordaWrapperBinaryType.name)
|
||||
// Will be used in open core
|
||||
fun buildHibernateMetadata(metadataBuilder: MetadataBuilder, jdbcUrl: String, attributeConverters: Collection<AttributeConverter<*, *>>): Metadata {
|
||||
val sff = findSessionFactoryFactory(jdbcUrl, null)
|
||||
return sff.buildHibernateMetadata(metadataBuilder, attributeConverters)
|
||||
}
|
||||
|
||||
// Create a custom type that will map a blob to byteA in postgres and as a normal blob for all other dbms.
|
||||
// This is required for the Checkpoints as a workaround for the issue that postgres has on azure.
|
||||
if (jdbcUrl.contains(":postgresql:", ignoreCase = true)) {
|
||||
applyBasicType(MapBlobToPostgresByteA, MapBlobToPostgresByteA.name)
|
||||
} else {
|
||||
applyBasicType(MapBlobToNormalBlob, MapBlobToNormalBlob.name)
|
||||
}
|
||||
private fun findSessionFactoryFactory(jdbcUrl: String, customClassLoader: ClassLoader?): CordaSessionFactoryFactory {
|
||||
val serviceLoader = if (customClassLoader != null)
|
||||
ServiceLoader.load(CordaSessionFactoryFactory::class.java, customClassLoader)
|
||||
else
|
||||
ServiceLoader.load(CordaSessionFactoryFactory::class.java)
|
||||
|
||||
return build()
|
||||
val sessionFactories = serviceLoader.filter { it.canHandleDatabase(jdbcUrl) }
|
||||
when (sessionFactories.size) {
|
||||
0 -> throw HibernateConfigException("Failed to find a SessionFactoryFactory to handle $jdbcUrl " +
|
||||
"- factories present for ${serviceLoader.map { it.databaseType }}")
|
||||
1 -> return sessionFactories.single()
|
||||
else -> throw HibernateConfigException("Found several SessionFactoryFactory classes to handle $jdbcUrl " +
|
||||
"- classes ${sessionFactories.map { it.javaClass.canonicalName }}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val sessionFactoryFactory = findSessionFactoryFactory(jdbcUrl, customClassLoader)
|
||||
|
||||
private val sessionFactories = cacheFactory.buildNamed<Set<MappedSchema>, SessionFactory>(Caffeine.newBuilder(), "HibernateConfiguration_sessionFactories")
|
||||
|
||||
val sessionFactoryForRegisteredSchemas = schemas.let {
|
||||
@ -70,35 +64,7 @@ class HibernateConfiguration(
|
||||
fun sessionFactoryForSchemas(key: Set<MappedSchema>): SessionFactory = sessionFactories.get(key, ::makeSessionFactoryForSchemas)!!
|
||||
|
||||
private fun makeSessionFactoryForSchemas(schemas: Set<MappedSchema>): SessionFactory {
|
||||
logger.info("Creating session factory for schemas: $schemas")
|
||||
val serviceRegistry = BootstrapServiceRegistryBuilder().build()
|
||||
val metadataSources = MetadataSources(serviceRegistry)
|
||||
|
||||
val hbm2dll: String =
|
||||
if(databaseConfig.initialiseSchema && databaseConfig.initialiseAppSchema == SchemaInitializationType.UPDATE) {
|
||||
"update"
|
||||
} else if((!databaseConfig.initialiseSchema && databaseConfig.initialiseAppSchema == SchemaInitializationType.UPDATE)
|
||||
|| databaseConfig.initialiseAppSchema == SchemaInitializationType.VALIDATE) {
|
||||
"validate"
|
||||
} else {
|
||||
"none"
|
||||
}
|
||||
|
||||
// We set a connection provider as the auto schema generation requires it. The auto schema generation will not
|
||||
// necessarily remain and would likely be replaced by something like Liquibase. For now it is very convenient though.
|
||||
val config = Configuration(metadataSources).setProperty("hibernate.connection.provider_class", NodeDatabaseConnectionProvider::class.java.name)
|
||||
.setProperty("hibernate.format_sql", "true")
|
||||
.setProperty("hibernate.hbm2ddl.auto", hbm2dll)
|
||||
.setProperty("javax.persistence.validation.mode", "none")
|
||||
.setProperty("hibernate.connection.isolation", databaseConfig.transactionIsolationLevel.jdbcValue.toString())
|
||||
|
||||
schemas.forEach { schema ->
|
||||
// TODO: require mechanism to set schemaOptions (databaseSchema, tablePrefix) which are not global to session
|
||||
schema.mappedTypes.forEach { config.addAnnotatedClass(it) }
|
||||
}
|
||||
|
||||
val sessionFactory = buildSessionFactory(config, metadataSources, customClassLoader)
|
||||
logger.info("Created session factory for schemas: $schemas")
|
||||
val sessionFactory = sessionFactoryFactory.makeSessionFactoryForSchemas(databaseConfig, schemas, customClassLoader, attributeConverters)
|
||||
|
||||
// export Hibernate JMX statistics
|
||||
if (databaseConfig.exportHibernateJMXStatistics)
|
||||
@ -123,27 +89,6 @@ class HibernateConfiguration(
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildSessionFactory(config: Configuration, metadataSources: MetadataSources, customClassLoader: ClassLoader?): SessionFactory {
|
||||
config.standardServiceRegistryBuilder.applySettings(config.properties)
|
||||
|
||||
if (customClassLoader != null) {
|
||||
config.standardServiceRegistryBuilder.addService(
|
||||
ClassLoaderService::class.java,
|
||||
ClassLoaderServiceImpl(customClassLoader))
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
val metadataBuilder = metadataSources.getMetadataBuilder(config.standardServiceRegistryBuilder.build())
|
||||
val metadata = buildHibernateMetadata(metadataBuilder, jdbcUrl, attributeConverters)
|
||||
return metadata.sessionFactoryBuilder.run {
|
||||
allowOutOfTransactionUpdateOperations(true)
|
||||
applySecondLevelCacheSupport(false)
|
||||
applyQueryCacheSupport(false)
|
||||
enableReleaseResourcesOnCloseEnabled(true)
|
||||
build()
|
||||
}
|
||||
}
|
||||
|
||||
// Supply Hibernate with connections from our underlying Exposed database integration. Only used
|
||||
// during schema creation / update.
|
||||
class NodeDatabaseConnectionProvider : ConnectionProvider {
|
||||
@ -168,55 +113,5 @@ class HibernateConfiguration(
|
||||
override fun isUnwrappableAs(unwrapType: Class<*>?): Boolean = unwrapType == NodeDatabaseConnectionProvider::class.java
|
||||
}
|
||||
|
||||
// A tweaked version of `org.hibernate.type.MaterializedBlobType` that truncates logged messages. Also logs in hex.
|
||||
object CordaMaterializedBlobType : AbstractSingleColumnStandardBasicType<ByteArray>(BlobTypeDescriptor.DEFAULT, CordaPrimitiveByteArrayTypeDescriptor) {
|
||||
override fun getName(): String {
|
||||
return "materialized_blob"
|
||||
}
|
||||
}
|
||||
|
||||
// A tweaked version of `org.hibernate.type.descriptor.java.PrimitiveByteArrayTypeDescriptor` that truncates logged messages.
|
||||
private object CordaPrimitiveByteArrayTypeDescriptor : PrimitiveByteArrayTypeDescriptor() {
|
||||
private const val LOG_SIZE_LIMIT = 1024
|
||||
|
||||
override fun extractLoggableRepresentation(value: ByteArray?): String {
|
||||
return if (value == null) {
|
||||
super.extractLoggableRepresentation(value)
|
||||
} else {
|
||||
if (value.size <= LOG_SIZE_LIMIT) {
|
||||
"[size=${value.size}, value=${value.toHexString()}]"
|
||||
} else {
|
||||
"[size=${value.size}, value=${value.copyOfRange(0, LOG_SIZE_LIMIT).toHexString()}...truncated...]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A tweaked version of `org.hibernate.type.WrapperBinaryType` that deals with ByteArray (java primitive byte[] type).
|
||||
object CordaWrapperBinaryType : AbstractSingleColumnStandardBasicType<ByteArray>(VarbinaryTypeDescriptor.INSTANCE, PrimitiveByteArrayTypeDescriptor.INSTANCE) {
|
||||
override fun getRegistrationKeys(): Array<String> {
|
||||
return arrayOf(name, "ByteArray", ByteArray::class.java.name)
|
||||
}
|
||||
|
||||
override fun getName(): String {
|
||||
return "corda-wrapper-binary"
|
||||
}
|
||||
}
|
||||
|
||||
// Maps to a byte array on postgres.
|
||||
object MapBlobToPostgresByteA : AbstractSingleColumnStandardBasicType<ByteArray>(VarbinaryTypeDescriptor.INSTANCE, PrimitiveByteArrayTypeDescriptor.INSTANCE) {
|
||||
override fun getRegistrationKeys(): Array<String> {
|
||||
return arrayOf(name, "ByteArray", ByteArray::class.java.name)
|
||||
}
|
||||
|
||||
override fun getName(): String {
|
||||
return "corda-blob"
|
||||
}
|
||||
}
|
||||
|
||||
object MapBlobToNormalBlob : MaterializedBlobType() {
|
||||
override fun getName(): String {
|
||||
return "corda-blob"
|
||||
}
|
||||
}
|
||||
fun getExtraConfiguration(key: String ) = sessionFactoryFactory.getExtraConfiguration(key)
|
||||
}
|
||||
|
@ -0,0 +1,138 @@
|
||||
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 org.hibernate.SessionFactory
|
||||
import org.hibernate.boot.Metadata
|
||||
import org.hibernate.boot.MetadataBuilder
|
||||
import org.hibernate.boot.MetadataSources
|
||||
import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder
|
||||
import org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl
|
||||
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService
|
||||
import org.hibernate.cfg.Configuration
|
||||
import org.hibernate.type.AbstractSingleColumnStandardBasicType
|
||||
import org.hibernate.type.MaterializedBlobType
|
||||
import org.hibernate.type.descriptor.java.PrimitiveByteArrayTypeDescriptor
|
||||
import org.hibernate.type.descriptor.sql.BlobTypeDescriptor
|
||||
import org.hibernate.type.descriptor.sql.VarbinaryTypeDescriptor
|
||||
import javax.persistence.AttributeConverter
|
||||
|
||||
abstract class BaseSessionFactoryFactory : CordaSessionFactoryFactory {
|
||||
companion object {
|
||||
private val logger = contextLogger()
|
||||
}
|
||||
|
||||
open fun buildHibernateConfig(databaseConfig: DatabaseConfig, metadataSources: MetadataSources): Configuration {
|
||||
// We set a connection provider as the auto schema generation requires it. The auto schema generation will not
|
||||
// necessarily remain and would likely be replaced by something like Liquibase. For now it is very convenient though.
|
||||
return Configuration(metadataSources).setProperty("hibernate.connection.provider_class", HibernateConfiguration.NodeDatabaseConnectionProvider::class.java.name)
|
||||
.setProperty("hibernate.format_sql", "true")
|
||||
.setProperty("javax.persistence.validation.mode", "none")
|
||||
.setProperty("hibernate.connection.isolation", databaseConfig.transactionIsolationLevel.jdbcValue.toString())
|
||||
.setProperty("hibernate.hbm2ddl.auto", "validate")
|
||||
}
|
||||
|
||||
override fun buildHibernateMetadata(metadataBuilder: MetadataBuilder, attributeConverters: Collection<AttributeConverter<*, *>>): Metadata {
|
||||
return metadataBuilder.run {
|
||||
attributeConverters.forEach { applyAttributeConverter(it) }
|
||||
// Register a tweaked version of `org.hibernate.type.MaterializedBlobType` that truncates logged messages.
|
||||
// to avoid OOM when large blobs might get logged.
|
||||
applyBasicType(CordaMaterializedBlobType, CordaMaterializedBlobType.name)
|
||||
applyBasicType(CordaWrapperBinaryType, CordaWrapperBinaryType.name)
|
||||
applyBasicType(MapBlobToNormalBlob, MapBlobToNormalBlob.name)
|
||||
|
||||
build()
|
||||
}
|
||||
}
|
||||
|
||||
fun buildSessionFactory(
|
||||
config: Configuration,
|
||||
metadataSources: MetadataSources,
|
||||
customClassLoader: ClassLoader?,
|
||||
attributeConverters: Collection<AttributeConverter<*, *>>): SessionFactory {
|
||||
config.standardServiceRegistryBuilder.applySettings(config.properties)
|
||||
|
||||
if (customClassLoader != null) {
|
||||
config.standardServiceRegistryBuilder.addService(
|
||||
ClassLoaderService::class.java,
|
||||
ClassLoaderServiceImpl(customClassLoader))
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
val metadataBuilder = metadataSources.getMetadataBuilder(config.standardServiceRegistryBuilder.build())
|
||||
val metadata = buildHibernateMetadata(metadataBuilder, attributeConverters)
|
||||
return metadata.sessionFactoryBuilder.run {
|
||||
allowOutOfTransactionUpdateOperations(true)
|
||||
applySecondLevelCacheSupport(false)
|
||||
applyQueryCacheSupport(false)
|
||||
enableReleaseResourcesOnCloseEnabled(true)
|
||||
build()
|
||||
}
|
||||
}
|
||||
|
||||
final override fun makeSessionFactoryForSchemas(
|
||||
databaseConfig: DatabaseConfig,
|
||||
schemas: Set<MappedSchema>,
|
||||
customClassLoader: ClassLoader?,
|
||||
attributeConverters: Collection<AttributeConverter<*, *>>): SessionFactory {
|
||||
logger.info("Creating session factory for schemas: $schemas")
|
||||
val serviceRegistry = BootstrapServiceRegistryBuilder().build()
|
||||
val metadataSources = MetadataSources(serviceRegistry)
|
||||
|
||||
val config = buildHibernateConfig(databaseConfig, metadataSources)
|
||||
schemas.forEach { schema ->
|
||||
schema.mappedTypes.forEach { config.addAnnotatedClass(it) }
|
||||
}
|
||||
val sessionFactory = buildSessionFactory(config, metadataSources, customClassLoader, attributeConverters)
|
||||
logger.info("Created session factory for schemas: $schemas")
|
||||
return sessionFactory
|
||||
}
|
||||
|
||||
override fun getExtraConfiguration(key: String): Any? {
|
||||
return null
|
||||
}
|
||||
|
||||
// A tweaked version of `org.hibernate.type.WrapperBinaryType` that deals with ByteArray (java primitive byte[] type).
|
||||
object CordaWrapperBinaryType : AbstractSingleColumnStandardBasicType<ByteArray>(VarbinaryTypeDescriptor.INSTANCE, PrimitiveByteArrayTypeDescriptor.INSTANCE) {
|
||||
override fun getRegistrationKeys(): Array<String> {
|
||||
return arrayOf(name, "ByteArray", ByteArray::class.java.name)
|
||||
}
|
||||
|
||||
override fun getName(): String {
|
||||
return "corda-wrapper-binary"
|
||||
}
|
||||
}
|
||||
|
||||
object MapBlobToNormalBlob : MaterializedBlobType() {
|
||||
override fun getName(): String {
|
||||
return "corda-blob"
|
||||
}
|
||||
}
|
||||
|
||||
// A tweaked version of `org.hibernate.type.descriptor.java.PrimitiveByteArrayTypeDescriptor` that truncates logged messages.
|
||||
private object CordaPrimitiveByteArrayTypeDescriptor : PrimitiveByteArrayTypeDescriptor() {
|
||||
private const val LOG_SIZE_LIMIT = 1024
|
||||
|
||||
override fun extractLoggableRepresentation(value: ByteArray?): String {
|
||||
return if (value == null) {
|
||||
super.extractLoggableRepresentation(value)
|
||||
} else {
|
||||
if (value.size <= LOG_SIZE_LIMIT) {
|
||||
"[size=${value.size}, value=${value.toHexString()}]"
|
||||
} else {
|
||||
"[size=${value.size}, value=${value.copyOfRange(0, LOG_SIZE_LIMIT).toHexString()}...truncated...]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A tweaked version of `org.hibernate.type.MaterializedBlobType` that truncates logged messages. Also logs in hex.
|
||||
object CordaMaterializedBlobType : AbstractSingleColumnStandardBasicType<ByteArray>(BlobTypeDescriptor.DEFAULT, CordaPrimitiveByteArrayTypeDescriptor) {
|
||||
override fun getName(): String {
|
||||
return "materialized_blob"
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
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
|
||||
import javax.persistence.AttributeConverter
|
||||
|
||||
interface CordaSessionFactoryFactory {
|
||||
val databaseType: String
|
||||
fun canHandleDatabase(jdbcUrl: String): Boolean
|
||||
fun makeSessionFactoryForSchemas(
|
||||
databaseConfig: DatabaseConfig,
|
||||
schemas: Set<MappedSchema>,
|
||||
customClassLoader: ClassLoader?,
|
||||
attributeConverters: Collection<AttributeConverter<*, *>>): SessionFactory
|
||||
fun getExtraConfiguration(key: String): Any?
|
||||
fun buildHibernateMetadata(metadataBuilder: MetadataBuilder, attributeConverters: Collection<AttributeConverter<*, *>>): Metadata
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package net.corda.nodeapi.internal.persistence.factory
|
||||
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import net.corda.nodeapi.internal.persistence.SchemaInitializationType
|
||||
import org.hibernate.boot.MetadataSources
|
||||
import org.hibernate.cfg.Configuration
|
||||
|
||||
class H2SessionFactoryFactory : BaseSessionFactoryFactory() {
|
||||
override fun canHandleDatabase(jdbcUrl: String): Boolean = jdbcUrl.startsWith("jdbc:h2:")
|
||||
override val databaseType: String = "H2"
|
||||
|
||||
override fun buildHibernateConfig(databaseConfig: DatabaseConfig, metadataSources: MetadataSources): Configuration {
|
||||
val config = super.buildHibernateConfig(databaseConfig, metadataSources)
|
||||
|
||||
val hbm2dll: String =
|
||||
if (databaseConfig.initialiseSchema && databaseConfig.initialiseAppSchema == SchemaInitializationType.UPDATE) {
|
||||
"update"
|
||||
} else if ((!databaseConfig.initialiseSchema && databaseConfig.initialiseAppSchema == SchemaInitializationType.UPDATE)
|
||||
|| databaseConfig.initialiseAppSchema == SchemaInitializationType.VALIDATE) {
|
||||
"validate"
|
||||
} else {
|
||||
"none"
|
||||
}
|
||||
config.setProperty("hibernate.hbm2ddl.auto", hbm2dll)
|
||||
return config
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package net.corda.nodeapi.internal.persistence.factory
|
||||
|
||||
import org.hibernate.boot.Metadata
|
||||
import org.hibernate.boot.MetadataBuilder
|
||||
import org.hibernate.type.AbstractSingleColumnStandardBasicType
|
||||
import org.hibernate.type.descriptor.java.PrimitiveByteArrayTypeDescriptor
|
||||
import org.hibernate.type.descriptor.sql.VarbinaryTypeDescriptor
|
||||
import javax.persistence.AttributeConverter
|
||||
|
||||
class PostgresSessionFactoryFactory : BaseSessionFactoryFactory() {
|
||||
override fun buildHibernateMetadata(metadataBuilder: MetadataBuilder, attributeConverters: Collection<AttributeConverter<*, *>>): Metadata {
|
||||
return metadataBuilder.run {
|
||||
attributeConverters.forEach { applyAttributeConverter(it) }
|
||||
// Register a tweaked version of `org.hibernate.type.MaterializedBlobType` that truncates logged messages.
|
||||
// to avoid OOM when large blobs might get logged.
|
||||
applyBasicType(CordaMaterializedBlobType, CordaMaterializedBlobType.name)
|
||||
applyBasicType(CordaWrapperBinaryType, CordaWrapperBinaryType.name)
|
||||
|
||||
// Create a custom type that will map a blob to byteA in postgres
|
||||
// This is required for the Checkpoints as a workaround for the issue that postgres has on azure.
|
||||
applyBasicType(MapBlobToPostgresByteA, MapBlobToPostgresByteA.name)
|
||||
|
||||
build()
|
||||
}
|
||||
}
|
||||
|
||||
override fun canHandleDatabase(jdbcUrl: String): Boolean = jdbcUrl.contains(":postgresql:")
|
||||
|
||||
// Maps to a byte array on postgres.
|
||||
object MapBlobToPostgresByteA : AbstractSingleColumnStandardBasicType<ByteArray>(VarbinaryTypeDescriptor.INSTANCE, PrimitiveByteArrayTypeDescriptor.INSTANCE) {
|
||||
override fun getRegistrationKeys(): Array<String> {
|
||||
return arrayOf(name, "ByteArray", ByteArray::class.java.name)
|
||||
}
|
||||
|
||||
override fun getName(): String {
|
||||
return "corda-blob"
|
||||
}
|
||||
}
|
||||
|
||||
override val databaseType: String = "PostgreSQL"
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
net.corda.nodeapi.internal.persistence.factory.H2SessionFactoryFactory
|
||||
net.corda.nodeapi.internal.persistence.factory.PostgresSessionFactoryFactory
|
@ -0,0 +1,26 @@
|
||||
package net.corda.nodeapi.internal.persistence
|
||||
|
||||
import com.nhaarman.mockito_kotlin.mock
|
||||
import net.corda.core.internal.NamedCacheFactory
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
|
||||
class HibernateConfigurationFactoryLoadingTest {
|
||||
@Test(timeout=300_000)
|
||||
fun checkErrorMessageForMissingFactory() {
|
||||
val jdbcUrl = "jdbc:madeUpNonense:foobar.com:1234"
|
||||
val presentFactories = listOf("H2", "PostgreSQL")
|
||||
try {
|
||||
val cacheFactory = mock<NamedCacheFactory>()
|
||||
HibernateConfiguration(
|
||||
emptySet(),
|
||||
DatabaseConfig(),
|
||||
emptyList(),
|
||||
jdbcUrl,
|
||||
cacheFactory)
|
||||
Assert.fail("Expected exception not thrown")
|
||||
} catch (e: HibernateConfigException) {
|
||||
Assert.assertEquals("Failed to find a SessionFactoryFactory to handle $jdbcUrl - factories present for ${presentFactories}", e.message)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user