Session JDBC — Overview & Setup¶
spring-ai-session-jdbc provides a JDBC-backed implementation of SessionRepository
that stores session data in two relational tables. It supports PostgreSQL, MySQL, MariaDB,
and H2 out of the box.
Tables¶
AI_SESSION — session metadata (id, user_id, TTL, metadata JSON, event_version)
AI_SESSION_EVENT — append-only event log (FK → AI_SESSION, ON DELETE CASCADE)
AI_SESSION_EVENT rows are ordered by a monotonic seq column (insertion order) and carry
synthetic and archived flags. Compaction archives events in place rather than deleting
them, so the full history stays searchable. The event_version column on AI_SESSION is
incremented on every appendEvent and compactEvents call, enabling optimistic-lock
compaction.
Dependency¶
<dependency>
<groupId>org.springaicommunity</groupId>
<artifactId>spring-ai-session-jdbc</artifactId>
<version>${spring-ai-session.version}</version>
</dependency>
Schema¶
DDL scripts are bundled on the classpath. Run the one matching your database:
| Database | Script |
|---|---|
| PostgreSQL | org/springframework/ai/session/jdbc/schema-postgresql.sql |
| H2 | org/springframework/ai/session/jdbc/schema-h2.sql |
| MySQL / MariaDB | org/springframework/ai/session/jdbc/schema-mysql.sql |
Apply the schema with Spring Boot's SQL initialisation:
spring:
sql:
init:
schema-locations: classpath:org/springframework/ai/session/jdbc/schema-postgresql.sql
Or use your existing migration tool (Flyway, Liquibase) by copying the appropriate script into your migration directory.
Manual bean setup¶
@Bean
SessionRepository sessionRepository(DataSource dataSource) {
return JdbcSessionRepository.builder()
.dataSource(dataSource) // SQL dialect is auto-detected from the DataSource URL
.build();
}
@Bean
SessionService sessionService(SessionRepository sessionRepository) {
return DefaultSessionService.builder().sessionRepository(sessionRepository).build();
}
The builder accepts optional overrides:
JdbcSessionRepository.builder()
.dataSource(dataSource)
.dialect(new PostgresJdbcSessionRepositoryDialect()) // explicit dialect
.transactionManager(txManager) // custom tx manager
.jsonMapper(customJsonMapper) // custom JSON serialization
.build();
Using the repository¶
@Autowired SessionRepository sessions;
// Create a session
Session session = sessions.save(Session.builder()
.id(UUID.randomUUID().toString())
.userId("alice")
.build());
// Append a message event
sessions.appendEvent(SessionEvent.builder()
.id(UUID.randomUUID().toString())
.sessionId(session.id())
.timestamp(Instant.now())
.message(new UserMessage("Hello"))
.build());
// Query events
List<SessionEvent> events = sessions.findEvents(session.id(), EventFilter.builder().build());
Supported databases¶
| Database | Dialect class |
|---|---|
| PostgreSQL | PostgresJdbcSessionRepositoryDialect |
| H2 | H2JdbcSessionRepositoryDialect |
| MySQL / MariaDB | MysqlJdbcSessionRepositoryDialect |
Other databases default to the PostgreSQL dialect. Open an issue or contribute a dialect implementation for additional databases.
Design notes¶
Message serialisation — each SessionEvent's wrapped Message is stored in three
columns: message_type (enum name), message_content (plain text), and message_data
(JSON for tool calls and tool responses). This avoids Jackson polymorphism on Spring AI
message types.
Optimistic concurrency — the event_version column on AI_SESSION is incremented on
every appendEvent and compactEvents call. compactEvents atomically claims the version
slot with UPDATE … WHERE event_version = ? before modifying the event log, making
compaction safe under concurrent access.
synthetic column — stored as a dedicated BOOLEAN column (not only in the metadata
JSON blob) so EventFilter.excludeSynthetic() translates to a SQL predicate instead of
an in-process scan.
See also¶
- Auto-configuration — let Spring Boot wire everything automatically
- Session Concepts —
SessionRepositorySPI details - Context Compaction — optimistic-lock compaction with JDBC