This is an automated email from the ASF dual-hosted git repository.

jdaugherty pushed a commit to branch 15501-fix-datasource-properties
in repository https://gitbox.apache.org/repos/asf/grails-core.git

commit fe92876a9013449060316071979440c80ceefe77
Author: James Daugherty <[email protected]>
AuthorDate: Thu Mar 12 18:38:38 2026 -0400

    15501 - support binding to dataSourceProperties as well as dbProperties
---
 grails-datamapping-core/build.gradle               |   1 +
 .../datastore/gorm/jdbc/DataSourceBuilder.java     |   5 +-
 .../gorm/jdbc/DataSourceBuilderSpec.groovy         | 259 +++++++++++++++++++++
 3 files changed, 264 insertions(+), 1 deletion(-)

diff --git a/grails-datamapping-core/build.gradle 
b/grails-datamapping-core/build.gradle
index 5698511a81..d2fb1fb690 100644
--- a/grails-datamapping-core/build.gradle
+++ b/grails-datamapping-core/build.gradle
@@ -107,6 +107,7 @@ dependencies {
     testImplementation 'org.junit.jupiter:junit-jupiter-api'
     testImplementation 'org.spockframework:spock-core'
 
+    testImplementation 'com.zaxxer:HikariCP'
     testRuntimeOnly 'com.h2database:h2'
     testRuntimeOnly 'org.slf4j:slf4j-nop' // Get rid of warning about missing 
slf4j implementation during tests
 }
diff --git 
a/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/jdbc/DataSourceBuilder.java
 
b/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/jdbc/DataSourceBuilder.java
index e1c349a48a..c69af9576e 100644
--- 
a/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/jdbc/DataSourceBuilder.java
+++ 
b/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/jdbc/DataSourceBuilder.java
@@ -103,7 +103,10 @@ public class DataSourceBuilder {
         }
         MutablePropertyValues properties = new 
MutablePropertyValues(this.properties);
         new RelaxedDataBinder(result).withAlias("url", "jdbcUrl")
-                .withAlias("username", "user").bind(properties);
+                .withAlias("username", "user")
+                // The HikariConfig's property name is dataSourceProperties, 
not dbProperties so support both
+                .withAlias("dbProperties", "dataSourceProperties")
+                .bind(properties);
     }
 
     public DataSourceBuilder properties(Map<String, String> properties) {
diff --git 
a/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/jdbc/DataSourceBuilderSpec.groovy
 
b/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/jdbc/DataSourceBuilderSpec.groovy
new file mode 100644
index 0000000000..7ca50fb12b
--- /dev/null
+++ 
b/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/jdbc/DataSourceBuilderSpec.groovy
@@ -0,0 +1,259 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.grails.datastore.gorm.jdbc
+
+import com.zaxxer.hikari.HikariDataSource
+import org.springframework.jdbc.datasource.DriverManagerDataSource
+import spock.lang.Specification
+
+class DataSourceBuilderSpec extends Specification {
+
+    def "build creates a DataSource with basic properties"() {
+        when:
+        def ds = DataSourceBuilder.create()
+                .url("jdbc:h2:mem:testDb;DB_CLOSE_DELAY=-1")
+                .username("sa")
+                .password("")
+                .driverClassName("org.h2.Driver")
+                .build()
+
+        then:
+        ds != null
+    }
+
+    def "build with explicit type creates the correct DataSource type"() {
+        when:
+        def ds = DataSourceBuilder.create()
+                .type(DriverManagerDataSource)
+                .url("jdbc:h2:mem:typeTestDb;DB_CLOSE_DELAY=-1")
+                .username("sa")
+                .password("")
+                .driverClassName("org.h2.Driver")
+                .build()
+
+        then:
+        ds instanceof DriverManagerDataSource
+        ((DriverManagerDataSource) ds).url == 
"jdbc:h2:mem:typeTestDb;DB_CLOSE_DELAY=-1"
+        ((DriverManagerDataSource) ds).username == "sa"
+    }
+
+    def "build with non-pooled flag creates DriverManagerDataSource"() {
+        given:
+        def builder = DataSourceBuilder.create()
+        builder.setPooled(false)
+
+        when:
+        def ds = builder
+                .url("jdbc:h2:mem:nonPooledDb;DB_CLOSE_DELAY=-1")
+                .driverClassName("org.h2.Driver")
+                .build()
+
+        then:
+        ds instanceof DriverManagerDataSource
+    }
+
+    def "build with readOnly and non-pooled creates 
ReadOnlyDriverManagerDataSource"() {
+        given:
+        def builder = DataSourceBuilder.create()
+        builder.setPooled(false)
+        builder.setReadOnly(true)
+
+        when:
+        def type = builder.findType()
+
+        then:
+        type == DataSourceBuilder.ReadOnlyDriverManagerDataSource
+    }
+
+    def "dbProperties as Map is coerced to Properties and bound to 
HikariDataSource"() {
+        given:
+        def builder = DataSourceBuilder.create()
+                .type(HikariDataSource)
+                .url("jdbc:h2:mem:hikariDbPropsTest;DB_CLOSE_DELAY=-1")
+                .driverClassName("org.h2.Driver")
+                .username("sa")
+                .password("")
+
+        Map<String, String> props = [
+                'dbProperties': [cachePrepStmts: 'true', prepStmtCacheSize: 
'250']
+        ]
+        builder.properties(props)
+
+        when:
+        HikariDataSource ds = (HikariDataSource) builder.build()
+
+        then:
+        ds.dataSourceProperties != null
+        ds.dataSourceProperties.getProperty('cachePrepStmts') == 'true'
+        ds.dataSourceProperties.getProperty('prepStmtCacheSize') == '250'
+
+        cleanup:
+        ds?.close()
+    }
+
+    def "dbProperties with null values are excluded during coercion"() {
+        given:
+        def builder = DataSourceBuilder.create()
+                .type(HikariDataSource)
+                .url("jdbc:h2:mem:hikariNullPropsTest;DB_CLOSE_DELAY=-1")
+                .driverClassName("org.h2.Driver")
+                .username("sa")
+                .password("")
+
+        Map<String, String> props = [
+                'dbProperties': [validProp: 'value', nullProp: null]
+        ]
+        builder.properties(props)
+
+        when:
+        HikariDataSource ds = (HikariDataSource) builder.build()
+
+        then:
+        ds.dataSourceProperties != null
+        ds.dataSourceProperties.getProperty('validProp') == 'value'
+        !ds.dataSourceProperties.containsKey('nullProp')
+
+        cleanup:
+        ds?.close()
+    }
+
+    def "dbProperties alias maps to dataSourceProperties on 
HikariDataSource"() {
+        given:
+        def builder = DataSourceBuilder.create()
+                .type(HikariDataSource)
+                .url("jdbc:h2:mem:hikariAliasTest;DB_CLOSE_DELAY=-1")
+                .driverClassName("org.h2.Driver")
+                .username("sa")
+                .password("")
+
+        Properties dbProps = new Properties()
+        dbProps.setProperty('useSSL', 'false')
+        dbProps.setProperty('serverTimezone', 'UTC')
+
+        Map props = [dbProperties: dbProps]
+        builder.properties(props)
+
+        when:
+        HikariDataSource ds = (HikariDataSource) builder.build()
+
+        then:
+        ds.dataSourceProperties != null
+        ds.dataSourceProperties.getProperty('useSSL') == 'false'
+        ds.dataSourceProperties.getProperty('serverTimezone') == 'UTC'
+
+        cleanup:
+        ds?.close()
+    }
+
+    def "url alias maps to jdbcUrl on HikariDataSource"() {
+        given:
+        def builder = DataSourceBuilder.create()
+                .type(HikariDataSource)
+                .url("jdbc:h2:mem:hikariUrlAliasTest;DB_CLOSE_DELAY=-1")
+                .driverClassName("org.h2.Driver")
+                .username("sa")
+                .password("")
+
+        when:
+        HikariDataSource ds = (HikariDataSource) builder.build()
+
+        then:
+        ds.jdbcUrl == "jdbc:h2:mem:hikariUrlAliasTest;DB_CLOSE_DELAY=-1"
+
+        cleanup:
+        ds?.close()
+    }
+
+    def "username alias maps to user property"() {
+        when:
+        def ds = DataSourceBuilder.create()
+                .type(DriverManagerDataSource)
+                .url("jdbc:h2:mem:userAliasTest;DB_CLOSE_DELAY=-1")
+                .driverClassName("org.h2.Driver")
+                .username("testuser")
+                .password("testpass")
+                .build()
+
+        then:
+        ds instanceof DriverManagerDataSource
+        ((DriverManagerDataSource) ds).username == "testuser"
+        ((DriverManagerDataSource) ds).password == "testpass"
+    }
+
+    def "driver class name is auto-detected from url"() {
+        when:
+        def ds = DataSourceBuilder.create()
+                .type(DriverManagerDataSource)
+                .url("jdbc:h2:mem:autoDriverTest;DB_CLOSE_DELAY=-1")
+                .username("sa")
+                .password("")
+                .build()
+
+        then:
+        ds instanceof DriverManagerDataSource
+    }
+
+    def "properties method merges additional properties"() {
+        given:
+        def builder = DataSourceBuilder.create()
+                .type(DriverManagerDataSource)
+
+        when:
+        builder.properties([url: "jdbc:h2:mem:mergeTest;DB_CLOSE_DELAY=-1", 
driverClassName: "org.h2.Driver"])
+        builder.properties([username: "sa", password: ""])
+        def ds = builder.build()
+
+        then:
+        ds instanceof DriverManagerDataSource
+        ((DriverManagerDataSource) ds).url == 
"jdbc:h2:mem:mergeTest;DB_CLOSE_DELAY=-1"
+        ((DriverManagerDataSource) ds).username == "sa"
+    }
+
+    def "HikariDataSource is built correctly with dbProperties from typical 
config map"() {
+        given: "a config map resembling what Grails would pass from 
application.yml"
+        def builder = DataSourceBuilder.create()
+                .type(HikariDataSource)
+
+        Map config = [
+                url            : 
"jdbc:h2:mem:fullConfigTest;DB_CLOSE_DELAY=-1",
+                driverClassName: "org.h2.Driver",
+                username       : "sa",
+                password       : "",
+                dbProperties   : [
+                        cachePrepStmts       : 'true',
+                        prepStmtCacheSize    : '250',
+                        prepStmtCacheSqlLimit: '2048'
+                ]
+        ]
+        builder.properties(config)
+
+        when:
+        HikariDataSource ds = (HikariDataSource) builder.build()
+
+        then:
+        ds.jdbcUrl == "jdbc:h2:mem:fullConfigTest;DB_CLOSE_DELAY=-1"
+        ds.username == "sa"
+        ds.dataSourceProperties.getProperty('cachePrepStmts') == 'true'
+        ds.dataSourceProperties.getProperty('prepStmtCacheSize') == '250'
+        ds.dataSourceProperties.getProperty('prepStmtCacheSqlLimit') == '2048'
+
+        cleanup:
+        ds?.close()
+    }
+}

Reply via email to