spring-session用mysql实现session共享实践

前段时间,写了篇<spring-session-data-redis解决session共享的问题>文章,介绍了spring-session使用redis存储实现session共享的内部机制,遇到很多人提问到jdbc的实现或者为什么只有理论没有实践?索性就实践一下jdbc方式。1.spring-session实现了使用不同存储介质保存共享的session的apispring-sessio...

spring-session用mysql实现session共享实践

前段时间,写了篇<spring-session-data-redis解决session共享的问题>文章,介绍了spring-session使用redis存储实现session共享的内部机制,

遇到很多人提问到jdbc的实现或者为什么只有理论没有实践?索性就实践一下jdbc方式。

1.spring-session实现了使用不同存储介质保存共享的session的api

spring-session实现session共享的实现有三种:

jdbc:使用数据库做介质,支持多种数据,如oracle,sqlserver,mysql等,对应的实现类

RedisOperationsSessionRepository

                  <图一 支持的数据库类型及脚本>

redis:使用redis做存储介质,对应的实现类为:

JdbcOperationsSessionRepository

Hazelcast:使用内存做存储介质,对应的实现类为:

HazelcastSessionRepository

Hazelcast作为一个高度可扩展的数据分发和集群平台,提供了高效的、可扩展的分布式数据存储、数据缓存。Hazelcast是开源的,在分布式技术方面,Hazelcast提供了十分友好的接口供开发者选择,如Map,Queue,ExecutorService, Lock和Jcache。
Hazelcast的稳定性很高,分布式应用可以使用Hazelcast进行存储数据、同步数据、发布订阅消息等。Hazelcast是基于Java开发的,其客户端有Java, C/C , .NET以及REST。Hazelcast同时也支持memcache协议。它很好的支持了Hibernate,可以很容易的在当今流行的数据库系统中应用。

2.spring-session-jdbc使用mysql保存共享session的实现实践

2.0 准备工作

  如<图一 支持的数据库类型及脚本>所示,找到schema-mysql.sql脚本,放入sql-client客户端执行,如sqlyog,红色部分是我自己添加的,为了可以重复执行的。

DROP TABLE IF EXISTS SPRING_SESSION_ATTRIBUTES;DROP TABLE IF EXISTS SPRING_SESSION;CREATE TABLE SPRING_SESSION ( PRIMARY_ID CHAR(36) NOT NULL, SESSION_ID CHAR(36) NOT NULL, CREATION_TIME BIGINT NOT NULL, LAST_ACCESS_TIME BIGINT NOT NULL, MAX_INACTIVE_INTERVAL INT NOT NULL, EXPIRY_TIME BIGINT NOT NULL, PRINCIPAL_NAME VARCHAR(100), CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)) ENGINE=INNODB ROW_FORMAT=DYNAMIC;CREATE UNIQUE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);CREATE INDEX SPRING_SESSION_IX3 ON SPRING_SESSION (PRINCIPAL_NAME);CREATE TABLE SPRING_SESSION_ATTRIBUTES ( SESSION_PRIMARY_ID CHAR(36) NOT NULL, ATTRIBUTE_NAME VARCHAR(200) NOT NULL, ATTRIBUTE_BYTES BLOB NOT NULL, CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_PRIMARY_ID, ATTRIBUTE_NAME), CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_PRIMARY_ID) REFERENCES SPRING_SESSION(PRIMARY_ID) ON DELETE CASCADE) ENGINE=INNODB ROW_FORMAT=DYNAMIC;

此时创建了两张表

  SPRING_SESSION

  SPRING_SESSION_ATTRIBUTES

 通过PRIMARY_ID关联,执行结果如下:

2.1 使用sts创建spring-boot项目,项目名称session-jdbc,使用jdbc,mysql,web(方便测试)的start,创建完项目如下:

2.2 添加spring-session-jdbc依赖

  <dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-jdbc</artifactId>  </dependency>

2.3 配置jdbc属性application.properties

spring.session.store-type=JDBCspring.datasource.url=jdbc:mysql://localhost:3306/www?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT+8&useSSL=falsespring.datasource.username=rootspring.datasource.password=wangwei456spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

注意,新版mysql的url需要增加

serverTimezone选项

而且,driver-class-name也变为com.mysql.cj.jdbc.Driver,否则有报警。

2.4 创建测试controller

package com.example.demo;import javax.servlet.http.HttpSession;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RestController@RequestMapping("/jdbc")public class TestController { @RequestMapping("/welcome") public String welcome(HttpSession httpSession) {  httpSession.setAttribute("hello", "world");  return "hello world !"; }}

2.5 测试

启动spring-boot项目session-jdbc,访问http://localhost:8080/jdbc/welcome/,返回"hello world".

查看数据库数据信息

SPRING_SESSION表信息

SPRING_SESSION_ATTRIBUTES表信息

2.6 Q&A

2.6.1session_id是如何产生的?

断点调试进入

此时的cookie和请求的一致。

进入HttpSessionAdapter的setAttribute属性方法,再调用JdbcOperationsSessionRepository的setAttribute方法

@Override  public void setAttribute(String attributeName, Object attributeValue) {boolean attributeExists = (this.delegate.getAttribute(attributeName) != null);boolean attributeRemoved = (attributeValue == null);if (!attributeExists && attributeRemoved) { return;}if (attributeExists) { if (attributeRemoved) {  this.delta.merge(attributeName, DeltaValue.REMOVED, (oldDeltaValue, deltaValue) -> (oldDeltaValue == DeltaValue.ADDED) ? null: deltaValue); } else {  this.delta.merge(attributeName, DeltaValue.UPDATED, (oldDeltaValue,deltaValue) -> (oldDeltaValue == DeltaValue.ADDED)  ? oldDeltaValue  : deltaValue); }}else { this.delta.merge(attributeName, DeltaValue.ADDED, 
源文地址:https://www.guoxiongfei.cn/cntech/9864.html