{"slug": "spring-ai-2-1-session-memory-build-a-restart-safe-agent-with-jdbc", "title": "Spring AI 2.1 Session Memory: Build a Restart-Safe Agent with JDBC", "summary": "Spring AI 2.1.0-RC1 introduces first-party session memory that stores conversation events in an append-only repository and builds the model's active prompt as a view over that history, with a JDBC starter supporting PostgreSQL, MySQL, MariaDB, and H2 via Spring Boot auto-configuration. The release adds turn-aware compaction so prompts can be trimmed at turn boundaries without rewriting the underlying log, separating durable conversation state from per-call context. The feature is a release candidate, and the source notes that applications must still handle authentication and per-request session access themselves.", "body_md": "A support assistant can remember the last message perfectly and still forget the customer as soon as the application restarts. That is not a model problem. It is a state-management problem.\n\nSpring AI 2.1.0-RC1 introduces session memory as a first-party way to keep conversation events in a repository and construct the model's active context from that history. The JDBC starter makes PostgreSQL, MySQL, MariaDB, and H2 available through Spring Boot auto-configuration. The important shift is architectural: the conversation log is durable state, while the prompt is a view built for a particular model call. [Spring AI's release announcement](https://spring.io/blog/2026/10/23/spring-ai-2-1-0-RC1-available-now/) and [session reference](https://docs.spring.io/spring-ai/reference/2.1/api/session.html) describe the feature and its modules.\n\nThis tutorial wires a `ChatClient` to JDBC-backed sessions, passes a session identifier on each turn, and adds turn-aware compaction so the prompt does not grow without limit. It also covers the deployment and migration details that are easy to miss.\n\n**Disclosure:** This source-based tutorial draws on the Spring AI 2.1 reference and release notes. It does not document a production deployment, and the sample has not been validated against a live database. Spring AI 2.1.0-RC1 is a release candidate, so check the official project status before choosing it for a production system.\n\nA conventional chat-memory abstraction stores a list of messages under a conversation ID. That can be enough for a short exchange, but it gives you less structure when a conversation includes tool calls, long histories, or future workflow state.\n\nSpring AI's session model stores events in an append-only log. A session is the conversation's identity and metadata; events contain what happened. When the model needs context, the application reads a view of that event log. Compaction can change the active view without rewriting or deleting the original history. The session documentation describes turn-aware compaction: it works at turn boundaries so the model is not shown a tool call without its result or an answer without the question that led to it. [The concepts guide explains sessions, events, turns, and views](https://docs.spring.io/spring-ai/reference/2.1/api/session/concepts.html).\n\nThat separation is useful, but it does not mean the framework decides who is allowed to read a session. Your application still has to authenticate the caller, associate the session with the right principal, and enforce access on every request.\n\nUse the Spring AI 2.1.0-RC1 dependency set through the project's Spring AI BOM, and include the session JDBC starter. The release candidate's documented starter is:\n\n```\n<dependency>\n    <groupId>org.springframework.ai</groupId>\n    <artifactId>spring-ai-starter-model-session-jdbc</artifactId>\n</dependency>\n```\n\nYour application also needs a JDBC `DataSource`, supplied by the appropriate Spring Boot JDBC starter and database driver. The Spring AI reference lists PostgreSQL, MySQL, MariaDB, and H2 support for `JdbcSessionRepository`. With a `DataSource` available, Boot auto-configuration creates the repository and `SessionService` beans. For a local PostgreSQL or MySQL development database, the documented schema setting is:\n\n```\nspring.ai.session.repository.jdbc.initialize-schema=always\n```\n\nSchema initialization should be treated as an environment decision, not copied blindly into every deployment. Review the [JDBC session repository documentation](https://docs.spring.io/spring-ai/reference/2.1/api/session/jdbc.html) and your database migration policy before enabling automatic initialization in production. Keep schema changes in the process your team uses to review and apply database migrations.\n\nInject the model and `SessionService`, then build a client with session advisors. A compaction trigger and strategy can be configured alongside them:\n\n``` python\nimport org.springframework.ai.chat.client.ChatClient;\nimport org.springframework.ai.chat.model.ChatModel;\nimport org.springframework.ai.session.SessionService;\nimport org.springframework.ai.session.advisor.SessionAdvisors;\nimport org.springframework.ai.session.compaction.TurnCountTrigger;\nimport org.springframework.ai.session.compaction.WindowCompactionStrategy;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\nclass AssistantConfiguration {\n\n    @Bean\n    ChatClient chatClient(ChatModel chatModel,\n                          SessionService sessionService) {\n        return ChatClient.builder(chatModel)\n            .defaultAdvisors(SessionAdvisors.builder(sessionService)\n                .compaction(\n                    new TurnCountTrigger(20),\n                    WindowCompactionStrategy.builder()\n                        .lastTurns(10)\n                        .build())\n                .build())\n            .build();\n    }\n}\n```\n\nThe exact imports and builder signatures should be checked against the version selected by your BOM. The official quick start uses `SessionAdvisors.builder(sessionService)` and shows a `TurnCountTrigger(20)` with a window retaining the last ten turns. The numbers are examples, not universal token limits. A turn-count policy is simple to reason about, but different applications may need a token-based trigger or a custom strategy based on their context budget and the kind of history users need.\n\nUnlike a bare list truncation, compaction records a boundary in the event log. The active view can become shorter while the underlying events remain available for audit, alternate projections, or a future recovery strategy. That is helpful when you want the model's prompt bounded without pretending the earlier conversation never happened.\n\nEvery request for a continuing conversation must include its session ID. Spring AI intentionally does not fall back to one shared default: silently combining multiple people's histories would be worse than rejecting a request with a missing identifier.\n\n```\nString answer = chatClient.prompt()\n    .user(\"Can you summarize the issue I described earlier?\")\n    .advisors(SessionAdvisors.session(sessionId))\n    .call()\n    .content();\n```\n\nThe session ID should come from your application's conversation record or be created when the user starts a new conversation. Do not trust an arbitrary ID supplied by a browser without checking that the authenticated user is authorized to use it. A UUID is hard to guess, but unpredictability is not an authorization policy.\n\nFor a new conversation, create or obtain the session through the application flow, then return an opaque conversation handle to the client. On later requests, resolve that handle to a session only after checking ownership. The reference says a session can be created on first use, but ownership and request authorization remain application responsibilities.\n\nA session does not have a dedicated owner field. Spring AI documents session metadata and an indexed `SessionAttributes.PRINCIPAL_NAME` attribute for looking up sessions associated with a principal. Set identity metadata from the authenticated server-side principal, not from a name or email submitted in the request body. Then make authorization checks part of the service boundary that retrieves or mutates a session.\n\nSessions created by the service have an expiration time: the request's time-to-live or the documented default of 60 days. A `Session` constructed directly has no expiration unless its builder sets one. Decide whether that default matches your privacy, retention, and product requirements. Expiration is not a substitute for a retention policy covering database backups, logs, exports, or legal holds.\n\nA useful service-layer checklist is:\n\nThese controls do not come automatically from adding the JDBC dependency. The persistence layer can store a conversation correctly while the application still exposes it to the wrong user.\n\nThe session module includes `InMemorySessionRepository`, which is useful for tests and demonstrations. The reference shows building a `SessionService` directly around it:\n\n```\nSessionService sessionService = DefaultSessionService.builder()\n    .sessionRepository(InMemorySessionRepository.builder().build())\n    .build();\n```\n\nThis lets a unit test exercise session creation and advisor wiring without a database. It is not a persistence substitute: the in-memory repository loses sessions when the process stops and does not share state across application instances. Keep at least one integration test using the same database family and schema strategy that the deployed service will use.\n\nTests should verify behavior, not just that a bean starts:\n\n`ChatMemory`\nSpring AI 2.1.0 deprecates `ChatMemory`, `MessageWindowChatMemory`, `ChatMemoryRepository`, `MessageChatMemoryAdvisor`, and several repository implementations in favor of session memory. The release announcement says these APIs are deprecated for removal in 3.0.0, but remain available for now. The upgrade guide maps the old APIs to their session counterparts and notes that the old and new stores do not share their tables. [Read the official migration guide before switching](https://docs.spring.io/spring-ai/reference/2.1/api/chat-memory.html).\n\nA migration is therefore not just a dependency rename. Existing messages in `SPRING_AI_CHAT_MEMORY` are not automatically read from the session tables. Plan whether to migrate historical conversations, start new sessions while retaining read-only legacy access, or let old conversations expire under an explicit policy. The right choice depends on whether users need continuity and whether the data is still within your retention obligations.\n\nAlso check repository coverage before committing to the new abstraction. The 2.1 reference lists JDBC support for PostgreSQL, MySQL, MariaDB, and H2. It says session equivalents for several other chat-memory backends are planned, not already available. If your current application uses Redis, MongoDB, Neo4j, Cassandra, or a less common SQL dialect, verify the current implementation status instead of assuming a drop-in replacement exists.\n\nA durable event log makes history recoverable; it does not make every event safe to replay to every model or user. Decide which event types belong in model context, how sensitive data is handled, how long records are retained, and whether tool results can be reconstructed or must be redacted.\n\nLikewise, session memory is not semantic search. A window or compaction strategy chooses a bounded view of an ordered conversation. If an assistant must retrieve a specific fact from a large knowledge base, that is a separate retrieval problem. Spring AI's release notes explicitly say `VectorStoreChatMemoryAdvisor` remains available because session memory does not yet provide a semantic-recall counterpart.\n\nFinally, 2.1.0-RC1 is pre-release software. It includes meaningful new capabilities, but a release candidate deserves version pinning, upgrade-note review, and a deliberate stability decision. If the application needs a stable Spring AI line today, the release announcement identifies 2.0.2 as available alongside the release candidate. Check the official release page for newer releases before adopting either version.\n\nBefore replacing a chat-memory implementation, confirm these points:\n\nSpring AI 2.1 session memory is most useful when your application needs a durable conversation log and a bounded, turn-aware prompt view. It does not remove the need to design authorization, retention, database migrations, or model-context policy. Keeping those responsibilities visible is what makes the new abstraction useful rather than merely convenient.\n\nIf practical Spring AI and backend engineering guides are useful to you, subscribe for the next one. Have you built chat persistence around a flat message window, an event log, or something else? What trade-off mattered most in your application?", "url": "https://wpnews.pro/news/spring-ai-2-1-session-memory-build-a-restart-safe-agent-with-jdbc", "canonical_source": "https://dev.to/jamilxt/spring-ai-21-session-memory-build-a-restart-safe-agent-with-jdbc-2igh", "published_at": "2026-10-27 12:05:26+00:00", "updated_at": "2026-10-27 12:12:16.851441+00:00", "lang": "en", "topics": ["artificial-intelligence", "ai-agents", "ai-tools", "developer-tools", "large-language-models"], "entities": ["Spring AI", "Spring Boot", "PostgreSQL", "MySQL", "MariaDB", "H2", "ChatClient", "SessionService"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/spring-ai-2-1-session-memory-build-a-restart-safe-agent-with-jdbc", "markdown": "https://wpnews.pro/news/spring-ai-2-1-session-memory-build-a-restart-safe-agent-with-jdbc.md", "text": "https://wpnews.pro/news/spring-ai-2-1-session-memory-build-a-restart-safe-agent-with-jdbc.txt", "jsonld": "https://wpnews.pro/news/spring-ai-2-1-session-memory-build-a-restart-safe-agent-with-jdbc.jsonld"}}