其他分享
首页 > 其他分享> > springboot2.1.3+spring-session2.1.4分库处理

springboot2.1.3+spring-session2.1.4分库处理

作者:互联网

使用spring session框架来统一管理session,该框架支持jdbc、redis存储,使用非常简单,可以去官网查看文档一步步接入即可,
官网文档如下:https://docs.spring.io/spring-session/docs/current/reference/html5/,

不过,我使用的场景官网没有提供方法给予解决,最后,本人只能重写了它的部分源码,来实现分库管理session,好了,上代码。

 

pom.xml

<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-core</artifactId>
    <version>2.1.4.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-jdbc</artifactId>
    <version>2.1.4.RELEASE</version>
</dependency>
<!-- 
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-redis</artifactId>
    <version>2.1.4.RELEASE</version>
</dependency>
-->
<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>3.2.0</version>
</dependency>

 

application.properties

# spring-session setting, timeout: 2 years
spring.session.timeout.setting=63072000

# sharding number, CONFIG_SHARDING_NUM 环境变量名
mydb.server.sharding.num=${CONFIG_SHARDING_NUM:4}

# global setting dbs parameter
mysql.global.pools.MinimumIdle=1
mysql.global.pools.MaximumPoolSize=20
mysql.global.pools.IdleTimeout=600000
mysql.global.pools.MaxLifetime=1800000

# 这里配置分库信息,这里只是demo,本人配置了4个主库,至于分库分表不在本博客中体现。
# master0
sharding.jdbc.datasource.mainshard0.type=com.zaxxer.hikari.HikariDataSource
sharding.jdbc.datasource.mainshard0.driver-class-name=org.mariadb.jdbc.Driver
sharding.jdbc.datasource.mainshard0.jdbc-url=jdbc:mysql://127.0.0.1:3306/demo?useUnicode=true&characterEncoding-utf8&allowMutiQueries=true
sharding.jdbc.datasource.mainshard0.username=xxxx
sharding.jdbc.datasource.mainshard0.password=xxxxxx

# master1
sharding.jdbc.datasource.mainshard1.type=com.zaxxer.hikari.HikariDataSource
sharding.jdbc.datasource.mainshard1.driver-class-name=org.mariadb.jdbc.Driver
sharding.jdbc.datasource.mainshard1.jdbc-url=jdbc:mysql://127.0.0.1:3307/demo?useUnicode=true&characterEncoding-utf8&allowMutiQueries=true
sharding.jdbc.datasource.mainshard1.username=xxxx
sharding.jdbc.datasource.mainshard1.password=xxxxxx

# master2
sharding.jdbc.datasource.mainshard2.type=com.zaxxer.hikari.HikariDataSource
sharding.jdbc.datasource.mainshard2.driver-class-name=org.mariadb.jdbc.Driver
sharding.jdbc.datasource.mainshard2.jdbc-url=jdbc:mysql://127.0.0.1:3308/demo?useUnicode=true&characterEncoding-utf8&allowMutiQueries=true
sharding.jdbc.datasource.mainshard2.username=xxxx
sharding.jdbc.datasource.mainshard2.password=xxxxxx

# master3
sharding.jdbc.datasource.mainshard3.type=com.zaxxer.hikari.HikariDataSource
sharding.jdbc.datasource.mainshard3.driver-class-name=org.mariadb.jdbc.Driver
sharding.jdbc.datasource.mainshard3.jdbc-url=jdbc:mysql://127.0.0.1:3309/demo?useUnicode=true&characterEncoding-utf8&allowMutiQueries=true
sharding.jdbc.datasource.mainshard3.username=xxxx
sharding.jdbc.datasource.mainshard3.password=xxxxxx

 

重写第一个类(源码文件是JdbcHttpSessionConfiguration.java,可以去官网下载),本人重写如下:

package com.szl.demo.spring.session.common.datasource.sessionConfig;

import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.core.serializer.support.DeserializingConverter;
import org.springframework.core.serializer.support.SerializingConverter;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.jdbc.support.MetaDataAccessException;
import org.springframework.jdbc.support.lob.DefaultLobHandler;
import org.springframework.jdbc.support.lob.LobHandler;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.session.MapSession;
import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration;
import org.springframework.session.jdbc.JdbcOperationsSessionRepository;
import org.springframework.session.jdbc.config.annotation.SpringSessionDataSource;
import org.springframework.session.web.http.SessionRepositoryFilter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.util.StringUtils;
import org.springframework.util.StringValueResolver;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

/**
 *  @author Jimmy Shan
 *  @date 2019-06-25
 *  @desc 重写jdbc session配置类
 */
@Configuration
@EnableScheduling
public class CustomizedJdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration
        implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware,
        SchedulingConfigurer {

  static final String DEFAULT_CLEANUP_CRON = "0 0 0/1 * * *";
  private String tableName = JdbcOperationsSessionRepository.DEFAULT_TABLE_NAME;
  private String cleanupCron = DEFAULT_CLEANUP_CRON;
  private LobHandler lobHandler;
  private ConversionService springSessionConversionService;
  private ConversionService conversionService;
  private ClassLoader classLoader;
  private StringValueResolver embeddedValueResolver;
  
  //--------------Modify by Jimmy Shan, the date is 2019-06-25 start------//
  @Value("${mydb.server.sharding.num}")
  private String shardingNum;
  @Autowired
  private Environment env;
  private Map<Integer, JdbcTemplate> myJdbcTemplateMap = new HashMap<>();
  private Map<Integer, PlatformTransactionManager> myDataSourceTransactionMap = new HashMap<>();
  private DataSource dataSource;
  // 时间设置,间隔时间为30秒
  private Integer maxInactiveIntervalInSeconds;
  //--------------Modify by Jimmy Shan, the date is 2019-06-25 end-------//
    
    
    /**
     * @desc 创建数据源,手动创建,为了后面的分库分表
     */
    private DataSource convertDataSource(int num) {
        HikariConfig hkConfig = new HikariConfig();
        hkConfig.setDriverClassName("org.mariadb,jdbc.Driver");
        hkConfig.setMinimumIdle(env.getProperty("mysql.global.pools.MinimumIdle") == null ? 5 : env.getProperty("mysql.global.pools.MinimumIdle")));
        hkConfig.setMaximumPoolSize(env.getProperty("mysql.global.pools.MaximumPoolSize") == null ? 20 : env.getProperty("mysql.global.pools.MaximumPoolSize")));
        hkConfig.setIdleTimeout(env.getProperty("mysql.global.pools.IdleTimeout") == null ? 600000 : Integer.parseInt(env.getProperty("mysql.global.pools.IdleTimeout")));
        hkConfig.setMaxLifetime(env.getProperty("mysql.global.pools.MaxLifetime") == null ? 1800000 : Integer.parseInt(env.getProperty("mysql.global.pools.MaxLifetime")));
        hkConfig.setJdbcUrl(env.getProperty("sharding.jdbc.datasource.mainshard" + num + ".jdbc-url"));
        hkConfig.setUsername(env.getProperty("sharding.jdbc.datasource.mainshard" + num + ".username"));
        hkConfig.setPassword(env.getProperty("sharding.jdbc.datasource.mainshard" + num + ".password"));
        HikariDataSource ds = new HikariDataSource(hkConfig);
        return ds;
    }
    
  /**
   * @desc 重写了部分逻辑
   */
    @Bean
    public CustomizedJdbcOperationsSessionRepository sessionRepository() {
      for (int i = 0; i < Integer.parseInt(shardingNum); i++) {
          DataSource ds = convertDataSource(i);
          myJdbcTemplateMap.put(i, new JdbcTemplate(ds));
          myDataSourceTransactionMap.put(i, new DataSourceTransactionManager(ds));
      }
      
      this.dataSource = myJdbcTemplateMap.get(0).getDataSource();
        CustomizedJdbcOperationsSessionRepository sessionRepository = 
            new JdbcOperationsSessionRepository(myJdbcTemplateMap, myDataSourceTransactionMap);
        if (StringUtils.hasText(this.tableName)) {
            sessionRepository.setTableName(this.tableName);
        }
        
        this.setMaxInactiveIntervalInSeconds(Integer.parseInt(env.getProperty("spring.session.timeout.setting")));
        sessionRepository.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
        if (this.lobHandler != null) {
             sessionRepository.setLobHandler(this.lobHandler);
        }    else if (requiresTemporaryLob(this.dataSource)) {
             DefaultLobHandler lobHandler = new DefaultLobHandler();
             lobHandler.setCreateTemporaryLob(true);
             sessionRepository.setLobHandler(lobHandler);
        }
        if (this.springSessionConversionService != null) {
             sessionRepository.setConversionService(this.springSessionConversionService);
        }    else if (this.conversionService != null) {
             sessionRepository.setConversionService(this.conversionService);
        }    else {
             sessionRepository.setConversionService(createConversionServiceWithBeanClassLoader());
        }
        return sessionRepository;
    }

    private static boolean requiresTemporaryLob(DataSource dataSource) {
        try {
            String productName = JdbcUtils.extractDatabaseMetaData(dataSource,
                    "getDatabaseProductName");
            return "Oracle".equalsIgnoreCase(JdbcUtils.commonDatabaseName(productName));
        }
        catch (MetaDataAccessException ex) {
            return false;
        }
    }

    public void setMaxInactiveIntervalInSeconds(Integer maxInactiveIntervalInSeconds) {
        this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
    }

    public void setTableName(String tableName) {
        this.tableName = tableName;
    }

    public void setCleanupCron(String cleanupCron) {
        this.cleanupCron = cleanupCron;
    }

    @Autowired(required = false)
    @Qualifier("springSessionLobHandler")
    public void setLobHandler(LobHandler lobHandler) {
        this.lobHandler = lobHandler;
    }

    @Autowired(required = false)
    @Qualifier("springSessionConversionService")
    public void setSpringSessionConversionService(ConversionService conversionService) {
        this.springSessionConversionService = conversionService;
    }

    @Autowired(required = false)
    @Qualifier("conversionService")
    public void setConversionService(ConversionService conversionService) {
        this.conversionService = conversionService;
    }

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        this.classLoader = classLoader;
    }

    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        this.embeddedValueResolver = resolver;
    }

    @Override
    public void setImportMetadata(AnnotationMetadata importMetadata) {
        Map<String, Object> attributeMap = importMetadata
                .getAnnotationAttributes(EnableJdbcHttpSession.class.getName());
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(attributeMap);
        this.maxInactiveIntervalInSeconds = attributes
                .getNumber("maxInactiveIntervalInSeconds");
        String tableNameValue = attributes.getString("tableName");
        if (StringUtils.hasText(tableNameValue)) {
            this.tableName = this.embeddedValueResolver
                    .resolveStringValue(tableNameValue);
        }
        String cleanupCron = attributes.getString("cleanupCron");
        if (StringUtils.hasText(cleanupCron)) {
            this.cleanupCron = cleanupCron;
        }
    }

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.addCronTask(() -> sessionRepository().cleanUpExpiredSessions(),
                this.cleanupCron);
    }

    private GenericConversionService createConversionServiceWithBeanClassLoader() {
        GenericConversionService conversionService = new GenericConversionService();
        conversionService.addConverter(Object.class, byte[].class,
                new SerializingConverter());
        conversionService.addConverter(byte[].class, Object.class,
                new DeserializingConverter(this.classLoader));
        return conversionService;
    }
}

 

 

重写第二个类(源码文件是JdbcOperationsSessionRepository.java,可以去官网下载),本人重写如下:

package com.szl.demo.spring.session.common.datasource.sessionConfig;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.core.serializer.support.DeserializingConverter;
import org.springframework.core.serializer.support.SerializingConverter;
import org.springframework.dao.DataAccessException;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.support.lob.DefaultLobHandler;
import org.springframework.jdbc.support.lob.LobHandler;
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.MapSession;
import org.springframework.session.Session;
import org.springframework.session.jdbc.JdbcOperationsSessionRepository;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionOperations;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

/**
 * @author Jimmy Shan
 * @date 2019-06-25
 * @desc 重写jdbc session类
 */
public class CustomizedJdbcOperationsSessionRepository implements
        FindByIndexNameSessionRepository<JdbcOperationsSessionRepository.JdbcSession> {
        
    public static final String DEFAULT_TABLE_NAME = "SPRING_SESSION";
    private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
    private static final String CREATE_SESSION_QUERY =
            "INSERT INTO %TABLE_NAME%(PRIMARY_ID, SESSION_ID, CREATION_TIME, LAST_ACCESS_TIME, MAX_INACTIVE_INTERVAL, EXPIRY_TIME, PRINCIPAL_NAME) " +
                    "VALUES (?, ?, ?, ?, ?, ?, ?)";
    private static final String CREATE_SESSION_ATTRIBUTE_QUERY =
            "INSERT INTO %TABLE_NAME%_ATTRIBUTES(SESSION_PRIMARY_ID, ATTRIBUTE_NAME, ATTRIBUTE_BYTES) " +
                    "SELECT PRIMARY_ID, ?, ? " +
                    "FROM %TABLE_NAME% " +
                    "WHERE SESSION_ID = ?";
    private static final String GET_SESSION_QUERY =
            "SELECT S.PRIMARY_ID, S.SESSION_ID, S.CREATION_TIME, S.LAST_ACCESS_TIME, S.MAX_INACTIVE_INTERVAL, SA.ATTRIBUTE_NAME, SA.ATTRIBUTE_BYTES " +
                    "FROM %TABLE_NAME% S " +
                    "LEFT OUTER JOIN %TABLE_NAME%_ATTRIBUTES SA ON S.PRIMARY_ID = SA.SESSION_PRIMARY_ID " +
                    "WHERE S.SESSION_ID = ?";
    private static final String UPDATE_SESSION_QUERY =
            "UPDATE %TABLE_NAME% SET SESSION_ID = ?, LAST_ACCESS_TIME = ?, MAX_INACTIVE_INTERVAL = ?, EXPIRY_TIME = ?, PRINCIPAL_NAME = ? " +
                    "WHERE PRIMARY_ID = ?";
    private static final String UPDATE_SESSION_ATTRIBUTE_QUERY =
            "UPDATE %TABLE_NAME%_ATTRIBUTES SET ATTRIBUTE_BYTES = ? " +
                    "WHERE SESSION_PRIMARY_ID = ? " +
                    "AND ATTRIBUTE_NAME = ?";
    private static final String DELETE_SESSION_ATTRIBUTE_QUERY =
            "DELETE FROM %TABLE_NAME%_ATTRIBUTES " +
                    "WHERE SESSION_PRIMARY_ID = ? " +
                    "AND ATTRIBUTE_NAME = ?";
    private static final String DELETE_SESSION_QUERY =
            "DELETE FROM %TABLE_NAME% " +
                    "WHERE SESSION_ID = ?";
    private static final String LIST_SESSIONS_BY_PRINCIPAL_NAME_QUERY =
            "SELECT S.PRIMARY_ID, S.SESSION_ID, S.CREATION_TIME, S.LAST_ACCESS_TIME, S.MAX_INACTIVE_INTERVAL, SA.ATTRIBUTE_NAME, SA.ATTRIBUTE_BYTES " +
                    "FROM %TABLE_NAME% S " +
                    "LEFT OUTER JOIN %TABLE_NAME%_ATTRIBUTES SA ON S.PRIMARY_ID = SA.SESSION_PRIMARY_ID " +
                    "WHERE S.PRINCIPAL_NAME = ?";
    private static final String DELETE_SESSIONS_BY_EXPIRY_TIME_QUERY =
            "DELETE FROM %TABLE_NAME% " +
                    "WHERE EXPIRY_TIME < ?";

    private static final Log logger = LogFactory.getLog(JdbcOperationsSessionRepository.class);
    private static final PrincipalNameResolver PRINCIPAL_NAME_RESOLVER = new PrincipalNameResolver();
    private final ResultSetExtractor<List<JdbcSession>> extractor = new SessionResultSetExtractor();
    private String tableName = DEFAULT_TABLE_NAME;
    private String createSessionQuery;
    private String createSessionAttributeQuery;
    private String getSessionQuery;
    private String updateSessionQuery;
    private String updateSessionAttributeQuery;
    private String deleteSessionAttributeQuery;
    private String deleteSessionQuery;
    private String listSessionsByPrincipalNameQuery;
    private String deleteSessionsByExpiryTimeQuery;
    private Integer defaultMaxInactiveInterval;
    private ConversionService conversionService;
  private LobHandler lobHandler = new DefaultLobHandler();
    
  //--------------Modify by Jimmy Shan, the date is 2019-06-25 start--------------//
  private Map<Integer, JdbcOperations> jdbcOperationMaps = new Hash<>();
  private Map<Integer, TransactionOperations> transOperationMaps = new Hash<>();
  @Value("{mydb.server.sharding.num}")
  private String shardingNum;
  //--------------Modify by Jimmy Shan, the date is 2019-06-25 end----------------//
    
  /**
   * @desc 重写这个方法
   */
    public CustomizedJdbcOperationsSessionRepository(Map<Integer, JdbcTemplate> myJdbcTemplateMap,
            Map<Integer, PlatformTransactionManager> myDataSourceTransactionMap) {
            if (myJdbcTemplateMap == null || myJdbcTemplateMap.isEmpty()) {
                Assert.notNull(myJdbcTemplateMap, "myJdbcTemplateMap must not be null");
            }
            if (myDataSourceTransactionMap == null || myDataSourceTransactionMap.isEmpty()) {
                Assert.notNull(myDataSourceTransactionMap, "myDataSourceTransactionMap must not be null");
            }
            jdbcOperationMaps.putAll(myJdbcTemplateMap);
            this.conversionService = createDefaultConversionService();
            prepareQueries();
            Set<Integer> setKey = myDataSourceTransactionMap.keySet();
            for (Iterator ir = setKey.iterator(); ir.hasNext(); ) {
                Integer key = (Integer) ir.next();
                TransactionOperations transOperations = createTransactionTemplate(myDataSourceTransactionMap.get(key));
                transOperationMaps.put(key, transOperations);
            }        
    }
  
    public void setTableName(String tableName) {
        Assert.hasText(tableName, "Table name must not be empty");
        this.tableName = tableName.trim();
        prepareQueries();
    }
  
    public void setCreateSessionQuery(String createSessionQuery) {
        Assert.hasText(createSessionQuery, "Query must not be empty");
        this.createSessionQuery = createSessionQuery;
    }
  
    public void setCreateSessionAttributeQuery(String createSessionAttributeQuery) {
        Assert.hasText(createSessionAttributeQuery, "Query must not be empty");
        this.createSessionAttributeQuery = createSessionAttributeQuery;
    }
  
    public void setGetSessionQuery(String getSessionQuery) {
        Assert.hasText(getSessionQuery, "Query must not be empty");
        this.getSessionQuery = getSessionQuery;
    }
  
    public void setUpdateSessionQuery(String updateSessionQuery) {
        Assert.hasText(updateSessionQuery, "Query must not be empty");
        this.updateSessionQuery = updateSessionQuery;
    }
  
    public void setUpdateSessionAttributeQuery(String updateSessionAttributeQuery) {
        Assert.hasText(updateSessionAttributeQuery, "Query must not be empty");
        this.updateSessionAttributeQuery = updateSessionAttributeQuery;
    }
  
    public void setDeleteSessionAttributeQuery(String deleteSessionAttributeQuery) {
        Assert.hasText(deleteSessionAttributeQuery, "Query must not be empty");
        this.deleteSessionAttributeQuery = deleteSessionAttributeQuery;
    }
  
    public void setDeleteSessionQuery(String deleteSessionQuery) {
        Assert.hasText(deleteSessionQuery, "Query must not be empty");
        this.deleteSessionQuery = deleteSessionQuery;
    }
  
    public void setListSessionsByPrincipalNameQuery(String listSessionsByPrincipalNameQuery) {
        Assert.hasText(listSessionsByPrincipalNameQuery, "Query must not be empty");
        this.listSessionsByPrincipalNameQuery = listSessionsByPrincipalNameQuery;
    }
  
    public void setDeleteSessionsByExpiryTimeQuery(String deleteSessionsByExpiryTimeQuery) {
        Assert.hasText(deleteSessionsByExpiryTimeQuery, "Query must not be empty");
        this.deleteSessionsByExpiryTimeQuery = deleteSessionsByExpiryTimeQuery;
    }
  
    public void setDefaultMaxInactiveInterval(Integer defaultMaxInactiveInterval) {
        this.defaultMaxInactiveInterval = defaultMaxInactiveInterval;
    }

    public void setLobHandler(LobHandler lobHandler) {
        Assert.notNull(lobHandler, "LobHandler must not be null");
        this.lobHandler = lobHandler;
    }
  
    public void setConversionService(ConversionService conversionService) {
        Assert.notNull(conversionService, "conversionService must not be null");
        this.conversionService = conversionService;
    }

    @Override
    public JdbcSession createSession() {
        JdbcSession session = new JdbcSession();
        if (this.defaultMaxInactiveInterval != null) {
            session.setMaxInactiveInterval(Duration.ofSeconds(this.defaultMaxInactiveInterval));
        }
        return session;
    }
  
  
  /**
   * @desc Modify by Jimmy Shan, the date is 2019-06-25
   *      Implementing Routing Function
   */
    @Override
    public void save(final JdbcSession session) {
      String sessionId = session.getId();
      int hashCode = Math.abs(sessionId.hashCode());
      int mod = hashCode % Integer.parseInt(shardingNum);
        if (session.isNew()) {
            this.transOperationMaps.get(mod).execute(new TransactionCallbackWithoutResult() {
                        @Override
                        protected void doInTransactionWithoutResult(TransactionStatus status) {
                            CustomizedJdbcOperationsSessionRepository.this.jdbcOperationMaps.get(mod).update(
                                    CustomizedJdbcOperationsSessionRepository.this.createSessionQuery,
                                    (ps) -> {
                                        ps.setString(1, session.primaryKey);
                                        ps.setString(2, session.getId());
                                        ps.setLong(3, session.getCreationTime().toEpochMilli());
                                        ps.setLong(4, session.getLastAccessedTime().toEpochMilli());
                                        ps.setInt(5, (int) session.getMaxInactiveInterval().getSeconds());
                                        ps.setLong(6, session.getExpiryTime().toEpochMilli());
                                        ps.setString(7, session.getPrincipalName());
                                    });
                            Set<String> attributeNames = session.getAttributeNames();
                            if (!attributeNames.isEmpty()) {
                                  insertSessionAttributes(session, new ArrayList<>(attributeNames));
                            }
                }
            });
        }    else {
            this.transOperationMaps.get(mod).execute(new TransactionCallbackWithoutResult() {
                        @Override
                        protected void doInTransactionWithoutResult(TransactionStatus status) {
                            if (session.isChanged()) {
                                    CustomizedJdbcOperationsSessionRepository.this.jdbcOperationMaps.get(mod).update(
                                            CustomizedJdbcOperationsSessionRepository.this.updateSessionQuery,
                                            (ps) -> {
                                                ps.setString(1, session.getId());
                                                ps.setLong(2, session.getLastAccessedTime().toEpochMilli());
                                                ps.setInt(3, (int) session.getMaxInactiveInterval().getSeconds());
                                                ps.setLong(4, session.getExpiryTime().toEpochMilli());
                                                ps.setString(5, session.getPrincipalName());
                                                ps.setString(6, session.primaryKey);
                                            });
                            }
                            List<String> addedAttributeNames = session.delta.entrySet().stream()
                                    .filter((entry) -> entry.getValue() == DeltaValue.ADDED)
                                    .map(Map.Entry::getKey)
                                    .collect(Collectors.toList());
                            if (!addedAttributeNames.isEmpty()) {
                                insertSessionAttributes(session, addedAttributeNames);
                            }
                            List<String> updatedAttributeNames = session.delta.entrySet().stream()
                                    .filter((entry) -> entry.getValue() == DeltaValue.UPDATED)
                                    .map(Map.Entry::getKey)
                                    .collect(Collectors.toList());
                            if (!updatedAttributeNames.isEmpty()) {
                                updateSessionAttributes(session, updatedAttributeNames);
                            }
                            List<String> removedAttributeNames = session.delta.entrySet().stream()
                                    .filter((entry) -> entry.getValue() == DeltaValue.REMOVED)
                                    .map(Map.Entry::getKey)
                                    .collect(Collectors.toList());
                            if (!removedAttributeNames.isEmpty()) {
                                  deleteSessionAttributes(session, removedAttributeNames);
                            }
                        }
            });
        }
        session.clearChangeFlags();
    }
  
  /**
   * @desc Modify by Jimmy Shan, the date is 2019-06-25
   *      Implementing Routing Function
   */
    @Override
    public JdbcSession findById(final String id) {
      String sessionId = id;
      int hashCode = Math.abs(sessionId.hashCode());
      int mod = hashCode % Integer.parseInt(shardingNum);
        final JdbcSession session = this.transOperationMaps.get(mod).execute((status) -> {
            List<JdbcSession> sessions = CustomizedJdbcOperationsSessionRepository
                                 .this.jdbcOperationMaps.get(mod).query(
                                                                     CustomizedJdbcOperationsSessionRepository.this.getSessionQuery,
                    (ps) -> ps.setString(1, id),
                    CustomizedJdbcOperationsSessionRepository.this.extractor
            );
            if (sessions.isEmpty()) {
                    return null;
            }
            return sessions.get(0);
        });
        
        if (session != null) {
            if (session.isExpired()) {
                    deleteById(id);
            }    else {
                    return session;
            }
        }
        return null;
    }
    
    /**
   * @desc Modify by Jimmy Shan, the date is 2019-06-25
   *      Implementing Routing Function
   */
    @Override
    public void deleteById(final String id) {
      String sessionId = id;
      int hashCode = Math.abs(sessionId.hashCode());
      int mod = hashCode % Integer.parseInt(shardingNum);
        this.transOperationMaps.get(mod).execute(new TransactionCallbackWithoutResult() {
                @Override
                protected void doInTransactionWithoutResult(TransactionStatus status) {
                        CustomizedJdbcOperationsSessionRepository.this.jdbcOperationMaps.get(mod).update(
                                CustomizedJdbcOperationsSessionRepository.this.deleteSessionQuery, id);
                }
        });
    }
    
    /**
   * @desc Modify by Jimmy Shan, the date is 2019-06-25
   *      Implementing Routing Function
   */
    @Override
    public Map<String, JdbcSession> findByIndexNameAndIndexValue(String indexName, final String indexValue) {
        if (!PRINCIPAL_NAME_INDEX_NAME.equals(indexName)) {
                return Collections.emptyMap();
        }
        List<JdbcSession> sessions = new ArrayList<>();
        Set<Integer> setKey = transOperationMaps.keySet();
        for (Iterator ir = setKey.iterator(); ir.hasNext(); ) {
                Integer key = (Integer) ir.next();
                TransactionOperations transOperation = (TransactionOperations) transOperationMaps.get(key);
                List<JdbcSession> tempSession = transOperation.execute(status) -> 
                                CustomizedJdbcOperationsSessionRepository.this.jdbcOperationMaps.get(key).query(
                                        CustomizedJdbcOperationsSessionRepository.this.listSessionsByPrincipalNameQuery,
                                        (ps) -> ps.setString(1, indexValue),
                                        CustomizedJdbcOperationsSessionRepository.this.extractor));
                if (tempSession != null && !tempSession.isEmpty()) {
                        sessions.addAll(tempSession);
                }    
        }
        Map<String, JdbcSession> sessionMap = new HashMap<>(sessions.size());
        for (JdbcSession session : sessions) {
                sessionMap.put(session.getId(), session);
        }

        return sessionMap;
    }
    
    /**
   * @desc Modify by Jimmy Shan, the date is 2019-06-25
   *      Implementing Routing Function
   */
    private void insertSessionAttributes(JdbcSession session, List<String> attributeNames) {
        Assert.notEmpty(attributeNames, "attributeNames must not be null or empty");
      String sessionId = session.getId();
      int hashCode = Math.abs(sessionId.hashCode());
      int mod = hashCode % Integer.parseInt(shardingNum);
        if (attributeNames.size() > 1) {
            this.jdbcOperationMaps.get(mod).batchUpdate(this.createSessionAttributeQuery, new BatchPreparedStatementSetter() {
                        @Override
                        public void setValues(PreparedStatement ps, int i) throws SQLException {
                                String attributeName = attributeNames.get(i);
                                ps.setString(1, attributeName);
                                setObjectAsBlob(ps, 2, session.getAttribute(attributeName));
                                ps.setString(3, session.getId());
                        }
                        @Override
                        public int getBatchSize() {
                                return attributeNames.size();
                        }

            });
        }    else {
                this.jdbcOperationMaps.get(mod).update(this.createSessionAttributeQuery, (ps) -> {
                    String attributeName = attributeNames.get(0);
                    ps.setString(1, attributeName);
                    setObjectAsBlob(ps, 2, session.getAttribute(attributeName));
                    ps.setString(3, session.getId());
                });
        }
    }
    
    /**
   * @desc Modify by Jimmy Shan, the date is 2019-06-25
   *      Implementing Routing Function
   */
    private void updateSessionAttributes(JdbcSession session, List<String> attributeNames) {
        Assert.notEmpty(attributeNames, "attributeNames must not be null or empty");
      String sessionId = session.getId();
      int hashCode = Math.abs(sessionId.hashCode());
      int mod = hashCode % Integer.parseInt(shardingNum);
        if (attributeNames.size() > 1) {
                this.jdbcOperationMaps.get(mod).batchUpdate(this.updateSessionAttributeQuery, new BatchPreparedStatementSetter() {
                            @Override
                            public void setValues(PreparedStatement ps, int i) throws SQLException {
                                String attributeName = attributeNames.get(i);
                                setObjectAsBlob(ps, 1, session.getAttribute(attributeName));
                                ps.setString(2, session.primaryKey);
                                ps.setString(3, attributeName);
                            }
                            @Override
                            public int getBatchSize() {
                                return attributeNames.size();
                            }
                });
        }    else {
                this.jdbcOperationMaps.get(mod).update(this.updateSessionAttributeQuery, (ps) -> {
                    String attributeName = attributeNames.get(0);
                    setObjectAsBlob(ps, 1, session.getAttribute(attributeName));
                    ps.setString(2, session.primaryKey);
                    ps.setString(3, attributeName);
                });
        }
    }
    
    /**
   * @desc Modify by Jimmy Shan, the date is 2019-06-25
   *      Implementing Routing Function
   */
    private void deleteSessionAttributes(JdbcSession session, List<String> attributeNames) {
        Assert.notEmpty(attributeNames, "attributeNames must not be null or empty");
      String sessionId = session.getId();
      int hashCode = Math.abs(sessionId.hashCode());
      int mod = hashCode % Integer.parseInt(shardingNum);
        if (attributeNames.size() > 1) {
                this.jdbcOperationMaps.get(mod).batchUpdate(this.deleteSessionAttributeQuery, new BatchPreparedStatementSetter() {
                            @Override
                            public void setValues(PreparedStatement ps, int i) throws SQLException {
                                String attributeName = attributeNames.get(i);
                                ps.setString(1, session.primaryKey);
                                ps.setString(2, attributeName);
                            }
                            @Override
                            public int getBatchSize() {
                                return attributeNames.size();
                            }
                });
        } else {
                this.jdbcOperationMaps.get(mod).update(this.deleteSessionAttributeQuery, (ps) -> {
                    String attributeName = attributeNames.get(0);
                    ps.setString(1, session.primaryKey);
                    ps.setString(2, attributeName);
                });
        }
    }
    
    /**
   * @desc Modify by Jimmy Shan, the date is 2019-06-25
   *      Implementing Routing Function
   */
    public void cleanUpExpiredSessions() {
        Set<Integer> setKey = transOperationMaps.keySet();
        for (Iterator ir = setKey.iterator(); ir.hasNext(); ) {
                Integer key = (Integer) ir.next();
                TransactionOperations transOperation = (TransactionOperations) transOperationMaps.get(key);
                Integer deletedCount = transOperation.execute((status) ->
                        CustomizedJdbcOperationsSessionRepository.this.jdbcOperationMaps.get(key).update(
                                CustomizedJdbcOperationsSessionRepository.this.deleteSessionsByExpiryTimeQuery,
                                        System.currentTimeMillis()));
                if (logger.isDebugEnabled()) {
                        logger.debug("Cleaned up " + deletedCount + " expired sessions");
                }
        }
    }

    private static TransactionTemplate createTransactionTemplate(
            PlatformTransactionManager transactionManager) {
            TransactionTemplate transactionTemplate = new TransactionTemplate(
                    transactionManager);
            transactionTemplate.setPropagationBehavior(
                    TransactionDefinition.PROPAGATION_REQUIRES_NEW);
            transactionTemplate.afterPropertiesSet();
            return transactionTemplate;
    }

    private static GenericConversionService createDefaultConversionService() {
            GenericConversionService converter = new GenericConversionService();
            converter.addConverter(Object.class, byte[].class,
                    new SerializingConverter());
            converter.addConverter(byte[].class, Object.class,
                    new DeserializingConverter());
            return converter;
    }

    private String getQuery(String base) {
            return StringUtils.replace(base, "%TABLE_NAME%", this.tableName);
    }

    private void prepareQueries() {
            this.createSessionQuery = getQuery(CREATE_SESSION_QUERY);
            this.createSessionAttributeQuery = getQuery(CREATE_SESSION_ATTRIBUTE_QUERY);
            this.getSessionQuery = getQuery(GET_SESSION_QUERY);
            this.updateSessionQuery = getQuery(UPDATE_SESSION_QUERY);
            this.updateSessionAttributeQuery = getQuery(UPDATE_SESSION_ATTRIBUTE_QUERY);
            this.deleteSessionAttributeQuery = getQuery(DELETE_SESSION_ATTRIBUTE_QUERY);
            this.deleteSessionQuery = getQuery(DELETE_SESSION_QUERY);
            this.listSessionsByPrincipalNameQuery =
                    getQuery(LIST_SESSIONS_BY_PRINCIPAL_NAME_QUERY);
            this.deleteSessionsByExpiryTimeQuery =
                    getQuery(DELETE_SESSIONS_BY_EXPIRY_TIME_QUERY);
    }

    private void setObjectAsBlob(PreparedStatement ps, int paramIndex, Object object)
            throws SQLException {
            byte[] bytes = (byte[]) this.conversionService.convert(object,
                    TypeDescriptor.valueOf(Object.class),
                    TypeDescriptor.valueOf(byte[].class));
            this.lobHandler.getLobCreator().setBlobAsBytes(ps, paramIndex, bytes);
    }

    private Object getBlobAsObject(ResultSet rs, String columnName) throws SQLException {
            byte[] bytes = this.lobHandler.getBlobAsBytes(rs, columnName);
            return this.conversionService.convert(bytes, TypeDescriptor.valueOf(byte[].class),
                    TypeDescriptor.valueOf(Object.class));
    }

    private enum DeltaValue {
            ADDED, UPDATED, REMOVED
    }

    private static <T> Supplier<T> value(T value) {
            return (value != null) ? () -> value : null;
    }

    private static <T> Supplier<T> lazily(Supplier<T> supplier) {
            Supplier<T> lazySupplier = new Supplier<T>() {
                private T value;
                @Override
                public T get() {
                    if (this.value == null) {
                        this.value = supplier.get();
                    }
                    return this.value;
                }
            };
            
            return (supplier != null) ? lazySupplier : null;
    }
    
    
    final class JdbcSession implements Session {

        private final Session delegate;

        private final String primaryKey;

        private boolean isNew;

        private boolean changed;

        private Map<String, DeltaValue> delta = new HashMap<>();

        JdbcSession() {
            this.delegate = new MapSession();
            this.isNew = true;
            this.primaryKey = UUID.randomUUID().toString();
        }

        JdbcSession(String primaryKey, Session delegate) {
            Assert.notNull(primaryKey, "primaryKey cannot be null");
            Assert.notNull(delegate, "Session cannot be null");
            this.primaryKey = primaryKey;
            this.delegate = delegate;
        }

        boolean isNew() {
            return this.isNew;
        }

        boolean isChanged() {
            return this.changed;
        }

        Map<String, DeltaValue> getDelta() {
            return this.delta;
        }

        void clearChangeFlags() {
            this.isNew = false;
            this.changed = false;
            this.delta.clear();
        }

        String getPrincipalName() {
            return PRINCIPAL_NAME_RESOLVER.resolvePrincipal(this);
        }

        Instant getExpiryTime() {
            return getLastAccessedTime().plus(getMaxInactiveInterval());
        }

        @Override
        public String getId() {
            return this.delegate.getId();
        }

        @Override
        public String changeSessionId() {
            this.changed = true;
            return this.delegate.changeSessionId();
        }

        @Override
        public <T> T getAttribute(String attributeName) {
            Supplier<T> supplier = this.delegate.getAttribute(attributeName);
            return (supplier != null) ? supplier.get() : null;
        }

        @Override
        public Set<String> getAttributeNames() {
            return this.delegate.getAttributeNames();
        }

        @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,
                        (oldDeltaValue, deltaValue) -> (oldDeltaValue == DeltaValue.ADDED)
                                ? oldDeltaValue
                                : DeltaValue.UPDATED);
            }
            this.delegate.setAttribute(attributeName, value(attributeValue));
            if (PRINCIPAL_NAME_INDEX_NAME.equals(attributeName) ||
                    SPRING_SECURITY_CONTEXT.equals(attributeName)) {
                this.changed = true;
            }
        }

        @Override
        public void removeAttribute(String attributeName) {
            setAttribute(attributeName, null);
        }

        @Override
        public Instant getCreationTime() {
            return this.delegate.getCreationTime();
        }

        @Override
        public void setLastAccessedTime(Instant lastAccessedTime) {
            this.delegate.setLastAccessedTime(lastAccessedTime);
            this.changed = true;
        }

        @Override
        public Instant getLastAccessedTime() {
            return this.delegate.getLastAccessedTime();
        }

        @Override
        public void setMaxInactiveInterval(Duration interval) {
            this.delegate.setMaxInactiveInterval(interval);
            this.changed = true;
        }

        @Override
        public Duration getMaxInactiveInterval() {
            return this.delegate.getMaxInactiveInterval();
        }

        @Override
        public boolean isExpired() {
            return this.delegate.isExpired();
        }

    }

    static class PrincipalNameResolver {

        private SpelExpressionParser parser = new SpelExpressionParser();

        public String resolvePrincipal(Session session) {
            String principalName = session.getAttribute(PRINCIPAL_NAME_INDEX_NAME);
            if (principalName != null) {
                return principalName;
            }
            Object authentication = session.getAttribute(SPRING_SECURITY_CONTEXT);
            if (authentication != null) {
                Expression expression = this.parser
                        .parseExpression("authentication?.name");
                return expression.getValue(authentication, String.class);
            }
            return null;
        }

    }

    private class SessionResultSetExtractor implements ResultSetExtractor<List<JdbcSession>> {

        @Override
        public List<JdbcSession> extractData(ResultSet rs) throws SQLException, DataAccessException {
            List<JdbcSession> sessions = new ArrayList<>();
            while (rs.next()) {
                String id = rs.getString("SESSION_ID");
                JdbcSession session;
                if (sessions.size() > 0 && getLast(sessions).getId().equals(id)) {
                    session = getLast(sessions);
                }
                else {
                    MapSession delegate = new MapSession(id);
                    String primaryKey = rs.getString("PRIMARY_ID");
                    delegate.setCreationTime(Instant.ofEpochMilli(rs.getLong("CREATION_TIME")));
                    delegate.setLastAccessedTime(Instant.ofEpochMilli(rs.getLong("LAST_ACCESS_TIME")));
                    delegate.setMaxInactiveInterval(Duration.ofSeconds(rs.getInt("MAX_INACTIVE_INTERVAL")));
                    session = new JdbcSession(primaryKey, delegate);
                }
                String attributeName = rs.getString("ATTRIBUTE_NAME");
                if (attributeName != null) {
                    Object attributeValue = getBlobAsObject(rs, "ATTRIBUTE_BYTES");
                    session.delegate.setAttribute(attributeName, lazily(() -> attributeValue));
                }
                sessions.add(session);
            }
            return sessions;
        }

        private JdbcSession getLast(List<JdbcSession> sessions) {
            return sessions.get(sessions.size() - 1);
        }

    }

}

 

以上工作都完成后,让我们来看看如何使用。

代码如下:

package com.szl.demo.spring.session.controller;

import java.io.PrintWriter;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class TestSessionController {
        
        @RequestMapping("/testGetSession")
        public void testGetSession(HttpServletRequest request, HttpServletResponse response, ModelMap model) {
                PrintWriter out = null;
                try {
                        out = response.getWriter();
                        
                        HttpSession session = request.getSession();
                        String content = (String) session.getAttribute("userId");
                        System.out.println("session content is : " + content);
                        
                        out.println("session content is : " + content);
                        out.flush();
                        out.close();
                } catch (Exception e) {
                        e.printStackTrace();
                }
        }
        
        @RequestMapping("/testSetSession")
        public void testSetSession(HttpServletRequest request, HttpServletResponse response, ModelMap model) {
                PrintWriter out = null;
                try {
                        out = response.getWriter();
                        UUID uuid = UUID.randomUUID();
                        String uid = uuid.toString().replaceAll("-", "");
                        
                        HttpSession session = request.getSession();
                        String content = (String) session.getAttribute("userId");
                        System.out.println("old session content is : " + content);
                        
                        
                        // 设置session内容
                        session.setAttribute("userId", uid);
                        out.println("new session content is : " + uid);
                        out.flush();
                        out.close();
                } catch (Exception e) {
                        e.printStackTrace();
                }
        }

}

 

是不是很简单,和平时使用session方式一样,好了,备忘录记录到此,还是那句,
仅供有需要的朋友参考,也欢迎转载,但请注明原著,谢谢。

标签:分库,springboot2.1,spring,private,session,springframework,org,import,String
来源: https://www.cnblogs.com/jimmyshan-study/p/11087223.html