根据用户注册,系统自动创建私有数据库,用户登录,动态添加数据源到Spring数据路由,Session超时删除数据源

好处:当数据量大的时候,类似水平切割效果,效率会高一些

坏处:数据源切换,Spring 事务处理比较繁琐,数据连接处理不好会有很大消耗,如果涉及后台系统管理数据,也比较繁琐.

使用Spring数据源路由,现在好像没有直接添加数据源的方法,无奈之下只能用反射.

用户登录成功时,在Spring Security UserDetailService.loadUserByUsername 里面添加用户数据源

        /**
* 加入用户数据源
*/
routingDataSource.addDataSource(userid);
    /**
* 根据用户创建数据源
*/
public void addDataSource(String userid) {
if (StringUtils.isBlank(userid))
return;
DbInfo dbInfo = getDbInfoService().getDbInfoByUserId(userid);
try {
Field targetDataSources = AbstractRoutingDataSource.class.getDeclaredField("targetDataSources");
Field resolvedDataSources = AbstractRoutingDataSource.class.getDeclaredField("resolvedDataSources");
targetDataSources.setAccessible(true);
resolvedDataSources.setAccessible(true);
Map<Object, Object> dataSources = (Map<Object, Object>) targetDataSources.get(this);
if (dataSources.get(userInfo.getId().toString()) != null)
return;
Map<Object, DataSource> dataSources2 = (Map<Object, DataSource>) resolvedDataSources.get(this);
DruidDataSource dds = new DruidDataSource();
dds.setUrl("jdbc:mysql://" + dbInfo.getDbaddr() +
":" + dbInfo.getDbport() + "/" + dbInfo.getDbname() + "?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&useSSL=true");
dds.setUsername(dbInfo.getUsername());
dds.setPassword(dbInfo.getPwd());
dataSources.put(userid, dds);
dataSources2.put(userid, dds);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}

加入了数据源,当然需要删除,可以在Session监听器里面,销毁Session的时候删除

    /**
* 根据用户删除数据源
*/
public void removeDataSource(String userid) {
if (StringUtils.isBlank(userid))
return;
try {
Field targetDataSources = AbstractRoutingDataSource.class.getDeclaredField("targetDataSources");
Field resolvedDataSources = AbstractRoutingDataSource.class.getDeclaredField("resolvedDataSources");
targetDataSources.setAccessible(true);
resolvedDataSources.setAccessible(true);
Map<Object, Object> dataSources = (Map<Object, Object>) targetDataSources.get(this);
if (dataSources.get(userInfo.getUsrno()) != null) {
Map<Object, DataSource> dataSources2 = (Map<Object, DataSource>) resolvedDataSources.get(this);
dataSources.remove(userid);
dataSources2.remove(userid);
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}

注解加Aop 切换数据源

注解

/**
* Created by 为 .
* 根据当前用户切换数据源
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SwitchDataSource {
}

Spring AOP,新版本的SpringAOP 可以很好切入监听器,因为监听器可以被Spring容器管理了,变相加强了SpringAop,这样就不需要使用原生Aspectj了

/**
* Created by 为 on 2017-4-27.
*/
@Component
@Aspect
@Order(0)//配置Spring注解事务时,在事务之前切换数据源
public class SwitchDataSourceAspectj { //定义切点
@Pointcut("@annotation(com.lzw.common.annotation.SwitchDataSource)")
public void switchDataSource(){} @Around("switchDataSource()")
public Object arounduserDataSource(ProceedingJoinPoint joinPoint){
DataSourceContextHolder.user();
try {
return joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}finally {
DataSourceContextHolder.write();
}
return null;
}
}

这样可以在方法上添加注解切换数据源(注意事务与切换数据源的注解顺序),不过如果在一个方法中需要多次切换到不同数据源查询数据,会消耗很多连接数,为了更好控制数据库连接数,需要使用Spring事务

编程式Spring事务

注入TransactionManager

    @Resource
private PlatformTransactionManager platformTransactionManager;

开始事务处理,每个用户单独数据库,访问量不大,所以没有配置连接池,每次重新获取连接性能比较低,开启事务是为了数据库连接重用

     //为了节省连接数,尽可能在一次切换里获取需要的数据
DataSourceContextHolder.user();
//TransactionTemplate 必须每次new出来,不能使用Spring单例注入,设置的数据会一直存在.
TransactionTemplate transactionTemplate = new TransactionTemplate(platformTransactionManager);
transactionTemplate.setPropagationBehavior(Propagation.REQUIRES_NEW.value());
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
public void doInTransactionWithoutResult(TransactionStatus status) {
    //数据库操作代码
}
});
DataSourceContextHolder.write();
  

动态添加数据源,根据用户登录切换数据库.编程式Spring事务.的更多相关文章

  1. SpringBoot运行时动态添加数据源

    此方案适用于解决springboot项目运行时动态添加数据源,非静态切换多数据源!!! 一.多数据源应用场景: 1.配置文件配置多数据源,如默认数据源:master,数据源1:salve1...,运行 ...

  2. springboot添加多数据源 以及 动态添加数据源动态切换数据源

    <!-- Druid 数据连接池依赖 --> <dependency> <groupId>com.alibaba</groupId> <artif ...

  3. ubuntu使用root用户登录/切换root权限

    ubuntu系统默认root用户是不能登录的,密码也是空的. 如果要使用root用户登录,必须先为root用户设置密码 打开终端,输入:sudo passwd root 然后按回车 此时会提示你输入密 ...

  4. asp.net 动态添加多个用户控件

    动态添加多个相同用户控件,并使每个用户控件获取不同的内容. 用户控件代码: 代码WebControls using System; using System.Collections.Generic;  ...

  5. Django学习路13_创建用户登录,判断数据库中账号名密码是否正确

    在 models.py 中设置数据库表的信息 from django.db import models # Create your models here. class User(models.Mod ...

  6. springboot动态多数据源

    参考文章:https://www.cnblogs.com/hehehaha/p/6147096.html 前言 目标是springboot工程支持多个MySQL数据源,在代码层面上,同一个SQL(Ma ...

  7. 【SSH学习笔记】用Struts2实现简单的用户登录

    准备阶段 在使用学习Struts2的时候首先要下载相应的架包 Struts2资源下载 这里建议下载第一个,在struts-2.5.14.1-all.zip里有很多实用的东西,不仅有架包还有官方为开发者 ...

  8. mysql 切换数据库方案

    业务场景 在SAAS模式下,不同的租户需要切换数据库,我们可以使用动态数据源,动态数据源有个问题,就是需要对每一个数据库创建一个连接池,在初始化的时候初始化这些连接池, 如果多台应用服务器的情况,每一 ...

  9. 用redis和cookie做单用户登录

    因为公司的项目需要用到单用户登录,于是今天用redis和cookie给系统添加了单用户登录功能,再次简单记录一下. 单用户登录是为了防止同一账户在不同电脑和不同浏览器里面同时登录.所以我这边的思路是: ...

随机推荐

  1. Linux普通用户修改owner非本人文件为什么成功

    关键字:错误提示:E45 已设定选项’readonly’(请加!强制执行) Linux对文件和目录有很好的权限管理,但今天遇到一个比较诡异的事情,普通用户对文件权限不可写,但可以强制保存该文件,这样就 ...

  2. Django环境安装--Django从入门到精通系列教程

    该系列教程系个人原创,并完整发布在个人官网刘江的博客和教程 所有转载本文者,需在顶部显著位置注明原作者及www.liujiangblog.com官网地址. Python及Django学习QQ群:453 ...

  3. 邪恶的PLS

    今天碰到一个存储过程编译错误,提示PLS-00103错误,关于这个错误网上能搜到一大把,原因很多,我碰到的错误提示如下: Compilation errors for PROCEDURE ETL.PR ...

  4. 小白成长系列--HTTP协议(一)

    序:小白成长系列是笔者使用最简单易懂的逻辑来解释常见的计算机相关知识,不仅理解,还让你记忆深刻\(^o^)/ 先理解什么是协议? 协议就是双方要做某件事情而制定的规则,而且双方必须要遵从协议所约定的内 ...

  5. 【转】Linux Oracle服务启动&停止脚本与开机自启动

    在CentOS 6.3下安装完Oracle 10g R2,重开机之后,你会发现Oracle没有自行启动,这是正常的,因为在Linux下安装Oracle的确不会自行启动,必须要自行设置相关参数,首先先介 ...

  6. 基于 xorm 的服务端框架 XGoServer

    作者:林冠宏 / 指尖下的幽灵 掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8 博客:http://www.cnblogs.com/linguan ...

  7. [codility] Lession1 - Iterations - BinaryGap

    Task1: A binary gap within a positive integer N is any maximal sequence of consecutive zeros that is ...

  8. 对List中每个对象元素按时间顺序排序

    需求: 需要对List中的每个User按照birthday顺序排序,时间由小到大排列. 代码实现: import java.text.SimpleDateFormat; import java.uti ...

  9. Spring学习一

    1.POJO和JavaBean的区别 POJO 和JavaBean是我们常见的两个关键字,一般容易混淆,POJO全称是Plain Ordinary Java Object / Pure Old Jav ...

  10. new function

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...