diff --git a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/main/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositoryAutoConfiguration.java b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/main/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositoryAutoConfiguration.java index 46cd9077edc..0d2ae5c7dd0 100644 --- a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/main/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositoryAutoConfiguration.java +++ b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/main/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositoryAutoConfiguration.java @@ -18,9 +18,6 @@ import javax.sql.DataSource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import org.springframework.ai.chat.memory.jdbc.JdbcChatMemoryDialect; import org.springframework.ai.chat.memory.jdbc.JdbcChatMemoryRepository; import org.springframework.ai.model.chat.memory.autoconfigure.ChatMemoryAutoConfiguration; @@ -28,13 +25,16 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration; +import org.springframework.boot.autoconfigure.sql.init.OnDatabaseInitializationCondition; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; import org.springframework.jdbc.core.JdbcTemplate; /** * @author Jonathan Leijendekker * @author Thomas Vitale + * @author Yanming Zhou * @since 1.0.0 */ @AutoConfiguration(after = JdbcTemplateAutoConfiguration.class, before = ChatMemoryAutoConfiguration.class) @@ -51,9 +51,19 @@ JdbcChatMemoryRepository jdbcChatMemoryRepository(JdbcTemplate jdbcTemplate, Dat @Bean @ConditionalOnMissingBean + @Conditional(OnJdbcChatMemoryRepositoryDatasourceInitializationCondition.class) JdbcChatMemoryRepositorySchemaInitializer jdbcChatMemoryScriptDatabaseInitializer(DataSource dataSource, JdbcChatMemoryRepositoryProperties properties) { return new JdbcChatMemoryRepositorySchemaInitializer(dataSource, properties); } + static class OnJdbcChatMemoryRepositoryDatasourceInitializationCondition extends OnDatabaseInitializationCondition { + + OnJdbcChatMemoryRepositoryDatasourceInitializationCondition() { + super("Jdbc Chat Memory Repository", + JdbcChatMemoryRepositoryProperties.CONFIG_PREFIX + ".initialize-schema"); + } + + } + } diff --git a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/main/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositoryProperties.java b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/main/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositoryProperties.java index 47e64e5a9c5..d7e9ddb7b64 100644 --- a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/main/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositoryProperties.java +++ b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/main/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositoryProperties.java @@ -17,10 +17,12 @@ package org.springframework.ai.model.chat.memory.repository.jdbc.autoconfigure; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.sql.init.DatabaseInitializationMode; /** * @author Jonathan Leijendekker * @author Thomas Vitale + * @author Yanming Zhou * @since 1.0.0 */ @ConfigurationProperties(JdbcChatMemoryRepositoryProperties.CONFIG_PREFIX) @@ -28,6 +30,8 @@ public class JdbcChatMemoryRepositoryProperties { public static final String CONFIG_PREFIX = "spring.ai.chat.memory.repository.jdbc"; + private static final String DEFAULT_SCHEMA_LOCATION = "classpath:org/springframework/ai/chat/memory/jdbc/schema-@@platform@@.sql"; + /** * Whether to initialize the schema on startup. Values: embedded, always, never. * Default is embedded. @@ -38,7 +42,13 @@ public class JdbcChatMemoryRepositoryProperties { * Locations of schema (DDL) scripts. Supports comma-separated list. Default is * classpath:org/springframework/ai/chat/memory/jdbc/schema-@@platform@@.sql */ - private String schema = "classpath:org/springframework/ai/chat/memory/jdbc/schema-@@platform@@.sql"; + private String schema = DEFAULT_SCHEMA_LOCATION; + + /** + * Platform to use in initialization scripts if the @@platform@@ placeholder is used. + * Auto-detected by default. + */ + private String platform; public DatabaseInitializationMode getInitializeSchema() { return this.initializeSchema; @@ -48,6 +58,14 @@ public void setInitializeSchema(DatabaseInitializationMode initializeSchema) { this.initializeSchema = initializeSchema; } + public String getPlatform() { + return platform; + } + + public void setPlatform(String platform) { + this.platform = platform; + } + public String getSchema() { return this.schema; } @@ -56,23 +74,4 @@ public void setSchema(String schema) { this.schema = schema; } - public enum DatabaseInitializationMode { - - /** - * Always initialize the database. - */ - ALWAYS, - - /** - * Only initialize an embedded database. - */ - EMBEDDED, - - /** - * Never initialize the database. - */ - NEVER - - } - } diff --git a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/main/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositorySchemaInitializer.java b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/main/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositorySchemaInitializer.java index e202e47946f..bdd3f599f85 100644 --- a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/main/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositorySchemaInitializer.java +++ b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/main/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositorySchemaInitializer.java @@ -22,7 +22,6 @@ import org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer; import org.springframework.boot.jdbc.init.PlatformPlaceholderDatabaseDriverResolver; -import org.springframework.boot.sql.init.DatabaseInitializationMode; import org.springframework.boot.sql.init.DatabaseInitializationSettings; import org.springframework.util.StringUtils; @@ -30,12 +29,11 @@ * Performs database initialization for the JDBC Chat Memory Repository. * * @author Mark Pollack + * @author Yanming Zhou * @since 1.0.0 */ class JdbcChatMemoryRepositorySchemaInitializer extends DataSourceScriptDatabaseInitializer { - private static final String DEFAULT_SCHEMA_LOCATION = "classpath:org/springframework/ai/chat/memory/jdbc/schema-@@platform@@.sql"; - JdbcChatMemoryRepositorySchemaInitializer(DataSource dataSource, JdbcChatMemoryRepositoryProperties properties) { super(dataSource, getSettings(dataSource, properties)); } @@ -43,50 +41,19 @@ class JdbcChatMemoryRepositorySchemaInitializer extends DataSourceScriptDatabase static DatabaseInitializationSettings getSettings(DataSource dataSource, JdbcChatMemoryRepositoryProperties properties) { var settings = new DatabaseInitializationSettings(); - - // Determine schema locations - String schemaProp = properties.getSchema(); - List schemaLocations; - PlatformPlaceholderDatabaseDriverResolver resolver = new PlatformPlaceholderDatabaseDriverResolver(); - try { - String url = dataSource.getConnection().getMetaData().getURL().toLowerCase(); - if (url.contains("hsqldb")) { - schemaLocations = List.of("classpath:org/springframework/ai/chat/memory/jdbc/schema-hsqldb.sql"); - } - else if (StringUtils.hasText(schemaProp)) { - schemaLocations = resolver.resolveAll(dataSource, schemaProp); - } - else { - schemaLocations = resolver.resolveAll(dataSource, DEFAULT_SCHEMA_LOCATION); - } - } - catch (Exception e) { - // fallback to default - if (StringUtils.hasText(schemaProp)) { - schemaLocations = resolver.resolveAll(dataSource, schemaProp); - } - else { - schemaLocations = resolver.resolveAll(dataSource, DEFAULT_SCHEMA_LOCATION); - } - } - settings.setSchemaLocations(schemaLocations); - - // Determine initialization mode - JdbcChatMemoryRepositoryProperties.DatabaseInitializationMode init = properties.getInitializeSchema(); - DatabaseInitializationMode mode; - if (JdbcChatMemoryRepositoryProperties.DatabaseInitializationMode.ALWAYS.equals(init)) { - mode = DatabaseInitializationMode.ALWAYS; - } - else if (JdbcChatMemoryRepositoryProperties.DatabaseInitializationMode.NEVER.equals(init)) { - mode = DatabaseInitializationMode.NEVER; - } - else { - // embedded or default - mode = DatabaseInitializationMode.EMBEDDED; - } - settings.setMode(mode); + settings.setSchemaLocations(resolveSchemaLocations(dataSource, properties)); + settings.setMode(properties.getInitializeSchema()); settings.setContinueOnError(true); return settings; } + private static List resolveSchemaLocations(DataSource dataSource, + JdbcChatMemoryRepositoryProperties properties) { + PlatformPlaceholderDatabaseDriverResolver platformResolver = new PlatformPlaceholderDatabaseDriverResolver(); + if (StringUtils.hasText(properties.getPlatform())) { + return platformResolver.resolveAll(properties.getPlatform(), properties.getSchema()); + } + return platformResolver.resolveAll(dataSource, properties.getSchema()); + } + } diff --git a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/test/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryPostgresqlAutoConfigurationIT.java b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/test/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryPostgresqlAutoConfigurationIT.java index 1631eb70726..2b97bbce75b 100644 --- a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/test/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryPostgresqlAutoConfigurationIT.java +++ b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/test/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryPostgresqlAutoConfigurationIT.java @@ -38,6 +38,7 @@ * @author Jonathan Leijendekker * @author Thomas Vitale * @author Linar Abzaltdinov + * @author Yanming Zhou */ class JdbcChatMemoryPostgresqlAutoConfigurationIT { @@ -49,14 +50,14 @@ class JdbcChatMemoryPostgresqlAutoConfigurationIT { @Test void jdbcChatMemoryScriptDatabaseInitializer_shouldBeLoaded() { this.contextRunner.withPropertyValues("spring.ai.chat.memory.repository.jdbc.initialize-schema=always") - .run(context -> assertThat(context.containsBean("jdbcChatMemoryScriptDatabaseInitializer")).isTrue()); + .run(context -> assertThat(context).hasBean("jdbcChatMemoryScriptDatabaseInitializer")); } @Test void jdbcChatMemoryScriptDatabaseInitializer_shouldNotRunSchemaInit() { this.contextRunner.withPropertyValues("spring.ai.chat.memory.repository.jdbc.initialize-schema=never") .run(context -> { - assertThat(context.containsBean("jdbcChatMemoryScriptDatabaseInitializer")).isTrue(); + assertThat(context).doesNotHaveBean("jdbcChatMemoryScriptDatabaseInitializer"); // Optionally, check that the schema is not initialized (could check table // absence if needed) }); @@ -65,7 +66,7 @@ void jdbcChatMemoryScriptDatabaseInitializer_shouldNotRunSchemaInit() { @Test void initializeSchemaEmbeddedDefault() { this.contextRunner.withPropertyValues("spring.ai.chat.memory.repository.jdbc.initialize-schema=embedded") - .run(context -> assertThat(context.containsBean("jdbcChatMemoryScriptDatabaseInitializer")).isTrue()); + .run(context -> assertThat(context).hasBean("jdbcChatMemoryScriptDatabaseInitializer")); } @Test diff --git a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/test/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositoryPropertiesTests.java b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/test/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositoryPropertiesTests.java index 50f851ef675..ca930febabd 100644 --- a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/test/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositoryPropertiesTests.java +++ b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/test/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemoryRepositoryPropertiesTests.java @@ -18,6 +18,8 @@ import org.junit.jupiter.api.Test; +import org.springframework.boot.sql.init.DatabaseInitializationMode; + import static org.assertj.core.api.Assertions.assertThat; /** @@ -28,16 +30,14 @@ class JdbcChatMemoryRepositoryPropertiesTests { @Test void defaultValues() { var props = new JdbcChatMemoryRepositoryProperties(); - assertThat(props.getInitializeSchema()) - .isEqualTo(JdbcChatMemoryRepositoryProperties.DatabaseInitializationMode.EMBEDDED); + assertThat(props.getInitializeSchema()).isEqualTo(DatabaseInitializationMode.EMBEDDED); } @Test void customValues() { var props = new JdbcChatMemoryRepositoryProperties(); - props.setInitializeSchema(JdbcChatMemoryRepositoryProperties.DatabaseInitializationMode.NEVER); - assertThat(props.getInitializeSchema()) - .isEqualTo(JdbcChatMemoryRepositoryProperties.DatabaseInitializationMode.NEVER); + props.setInitializeSchema(DatabaseInitializationMode.NEVER); + assertThat(props.getInitializeSchema()).isEqualTo(DatabaseInitializationMode.NEVER); } } diff --git a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/test/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemorySqlServerAutoConfigurationIT.java b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/test/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemorySqlServerAutoConfigurationIT.java index 72281c19c20..72064374a1a 100644 --- a/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/test/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemorySqlServerAutoConfigurationIT.java +++ b/auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-jdbc/src/test/java/org/springframework/ai/model/chat/memory/repository/jdbc/autoconfigure/JdbcChatMemorySqlServerAutoConfigurationIT.java @@ -52,21 +52,21 @@ class JdbcChatMemorySqlServerAutoConfigurationIT { @Test void jdbcChatMemoryScriptDatabaseInitializer_shouldBeLoaded() { this.contextRunner.withPropertyValues("spring.ai.chat.memory.repository.jdbc.initialize-schema=always") - .run(context -> assertThat(context.containsBean("jdbcChatMemoryScriptDatabaseInitializer")).isTrue()); + .run(context -> assertThat(context).hasBean("jdbcChatMemoryScriptDatabaseInitializer")); } @Test void jdbcChatMemoryScriptDatabaseInitializer_shouldNotRunSchemaInit() { this.contextRunner.withPropertyValues("spring.ai.chat.memory.repository.jdbc.initialize-schema=never") .run(context -> { - assertThat(context.containsBean("jdbcChatMemoryScriptDatabaseInitializer")).isTrue(); + assertThat(context).doesNotHaveBean("jdbcChatMemoryScriptDatabaseInitializer"); }); } @Test void initializeSchemaEmbeddedDefault() { this.contextRunner.withPropertyValues("spring.ai.chat.memory.repository.jdbc.initialize-schema=embedded") - .run(context -> assertThat(context.containsBean("jdbcChatMemoryScriptDatabaseInitializer")).isTrue()); + .run(context -> assertThat(context).hasBean("jdbcChatMemoryScriptDatabaseInitializer")); } @Test