From 1bd550227b4060efa78f92466535aa62cd18ba17 Mon Sep 17 00:00:00 2001 From: Roman Makeev Date: Fri, 1 Nov 2024 02:58:13 -0700 Subject: [PATCH 1/2] fix class loader --- .../topics/Getting-Started-with-Exposed.topic | 4 ++ .../topics/Working-with-Database.md | 3 ++ exposed-core/api/exposed-core.api | 16 ++++---- .../org/jetbrains/exposed/sql/Database.kt | 39 ++++++++++++------- gradle.properties | 2 +- 5 files changed, 42 insertions(+), 22 deletions(-) diff --git a/documentation-website/Writerside/topics/Getting-Started-with-Exposed.topic b/documentation-website/Writerside/topics/Getting-Started-with-Exposed.topic index 914118a1e9..cbddc93fe7 100644 --- a/documentation-website/Writerside/topics/Getting-Started-with-Exposed.topic +++ b/documentation-website/Writerside/topics/Getting-Started-with-Exposed.topic @@ -194,6 +194,10 @@ Every database access using Exposed is started by obtaining a connection and creating a transaction. To configure the database connection, use the Database.connect() function.

+

+ By default, Exposed using `ServiceLoader` to get `DatabaseConnectionAutoRegistration`. + It can be modified when calling `Database.connect` method by providing `connectionAutoRegistration` in parameter list. +

diff --git a/documentation-website/Writerside/topics/Working-with-Database.md b/documentation-website/Writerside/topics/Working-with-Database.md index 445505534c..ade063f5a8 100644 --- a/documentation-website/Writerside/topics/Working-with-Database.md +++ b/documentation-website/Writerside/topics/Working-with-Database.md @@ -12,6 +12,9 @@ Every database access using Exposed is started by obtaining a connection and cre First of all, you have to tell Exposed how to connect to a database by using the `Database.connect` function. It won't create a real database connection but will only provide a descriptor for future usage. +By default, Exposed using `ServiceLoader` to get `DatabaseConnectionAutoRegistration`. +It can be modified when calling `Database.connect` method by providing `connectionAutoRegistration` in parameter list. + A real connection will be instantiated later by calling the `transaction` lambda (see [Transactions](Transactions.md) for more details). diff --git a/exposed-core/api/exposed-core.api b/exposed-core/api/exposed-core.api index 4b06fe891f..ad453dce25 100644 --- a/exposed-core/api/exposed-core.api +++ b/exposed-core/api/exposed-core.api @@ -674,14 +674,14 @@ public final class org/jetbrains/exposed/sql/Database { } public final class org/jetbrains/exposed/sql/Database$Companion { - public final fun connect (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lorg/jetbrains/exposed/sql/DatabaseConfig;Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/exposed/sql/Database; - public final fun connect (Ljavax/sql/DataSource;Lkotlin/jvm/functions/Function1;Lorg/jetbrains/exposed/sql/DatabaseConfig;Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/exposed/sql/Database; - public final fun connect (Lkotlin/jvm/functions/Function0;Lorg/jetbrains/exposed/sql/DatabaseConfig;Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/exposed/sql/Database; - public static synthetic fun connect$default (Lorg/jetbrains/exposed/sql/Database$Companion;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lorg/jetbrains/exposed/sql/DatabaseConfig;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/jetbrains/exposed/sql/Database; - public static synthetic fun connect$default (Lorg/jetbrains/exposed/sql/Database$Companion;Ljavax/sql/DataSource;Lkotlin/jvm/functions/Function1;Lorg/jetbrains/exposed/sql/DatabaseConfig;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/jetbrains/exposed/sql/Database; - public static synthetic fun connect$default (Lorg/jetbrains/exposed/sql/Database$Companion;Lkotlin/jvm/functions/Function0;Lorg/jetbrains/exposed/sql/DatabaseConfig;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/jetbrains/exposed/sql/Database; - public final fun connectPool (Ljavax/sql/ConnectionPoolDataSource;Lkotlin/jvm/functions/Function1;Lorg/jetbrains/exposed/sql/DatabaseConfig;Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/exposed/sql/Database; - public static synthetic fun connectPool$default (Lorg/jetbrains/exposed/sql/Database$Companion;Ljavax/sql/ConnectionPoolDataSource;Lkotlin/jvm/functions/Function1;Lorg/jetbrains/exposed/sql/DatabaseConfig;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/jetbrains/exposed/sql/Database; + public final fun connect (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lorg/jetbrains/exposed/sql/DatabaseConfig;Lorg/jetbrains/exposed/sql/DatabaseConnectionAutoRegistration;Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/exposed/sql/Database; + public final fun connect (Ljavax/sql/DataSource;Lkotlin/jvm/functions/Function1;Lorg/jetbrains/exposed/sql/DatabaseConfig;Lorg/jetbrains/exposed/sql/DatabaseConnectionAutoRegistration;Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/exposed/sql/Database; + public final fun connect (Lkotlin/jvm/functions/Function0;Lorg/jetbrains/exposed/sql/DatabaseConfig;Lorg/jetbrains/exposed/sql/DatabaseConnectionAutoRegistration;Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/exposed/sql/Database; + public static synthetic fun connect$default (Lorg/jetbrains/exposed/sql/Database$Companion;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lorg/jetbrains/exposed/sql/DatabaseConfig;Lorg/jetbrains/exposed/sql/DatabaseConnectionAutoRegistration;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/jetbrains/exposed/sql/Database; + public static synthetic fun connect$default (Lorg/jetbrains/exposed/sql/Database$Companion;Ljavax/sql/DataSource;Lkotlin/jvm/functions/Function1;Lorg/jetbrains/exposed/sql/DatabaseConfig;Lorg/jetbrains/exposed/sql/DatabaseConnectionAutoRegistration;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/jetbrains/exposed/sql/Database; + public static synthetic fun connect$default (Lorg/jetbrains/exposed/sql/Database$Companion;Lkotlin/jvm/functions/Function0;Lorg/jetbrains/exposed/sql/DatabaseConfig;Lorg/jetbrains/exposed/sql/DatabaseConnectionAutoRegistration;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/jetbrains/exposed/sql/Database; + public final fun connectPool (Ljavax/sql/ConnectionPoolDataSource;Lkotlin/jvm/functions/Function1;Lorg/jetbrains/exposed/sql/DatabaseConfig;Lorg/jetbrains/exposed/sql/DatabaseConnectionAutoRegistration;Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/exposed/sql/Database; + public static synthetic fun connectPool$default (Lorg/jetbrains/exposed/sql/Database$Companion;Ljavax/sql/ConnectionPoolDataSource;Lkotlin/jvm/functions/Function1;Lorg/jetbrains/exposed/sql/DatabaseConfig;Lorg/jetbrains/exposed/sql/DatabaseConnectionAutoRegistration;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/jetbrains/exposed/sql/Database; public final fun getDefaultIsolationLevel (Lorg/jetbrains/exposed/sql/Database;)I public final fun getDialectName (Ljava/lang/String;)Ljava/lang/String; public final fun registerDialect (Ljava/lang/String;Lkotlin/jvm/functions/Function0;)V diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Database.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Database.kt index d55c7dae28..6c91d687ee 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Database.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Database.kt @@ -112,9 +112,11 @@ class Database private constructor( companion object { internal val dialects = ConcurrentHashMap DatabaseDialect>() - private val connectionInstanceImpl: DatabaseConnectionAutoRegistration = - ServiceLoader.load(DatabaseConnectionAutoRegistration::class.java, Database::class.java.classLoader).firstOrNull() + private val connectionInstanceImpl: DatabaseConnectionAutoRegistration by lazy { + ServiceLoader.load(DatabaseConnectionAutoRegistration::class.java, Database::class.java.classLoader) + .firstOrNull() ?: error("Can't load implementation for ${DatabaseConnectionAutoRegistration::class.simpleName}") + } private val driverMapping = mutableMapOf( "jdbc:h2" to "org.h2.Driver", @@ -162,12 +164,13 @@ class Database private constructor( private fun doConnect( explicitVendor: String?, config: DatabaseConfig?, + connectionAutoRegistration: DatabaseConnectionAutoRegistration, getNewConnection: () -> Connection, setupConnection: (Connection) -> Unit = {}, manager: (Database) -> TransactionManager = { ThreadLocalTransactionManager(it) } ): Database { return Database(explicitVendor, config ?: DatabaseConfig.invoke()) { - connectionInstanceImpl(getNewConnection().apply { setupConnection(this) }) + connectionAutoRegistration(getNewConnection().apply { setupConnection(this) }) }.apply { TransactionManager.registerManager(this, manager(this)) } @@ -180,6 +183,7 @@ class Database private constructor( * but instead provides the details necessary to do so whenever a connection is required by a transaction. * * @param datasource The [DataSource] object to be used as a means of getting a connection. + * @param connectionAutoRegistration The connection provider for database. If not provided, will use ServiceLoaded [connectionInstanceImpl] * @param setupConnection Any setup that should be applied to each new connection. * @param databaseConfig Configuration parameters for this [Database] instance. * @param manager The [TransactionManager] responsible for new transactions that use this [Database] instance. @@ -188,6 +192,7 @@ class Database private constructor( datasource: DataSource, setupConnection: (Connection) -> Unit = {}, databaseConfig: DatabaseConfig? = null, + connectionAutoRegistration: DatabaseConnectionAutoRegistration = connectionInstanceImpl, manager: (Database) -> TransactionManager = { ThreadLocalTransactionManager(it) } ): Database { return doConnect( @@ -195,7 +200,8 @@ class Database private constructor( config = databaseConfig, getNewConnection = { datasource.connection!! }, setupConnection = setupConnection, - manager = manager + manager = manager, + connectionAutoRegistration = connectionAutoRegistration ).apply { connectsViaDataSource = true } @@ -225,6 +231,7 @@ class Database private constructor( datasource: ConnectionPoolDataSource, setupConnection: (Connection) -> Unit = {}, databaseConfig: DatabaseConfig? = null, + connectionAutoRegistration: DatabaseConnectionAutoRegistration = connectionInstanceImpl, manager: (Database) -> TransactionManager = { ThreadLocalTransactionManager(it) } ): Database { return doConnect( @@ -232,7 +239,8 @@ class Database private constructor( config = databaseConfig, getNewConnection = { datasource.pooledConnection.connection!! }, setupConnection = setupConnection, - manager = manager + manager = manager, + connectionAutoRegistration = connectionAutoRegistration ) } @@ -243,19 +251,22 @@ class Database private constructor( * but instead provides the details necessary to do so whenever a connection is required by a transaction. * * @param getNewConnection A function that returns a new connection. + * @param connectionAutoRegistration The connection provider for database. If not provided, will use ServiceLoaded [connectionInstanceImpl] * @param databaseConfig Configuration parameters for this [Database] instance. * @param manager The [TransactionManager] responsible for new transactions that use this [Database] instance. */ fun connect( getNewConnection: () -> Connection, databaseConfig: DatabaseConfig? = null, + connectionAutoRegistration: DatabaseConnectionAutoRegistration = connectionInstanceImpl, manager: (Database) -> TransactionManager = { ThreadLocalTransactionManager(it) } ): Database { return doConnect( explicitVendor = null, config = databaseConfig, getNewConnection = getNewConnection, - manager = manager + manager = manager, + connectionAutoRegistration = connectionAutoRegistration ) } @@ -266,6 +277,7 @@ class Database private constructor( * but instead provides the details necessary to do so whenever a connection is required by a transaction. * * @param url The URL that represents the database when getting a connection. + * @param connectionAutoRegistration The connection provider for database. If not provided, will use ServiceLoaded [connectionInstanceImpl] * @param driver The JDBC driver class. If not provided, the specified [url] will be used to find * a match from the existing driver mappings. * @param user The database user that owns the new connections. @@ -274,6 +286,7 @@ class Database private constructor( * @param databaseConfig Configuration parameters for this [Database] instance. * @param manager The [TransactionManager] responsible for new transactions that use this [Database] instance. */ + @Suppress("UnusedParameter", "LongParameterList") fun connect( url: String, driver: String = getDriver(url), @@ -281,18 +294,18 @@ class Database private constructor( password: String = "", setupConnection: (Connection) -> Unit = {}, databaseConfig: DatabaseConfig? = null, + connectionAutoRegistration: DatabaseConnectionAutoRegistration = connectionInstanceImpl, manager: (Database) -> TransactionManager = { ThreadLocalTransactionManager(it) } ): Database { Class.forName(driver).getDeclaredConstructor().newInstance() val dialectName = getDialectName(url) ?: error("Can't resolve dialect for connection: $url") return doConnect( - dialectName, - databaseConfig, - { - DriverManager.getConnection(url, user, password) - }, - setupConnection, - manager + explicitVendor = dialectName, + config = databaseConfig, + getNewConnection = { DriverManager.getConnection(url, user, password) }, + setupConnection = setupConnection, + manager = manager, + connectionAutoRegistration = connectionAutoRegistration, ) } diff --git a/gradle.properties b/gradle.properties index b0bfb74d7c..2d8d635a81 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,4 +4,4 @@ org.gradle.configuration.cache=true org.gradle.caching=true group=org.jetbrains.exposed -version=0.56.0 +version=0.57.0 From 485da607f077c37cc4beb441463b30710389c693 Mon Sep 17 00:00:00 2001 From: Roman Makeev Date: Tue, 26 Nov 2024 11:18:42 +0300 Subject: [PATCH 2/2] fix review issues --- .../topics/Getting-Started-with-Exposed.topic | 10 ++++++---- .../Writerside/topics/Working-with-Database.md | 16 ++++++++++------ .../kotlin/org/jetbrains/exposed/sql/Database.kt | 9 ++++++--- gradle.properties | 2 +- 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/documentation-website/Writerside/topics/Getting-Started-with-Exposed.topic b/documentation-website/Writerside/topics/Getting-Started-with-Exposed.topic index cbddc93fe7..1f7904428b 100644 --- a/documentation-website/Writerside/topics/Getting-Started-with-Exposed.topic +++ b/documentation-website/Writerside/topics/Getting-Started-with-Exposed.topic @@ -194,10 +194,6 @@ Every database access using Exposed is started by obtaining a connection and creating a transaction. To configure the database connection, use the Database.connect() function.

-

- By default, Exposed using `ServiceLoader` to get `DatabaseConnectionAutoRegistration`. - It can be modified when calling `Database.connect` method by providing `connectionAutoRegistration` in parameter list. -

@@ -254,6 +250,12 @@ but it does not immediately establish a connection with the database. The actual connection to the database will be established later when a database operation is performed. + + By default, Exposed uses a ServiceLoader to get an implementation of the DatabaseConnectionAutoRegistration + interface that represents a connection accessed by the Database instance. + This can be modified when calling the Database.connect method by providing an argument to connectionAutoRegistration + in the parameter list. +

With this, you've added Exposed to your Kotlin project and configured a database connection. You're now ready to define your data model and engage with the database using Exposed's DSL API. diff --git a/documentation-website/Writerside/topics/Working-with-Database.md b/documentation-website/Writerside/topics/Working-with-Database.md index ade063f5a8..4b8231055e 100644 --- a/documentation-website/Writerside/topics/Working-with-Database.md +++ b/documentation-website/Writerside/topics/Working-with-Database.md @@ -12,9 +12,6 @@ Every database access using Exposed is started by obtaining a connection and cre First of all, you have to tell Exposed how to connect to a database by using the `Database.connect` function. It won't create a real database connection but will only provide a descriptor for future usage. -By default, Exposed using `ServiceLoader` to get `DatabaseConnectionAutoRegistration`. -It can be modified when calling `Database.connect` method by providing `connectionAutoRegistration` in parameter list. - A real connection will be instantiated later by calling the `transaction` lambda (see [Transactions](Transactions.md) for more details). @@ -27,13 +24,20 @@ val db = Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver") Executing this code more than once per database will create leaks in your application, hence it is recommended to store it for later use: object DbSettings { - val db by lazy { - Database.connect(/* setup connection */) - } + val db by lazy { + Database.connect(/* setup connection */) + } } + + By default, Exposed uses a ServiceLoader to get an implementation of the DatabaseConnectionAutoRegistration + interface that represents a connection accessed by the Database instance. + This can be modified when calling the Database.connect method by providing an argument to connectionAutoRegistration + in the parameter list. + + ### H2 In order to use H2, you need to add the H2 driver dependency: diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Database.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Database.kt index 6c91d687ee..20e3400f03 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Database.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Database.kt @@ -183,7 +183,8 @@ class Database private constructor( * but instead provides the details necessary to do so whenever a connection is required by a transaction. * * @param datasource The [DataSource] object to be used as a means of getting a connection. - * @param connectionAutoRegistration The connection provider for database. If not provided, will use ServiceLoaded [connectionInstanceImpl] + * @param connectionAutoRegistration The connection provider for database. If not provided, + * a service loader will be used to locate and load a provider for [DatabaseConnectionAutoRegistration]. * @param setupConnection Any setup that should be applied to each new connection. * @param databaseConfig Configuration parameters for this [Database] instance. * @param manager The [TransactionManager] responsible for new transactions that use this [Database] instance. @@ -251,7 +252,8 @@ class Database private constructor( * but instead provides the details necessary to do so whenever a connection is required by a transaction. * * @param getNewConnection A function that returns a new connection. - * @param connectionAutoRegistration The connection provider for database. If not provided, will use ServiceLoaded [connectionInstanceImpl] + * @param connectionAutoRegistration The connection provider for database. If not provided, + * a service loader will be used to locate and load a provider for [DatabaseConnectionAutoRegistration]. * @param databaseConfig Configuration parameters for this [Database] instance. * @param manager The [TransactionManager] responsible for new transactions that use this [Database] instance. */ @@ -277,7 +279,8 @@ class Database private constructor( * but instead provides the details necessary to do so whenever a connection is required by a transaction. * * @param url The URL that represents the database when getting a connection. - * @param connectionAutoRegistration The connection provider for database. If not provided, will use ServiceLoaded [connectionInstanceImpl] + * @param connectionAutoRegistration The connection provider for database. If not provided, + * a service loader will be used to locate and load a provider for [DatabaseConnectionAutoRegistration]. * @param driver The JDBC driver class. If not provided, the specified [url] will be used to find * a match from the existing driver mappings. * @param user The database user that owns the new connections. diff --git a/gradle.properties b/gradle.properties index 2d8d635a81..b0bfb74d7c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,4 +4,4 @@ org.gradle.configuration.cache=true org.gradle.caching=true group=org.jetbrains.exposed -version=0.57.0 +version=0.56.0