Spring Session Management – Spring Session JDBC

Filed Under: Spring

Spring Session Module provides APIs and implementation for managing user session in a web application.

Spring Session

Spring Session consists of following modules:

  1. Spring Session Core: Provides API and core support for session management.
  2. Spring Session JDBC: provides session management using relational database.
  3. Spring Session Data Redis: provides session management implementation for Redis database.
  4. Spring Session Hazelcast: provides session management support using Hazelcast.

Spring Session Benefits

  • Spring Session decouples session management logic from the application, making it more fault tolerant.
  • Spring Session keeps user session information in the database, so it’s great to use in a clustered environment with multiple server nodes. We don’t need the sticky session or session replication logic.
  • User session data is not lost if the application crashes. When the application is started again it picks up all the user session data.
  • It’s easy to switch between session storage software, just by changing some configuration we can switch from using Redis to JDBC.
  • Spring is an open source project and easily bootstrapped. If you face any issues, there is a good chance that you will find the solution easily online.

Spring Session JDBC Example

Let’s create a simple Spring Session JDBC example project. I will use Spring Boot and MySQL database in the application. So we would need following dependencies in our project.

  • spring-boot-starter-web: It’s a spring mvc web application.
  • spring-boot-starter-jdbc: For Spring Boot JDBC support and auto configuration.
  • spring-boot-starter-thymeleaf: This is to support Thymeleaf for our view pages. Thymeleaf is simple to use and provides good integration with Spring framework.
  • spring-session-core: Spring Session Core API
  • spring-session-jdbc: Spring Session JDBC Implementation
  • mysql-connector-java: MySQL JDBC Driver Jar

Here is our final pom.xml file:


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.journaldev.spring</groupId>
	<artifactId>Spring-Session-Example</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>Spring-Session-Example</name>
	<description>Spring Session Example</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.3.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>10</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.session</groupId>
			<artifactId>spring-session-core</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.session</groupId>
			<artifactId>spring-session-jdbc</artifactId>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

You can generate the base maven project using Spring Initializr and then add required dependencies.

Below image shows the final project structure from Spring Tool Suite.

Spring Session JDBC Example

Let’s look at each of the components one by one.

application.properties

This is where we specify our database configuration and some spring session attributes.


spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/SpringSessionDB
spring.datasource.username=journaldev
spring.datasource.password=journaldev

spring.session.store-type=jdbc
spring.session.jdbc.initialize-schema=always
spring.session.timeout.seconds=900

I am specifying spring.session.jdbc.initialize-schema to always so that Spring will create the required tables for us.

Usually, in the production environment, the application user doesn’t have the privilege to execute DDL statements, in that case, set this attribute to never and create database tables manually from the scripts provided in the JAR file.

spring session jdbc schema sql scripts

index.html

Here is our view page built using Thymeleaf.


<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Spring Session JDBC Example</title>
</head>
<body>
	<div>
		<form th:action="@{/saveMessage}" method="post">
			<textarea name="msg" cols="40" rows="2"></textarea>
			<br> <input type="submit" value="Save Message" />
		</form>
	</div>
	<div>
		<h2>Messages</h2>
		<ul th:each="m : ${messages}">
			<li th:text="${m}">msg</li>
		</ul>
	</div>
	<div>
		<form th:action="@{/invalidate}" method="post">
			<input type="submit" value="Destroy Session" />
		</form>
	</div>
</body>
</html>

Notice that /saveMessage will be used to send message to server. We will save this message to user session attribute.

When the home page is requested, messages attribute will be set to model. So if the user session is valid, we should see all the messages saved on the home page. Spring session uses Cookies to identify user session, so if you hit reload then also you will see all the earlier saved messages.

Finally, there is a button to invalidate and destroy the session. Once the session is destroyed, it’s attribute will get deleted and you won’t see the earlier saved messages.

HomeController.java

This is our controller class where we are handling user requests and saving the message to user session attribute.


package com.journaldev.spring;

import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class HomeController {

	@GetMapping("/")
	public String home(Model model, HttpSession session) {
		@SuppressWarnings("unchecked")
		List<String> msgs = (List<String>) session.getAttribute("MY_MESSAGES");

		if (msgs == null) {
			msgs = new ArrayList<>();
		}
		model.addAttribute("messages", msgs);

		return "index";
	}

	@PostMapping("/saveMessage")
	public String saveMessage(@RequestParam("msg") String msg, HttpServletRequest request) {
		@SuppressWarnings("unchecked")
		List<String> msgs = (List<String>) request.getSession().getAttribute("MY_MESSAGES");
		if (msgs == null) {
			msgs = new ArrayList<>();
			request.getSession().setAttribute("MY_MESSAGES", msgs);
		}
		msgs.add(msg);
		request.getSession().setAttribute("MY_MESSAGES", msgs);
		return "redirect:/";
	}

	@PostMapping("/invalidate")
	public String destroySession(HttpServletRequest request) {
		request.getSession().invalidate();
		return "redirect:/";
	}
}

Most of the logic is straightforward, notice that we are not using anything from Spring Session module. This is the beauty of spring framework, it will automatically configure our application to use the database for session management.

SpringSessionExampleApplication.java

Just a Spring Boot Application to run our application.


package com.journaldev.spring;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringSessionExampleApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringSessionExampleApplication.class, args);
	}
}

Spring Session JDBC Test

Our application is ready, just run SpringSessionExampleApplication class as java application. You will notice following mappings in the console logs.


Mapped "{[/saveMessage],methods=[POST]}" onto public java.lang.String com.journaldev.spring.HomeController.saveMessage(java.lang.String,javax.servlet.http.HttpServletRequest)
Mapped "{[/invalidate],methods=[POST]}" onto public java.lang.String com.journaldev.spring.HomeController.destroySession(javax.servlet.http.HttpServletRequest)
Mapped "{[/],methods=[GET]}" onto public java.lang.String com.journaldev.spring.HomeController.home(org.springframework.ui.Model,javax.servlet.http.HttpSession)

You will also notice logger for execution of SQL script to create database tables.


Executing SQL script from class path resource [org/springframework/session/jdbc/schema-mysql.sql]

You can check these tables in the database too.

spring session database tables

Our index.html page will be automatically configured as the welcome page, as seen in the logs.


2018-06-27 12:50:09.761  INFO 1666 --- [main] o.s.b.a.w.s.WelcomePageHandlerMapping: Adding welcome page template: index

Below screen recording shows the output when the application is executed.

Spring Session JDBC Application Output

Summary

Spring Session is an awesome module that separates session management logic from application logic. It makes our application fault-tolerant and reduces memory usage. It’s very easy to implement and best suited for the clustered environment.

You can download the example code from our GitHub Repository.

Comments

  1. Raghavender Reddy says:

    Nice post. I have one question on performance with JDBC session type since for every get session request query will execute and this cause connection leak issue. Please let me know if there any way to add this concept to second level cache or not.

  2. Carlos says:

    Great post, thanks for share.

    from Java Friend from Brazil

Leave a Reply

Your email address will not be published. Required fields are marked *

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages