Skip to main content

Dynamic Datasource Configuration

This page covers every configuration entry of the dynamic datasource: where connection definitions live, what YAML can tune, how the default datasource is determined, and how to refresh after a connection change.

Configuration spans three layers:

  • Connection definitions: the information of each physical database connection, stored in the platform table platform_dev_db_connect
  • Module properties: pool sizing, lazy loading, catalog mapping, etc., under the prefix geelato.datasource.dynamic.*
  • ORM properties: default datasource and Dao binding, under the prefix geelato.orm.*

Connection Definitions: the platform_dev_db_connect Tableโ€‹

The default source of truth for dynamic datasource definitions is the connection table in the platform primary database, mapped by the entity ConnectMeta:

FieldColumnDescription
ididprimary key, i.e. the routing key โ€” the connectId used by entity binding and switching APIs is this id
appIdapp_idowning application
dbConnectNamedb_connect_nameconnection name (required)
dbNamedb_namedatabase name (required)
dbSchemadb_schemadatabase schema (required)
dbTypedb_typedatabase type (required), currently mysql / postgresql
dbUserNamedb_user_nameusername (required)
dbPassworddb_passwordpassword (required, stored encrypted, decrypted automatically when the pool is built)
dbPortdb_portconnection port
dbHostnameIpdb_hostname_iphostname or IP (required)
enableStatusenable_statusenable status, 1 enabled / 0 disabled

A few key points:

  • routing key aligns with the primary key. The xxx in @Entity(connectId = "xxx"), useDataSource("xxx") and switchDbByConnectId("xxx") is always the id of this table
  • pool construction is centralized in DataSourceFactory: it picks the dialect by dbType (MySQL / PostgreSQL only), uses HikariCP, decrypts the password and applies unified pool properties
  • for mapping intranet host addresses to local ones in development, see Host Mapping
  • to load definitions from a config center, YAML or an external service instead, see ORM / Datasource Extension

Module Properties: geelato.datasource.dynamic.*โ€‹

Catalog Mappingโ€‹

The mapping from catalog (logical database group) to datasource key, used together with @Entity(catalog):

geelato:
datasource:
dynamic:
catalog-mapping:
platform: primary
business: biz_db

Entities declaring @Entity(catalog = "business") route to biz_db. The mapping is resolved at query time, so adding it after startup works too. Full priority rules in Entity Binding.

Pool and Timeoutsโ€‹

These properties apply to every dynamically built datasource (HikariCP):

PropertyDefaultDescription
minimum-idle5minimum idle connections
maximum-pool-size20maximum connections
idle-timeout-ms600000idle timeout (ms)
max-lifetime-ms1800000max connection lifetime
connection-timeout-ms5000timeout for borrowing from the pool
validation-timeout-ms3000connection validation timeout
keepalive-time-ms300000keepalive probe interval
initialization-fail-timeout-ms0init fail timeout (0 does not block startup)
connection-test-querySELECT 1validity test statement
connect-timeout-ms5000TCP connect timeout, appended to the JDBC URL
socket-timeout-ms300000socket read timeout, appended to the JDBC URL; longer queries are cut off by the driver

Lazy Loading and Transaction Enhancementsโ€‹

PropertyDefaultDescription
delay-load-data-sourcetruelazy loading: cache definitions only at startup, build the pool when a key is first routed to. Friendly when many datasources are configured but few are active
enable-jta-transactionfalsewires the Atomikos JTA distributed-transaction extension point (optional; default stays lightweight local transactions)
enable-seata-proxyfalsewraps dynamic datasources with a Seata DataSourceProxy (requires your own Seata environment)

JTA / Seata are reserved capabilities; ordinary business applications do not need them. See the overview for the current JTA / Seata status.

ORM Properties: geelato.orm.*โ€‹

PropertyDefaultDescription
default-data-source-keyauto-inferredthe platform default datasource key (level 4 of the priority chain). When unset: primary if any of primaryDataSource / primaryJdbcTemplate / primaryDao beans exists
dao-bean-nameauto-resolvedthe Dao bean bound to the ORM. Resolution order: dynamicDao > primaryDao > the single Dao bean; usually no configuration needed

default-data-source-key is resolved in the late startup phase (after all singleton beans are ready) by OrmAutoConfiguration and written into DataSourceManager. How the runtime priority chain consumes it is covered in Entity Binding.

Bean Wiring Conditionsโ€‹

Knowing which beans appear under which conditions helps diagnose "why didn't it switch":

  • Starter side (when spring.datasource.primary.jdbc-url is present): primaryDataSource โ†’ primaryJdbcTemplate โ†’ primaryDao; with spring.datasource.secondary.* configured, secondaryDataSource and friends appear too. primaryDao connects straight to the physical primary and bypasses routing
  • Dynamic datasource stack (auto-configured when primaryJdbcTemplate exists in the container): dynamicDataSource (routing) โ†’ dynamicJdbcTemplate โ†’ dynamicDao, plus the definition loader, registry and aspects
  • applications not using the Starter can trigger the dynamic datasource stack by providing a bean named primaryJdbcTemplate themselves

The bean topology and "only dynamicDao can switch" are covered fully in Switching.

Refreshing After a Connection Changeโ€‹

Connection definitions live in the database; after changing them there is no restart needed โ€” refresh through the platform endpoints:

  • POST /model/connect/refresh/{id} โ€” refresh a single connection (reload the definition, rebuild that key's datasource)
  • POST /model/connect/refreshAll โ€” refresh everything

Refreshing is done by DevDbConnectService: definitions and instances are updated in the registry first, then the routing map of the routing datasource is refreshed; deleted or disabled connections are removed from routing. Under lazy loading, connections that never built a pool only get their definition updated and still build lazily on first route.

:::note Entity mapping cache

EntityDataSourceResolver keeps an in-memory cache of "entity โ†’ datasource" resolutions, but every hit re-checks that the target datasource is still registered; the cache invalidates itself when a datasource is removed. No extra cleanup of entity mappings is needed after refreshing connection definitions.

:::

Suggested Readingโ€‹

  • Binding & Priority โ€” the four binding styles and the full priority chain
  • Switching โ€” automatic switching, manual APIs and transaction limits
  • Host Mapping โ€” mapping intranet addresses locally for development