动态添加数据源,根据用户登录切换数据库.编程式Spring事务.
根据用户注册,系统自动创建私有数据库,用户登录,动态添加数据源到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事务.的更多相关文章
- SpringBoot运行时动态添加数据源
此方案适用于解决springboot项目运行时动态添加数据源,非静态切换多数据源!!! 一.多数据源应用场景: 1.配置文件配置多数据源,如默认数据源:master,数据源1:salve1...,运行 ...
- springboot添加多数据源 以及 动态添加数据源动态切换数据源
<!-- Druid 数据连接池依赖 --> <dependency> <groupId>com.alibaba</groupId> <artif ...
- ubuntu使用root用户登录/切换root权限
ubuntu系统默认root用户是不能登录的,密码也是空的. 如果要使用root用户登录,必须先为root用户设置密码 打开终端,输入:sudo passwd root 然后按回车 此时会提示你输入密 ...
- asp.net 动态添加多个用户控件
动态添加多个相同用户控件,并使每个用户控件获取不同的内容. 用户控件代码: 代码WebControls using System; using System.Collections.Generic; ...
- Django学习路13_创建用户登录,判断数据库中账号名密码是否正确
在 models.py 中设置数据库表的信息 from django.db import models # Create your models here. class User(models.Mod ...
- springboot动态多数据源
参考文章:https://www.cnblogs.com/hehehaha/p/6147096.html 前言 目标是springboot工程支持多个MySQL数据源,在代码层面上,同一个SQL(Ma ...
- 【SSH学习笔记】用Struts2实现简单的用户登录
准备阶段 在使用学习Struts2的时候首先要下载相应的架包 Struts2资源下载 这里建议下载第一个,在struts-2.5.14.1-all.zip里有很多实用的东西,不仅有架包还有官方为开发者 ...
- mysql 切换数据库方案
业务场景 在SAAS模式下,不同的租户需要切换数据库,我们可以使用动态数据源,动态数据源有个问题,就是需要对每一个数据库创建一个连接池,在初始化的时候初始化这些连接池, 如果多台应用服务器的情况,每一 ...
- 用redis和cookie做单用户登录
因为公司的项目需要用到单用户登录,于是今天用redis和cookie给系统添加了单用户登录功能,再次简单记录一下. 单用户登录是为了防止同一账户在不同电脑和不同浏览器里面同时登录.所以我这边的思路是: ...
随机推荐
- Maven初步
Maven初入 maven 是一个项目管理工具, 它包含了一个 项目对象模型(Project Object Model POM), 一组标准集合, 一个项目生命周期(Project Lifecycle ...
- libev学习笔记
转 libev的使用--结合Socket编程 作者:cxy450019566 之前自己学过一些libev编程的基础,这次写压测刚好用上了,才算真正动手写了些东西,在这里做一些总结.写这篇文章是为了用浅 ...
- Linux中的shell到底是什么
(引自:https://zhidao.baidu.com/question/557066905.html) [一] shell的含义: 首先shell的英文含义是"壳": 它是相对 ...
- awk脚本使用的几种方法
1. awk名包含在文件内 [root@nhserver1 08]# cat sample.txtaaabbbccc [root@nhserver1 08]# cat readsample.awkaw ...
- @Controller注解
Spring从2.5版本后开始引入注解,用户可以使用@Controller,@RequestMapping,@RequestParam,@ModelAttribute等类似这样的注解. @Contro ...
- 从返回的HTTP Header信息中隐藏Apache的版本号及PHP的X-Powered-By信息
默认情况下,很多apache安装时会显示版本号及操作系统版本,甚至会显示服务器上安装的是什么样的apache模块.这些信息可以为黑客所用,并且黑客还可以从中得知你所配置的服务器上的很多设置都是默认状态 ...
- mongodb备份恢复,数据导入导出
数据导出 mongoexport 假设库里有一张apachelog表,里面有2 条记录,我们要将它导出 /test/mongodb/bin/mongo use wxdata switched to d ...
- Spring单元测试
1.基于AbstractDependencyInjectionSpringContextTests Spring的单元测试可以使用AbstractDependencyInjectionSpringCo ...
- 洛谷 [P1265] 公路修建
本题的描述:城市联盟,最短距离.. 使人想到了prim求MST,再一看数据范围:完全图!,那么一定得用prim,因为只有5000个点,所以不加优化的prim就能过. #include <iost ...
- CF 570D. Tree Requests [dsu on tree]
传送门 题意: 一棵树,询问某棵子树指定深度的点能否构成回文 当然不用dsu on tree也可以做 dsu on tree的话,维护当前每一个深度每种字母出现次数和字母数,我直接用了二进制.... ...