Skip to content

Commit 08958af

Browse files
markpollackilayaperumalg
authored andcommitted
Add flexible support for multiple database variations in JdbcChatMemory
Add SQL Server dialect support with new SqlServerChatMemoryDialect class Create schema-sqlserver.sql script for SQL Server table creation Refactor database schema initialization with new JdbcChatMemorySchemaInitializer Standardize table names across databases to SPRING_AI_CHAT_MEMORY Implement ChatMemoryDialect abstraction for database-specific operations Add dialect implementations for PostgreSQL, MySQL/MariaDB, HSQLDB Update configuration properties with more flexible schema initialization options Enhance documentation with detailed information about dialect support and configuration Add integration tests for HSQLDB and SQL Server Signed-off-by: Mark Pollack <[email protected]>
1 parent 6ae1c13 commit 08958af

File tree

25 files changed

+913
-195
lines changed

25 files changed

+913
-195
lines changed

auto-configurations/models/chat/memory/spring-ai-autoconfigure-model-chat-memory-jdbc/pom.xml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,23 @@
7878
<artifactId>postgresql</artifactId>
7979
<scope>test</scope>
8080
</dependency>
81+
82+
<dependency>
83+
<groupId>com.microsoft.sqlserver</groupId>
84+
<artifactId>mssql-jdbc</artifactId>
85+
<scope>test</scope>
86+
</dependency>
87+
<dependency>
88+
<groupId>org.testcontainers</groupId>
89+
<artifactId>mssqlserver</artifactId>
90+
<scope>test</scope>
91+
</dependency>
92+
93+
<dependency>
94+
<groupId>org.hsqldb</groupId>
95+
<artifactId>hsqldb</artifactId>
96+
<scope>runtime</scope>
97+
</dependency>
8198
</dependencies>
8299

83100
</project>

auto-configurations/models/chat/memory/spring-ai-autoconfigure-model-chat-memory-jdbc/src/main/java/org/springframework/ai/model/chat/memory/jdbc/autoconfigure/JdbcChatMemoryDataSourceScriptDatabaseInitializer.java

Lines changed: 0 additions & 56 deletions
This file was deleted.

auto-configurations/models/chat/memory/spring-ai-autoconfigure-model-chat-memory-jdbc/src/main/java/org/springframework/ai/model/chat/memory/jdbc/autoconfigure/JdbcChatMemoryProperties.java

Lines changed: 0 additions & 44 deletions
This file was deleted.
Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,19 @@
1414
* limitations under the License.
1515
*/
1616

17-
package org.springframework.ai.model.chat.memory.jdbc.autoconfigure;
17+
package org.springframework.ai.model.chat.memory.repository.jdbc.autoconfigure;
1818

1919
import javax.sql.DataSource;
2020

2121
import org.slf4j.Logger;
2222
import org.slf4j.LoggerFactory;
2323

24+
import org.springframework.ai.chat.memory.jdbc.JdbcChatMemoryDialect;
2425
import org.springframework.ai.chat.memory.jdbc.JdbcChatMemoryRepository;
2526
import org.springframework.ai.model.chat.memory.autoconfigure.ChatMemoryAutoConfiguration;
2627
import org.springframework.boot.autoconfigure.AutoConfiguration;
2728
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2829
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
29-
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
3030
import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration;
3131
import org.springframework.boot.context.properties.EnableConfigurationProperties;
3232
import org.springframework.context.annotation.Bean;
@@ -39,24 +39,23 @@
3939
*/
4040
@AutoConfiguration(after = JdbcTemplateAutoConfiguration.class, before = ChatMemoryAutoConfiguration.class)
4141
@ConditionalOnClass({ JdbcChatMemoryRepository.class, DataSource.class, JdbcTemplate.class })
42-
@EnableConfigurationProperties(JdbcChatMemoryProperties.class)
43-
public class JdbcChatMemoryAutoConfiguration {
42+
@EnableConfigurationProperties(JdbcChatMemoryRepositoryProperties.class)
43+
public class JdbcChatMemoryRepositoryAutoConfiguration {
4444

45-
private static final Logger logger = LoggerFactory.getLogger(JdbcChatMemoryAutoConfiguration.class);
45+
private static final Logger logger = LoggerFactory.getLogger(JdbcChatMemoryRepositoryAutoConfiguration.class);
4646

4747
@Bean
4848
@ConditionalOnMissingBean
49-
JdbcChatMemoryRepository chatMemoryRepository(JdbcTemplate jdbcTemplate) {
50-
return JdbcChatMemoryRepository.builder().jdbcTemplate(jdbcTemplate).build();
49+
JdbcChatMemoryRepository chatMemoryRepository(JdbcTemplate jdbcTemplate, DataSource dataSource) {
50+
JdbcChatMemoryDialect dialect = JdbcChatMemoryDialect.from(dataSource);
51+
return JdbcChatMemoryRepository.builder().jdbcTemplate(jdbcTemplate).dialect(dialect).build();
5152
}
5253

5354
@Bean
5455
@ConditionalOnMissingBean
55-
@ConditionalOnProperty(prefix = JdbcChatMemoryProperties.CONFIG_PREFIX, name = "initialize-schema",
56-
havingValue = "true", matchIfMissing = true)
57-
JdbcChatMemoryDataSourceScriptDatabaseInitializer jdbcChatMemoryScriptDatabaseInitializer(DataSource dataSource) {
58-
logger.debug("Initializing schema for JdbcChatMemoryRepository");
59-
return new JdbcChatMemoryDataSourceScriptDatabaseInitializer(dataSource);
56+
JdbcChatMemoryRepositorySchemaInitializer jdbcChatMemoryScriptDatabaseInitializer(DataSource dataSource,
57+
JdbcChatMemoryRepositoryProperties properties) {
58+
return new JdbcChatMemoryRepositorySchemaInitializer(dataSource, properties);
6059
}
6160

6261
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright 2024-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.ai.model.chat.memory.repository.jdbc.autoconfigure;
18+
19+
import org.springframework.boot.context.properties.ConfigurationProperties;
20+
21+
/**
22+
* @author Jonathan Leijendekker
23+
* @author Thomas Vitale
24+
* @since 1.0.0
25+
*/
26+
@ConfigurationProperties(JdbcChatMemoryRepositoryProperties.CONFIG_PREFIX)
27+
public class JdbcChatMemoryRepositoryProperties {
28+
29+
public static final String CONFIG_PREFIX = "spring.ai.chat.memory.repository.jdbc";
30+
31+
/**
32+
* Whether to initialize the schema on startup. Values: embedded, always, never.
33+
* Default is embedded.
34+
*/
35+
private DatabaseInitializationMode initializeSchema = DatabaseInitializationMode.EMBEDDED;
36+
37+
/**
38+
* Locations of schema (DDL) scripts. Supports comma-separated list. Default is
39+
* classpath:org/springframework/ai/chat/memory/jdbc/schema-@@platform@@.sql
40+
*/
41+
private String schema = "classpath:org/springframework/ai/chat/memory/jdbc/schema-@@platform@@.sql";
42+
43+
public DatabaseInitializationMode getInitializeSchema() {
44+
return this.initializeSchema;
45+
}
46+
47+
public void setInitializeSchema(DatabaseInitializationMode initializeSchema) {
48+
this.initializeSchema = initializeSchema;
49+
}
50+
51+
public String getSchema() {
52+
return this.schema;
53+
}
54+
55+
public void setSchema(String schema) {
56+
this.schema = schema;
57+
}
58+
59+
public enum DatabaseInitializationMode {
60+
61+
/**
62+
* Always initialize the database.
63+
*/
64+
ALWAYS,
65+
66+
/**
67+
* Only initialize an embedded database.
68+
*/
69+
EMBEDDED,
70+
71+
/**
72+
* Never initialize the database.
73+
*/
74+
NEVER
75+
76+
}
77+
78+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Copyright 2024-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.ai.model.chat.memory.repository.jdbc.autoconfigure;
18+
19+
import java.util.List;
20+
21+
import javax.sql.DataSource;
22+
23+
import org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer;
24+
import org.springframework.boot.jdbc.init.PlatformPlaceholderDatabaseDriverResolver;
25+
import org.springframework.boot.sql.init.DatabaseInitializationMode;
26+
import org.springframework.boot.sql.init.DatabaseInitializationSettings;
27+
import org.springframework.util.StringUtils;
28+
29+
/**
30+
* Performs database initialization for the JDBC Chat Memory Repository.
31+
*
32+
* @author Mark Pollack
33+
* @since 1.0.0
34+
*/
35+
class JdbcChatMemoryRepositorySchemaInitializer extends DataSourceScriptDatabaseInitializer {
36+
37+
private static final String DEFAULT_SCHEMA_LOCATION = "classpath:org/springframework/ai/chat/memory/jdbc/schema-@@platform@@.sql";
38+
39+
JdbcChatMemoryRepositorySchemaInitializer(DataSource dataSource, JdbcChatMemoryRepositoryProperties properties) {
40+
super(dataSource, getSettings(dataSource, properties));
41+
}
42+
43+
static DatabaseInitializationSettings getSettings(DataSource dataSource,
44+
JdbcChatMemoryRepositoryProperties properties) {
45+
var settings = new DatabaseInitializationSettings();
46+
47+
// Determine schema locations
48+
String schemaProp = properties.getSchema();
49+
List<String> schemaLocations;
50+
PlatformPlaceholderDatabaseDriverResolver resolver = new PlatformPlaceholderDatabaseDriverResolver();
51+
try {
52+
String url = dataSource.getConnection().getMetaData().getURL().toLowerCase();
53+
if (url.contains("hsqldb")) {
54+
schemaLocations = List.of("classpath:org/springframework/ai/chat/memory/jdbc/schema-hsqldb.sql");
55+
}
56+
else if (StringUtils.hasText(schemaProp)) {
57+
schemaLocations = resolver.resolveAll(dataSource, schemaProp);
58+
}
59+
else {
60+
schemaLocations = resolver.resolveAll(dataSource, DEFAULT_SCHEMA_LOCATION);
61+
}
62+
}
63+
catch (Exception e) {
64+
// fallback to default
65+
if (StringUtils.hasText(schemaProp)) {
66+
schemaLocations = resolver.resolveAll(dataSource, schemaProp);
67+
}
68+
else {
69+
schemaLocations = resolver.resolveAll(dataSource, DEFAULT_SCHEMA_LOCATION);
70+
}
71+
}
72+
settings.setSchemaLocations(schemaLocations);
73+
74+
// Determine initialization mode
75+
JdbcChatMemoryRepositoryProperties.DatabaseInitializationMode init = properties.getInitializeSchema();
76+
DatabaseInitializationMode mode;
77+
if (JdbcChatMemoryRepositoryProperties.DatabaseInitializationMode.ALWAYS.equals(init)) {
78+
mode = DatabaseInitializationMode.ALWAYS;
79+
}
80+
else if (JdbcChatMemoryRepositoryProperties.DatabaseInitializationMode.NEVER.equals(init)) {
81+
mode = DatabaseInitializationMode.NEVER;
82+
}
83+
else {
84+
// embedded or default
85+
mode = DatabaseInitializationMode.EMBEDDED;
86+
}
87+
settings.setMode(mode);
88+
settings.setContinueOnError(true);
89+
return settings;
90+
}
91+
92+
}

auto-configurations/models/chat/memory/spring-ai-autoconfigure-model-chat-memory-jdbc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515
#
16-
org.springframework.ai.model.chat.memory.jdbc.autoconfigure.JdbcChatMemoryAutoConfiguration
16+
org.springframework.ai.model.chat.memory.repository.jdbc.autoconfigure.JdbcChatMemoryRepositoryAutoConfiguration

0 commit comments

Comments
 (0)