动态添加数据源,根据用户登录切换数据库.编程式Spring事务.

摘要:
resolvedDataSources.setAccessible(true);characterEncoding=UTF-8&zeroDateTimeBehavior=转换为空&useSSL=true“);}catch(IllegalAccessExceptione){e.printStackTrace();

根据用户注册,系统自动创建私有数据库,用户登录,动态添加数据源到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事务.》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇haproxy实现会话保持(2):stick tableyml配置从nacos配置中心取数据(单个或多个),读读源码,寻找如何配置多个下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

【Oracle】Oracle安装配置、创建数据库实例及用户和连接

https://blog.csdn.net/wudiyong22/article/details/78904361 参考资料:https://www.cnblogs.com/hoobey/p/6010804.html 一、Oracle下载 注意Oracle分成两个文件,下载完后,将两个文件解压到同一目录下即可。 路径名称中,最好不要出现中文,也不要出现空格...

Python爬取中文页面的时候出现的乱码问题

一、读取返回的页面数据 在浏览器打开的时候查看源代码,如果在头部信息中指定了UTF-8 那么再python代码中读取页面信息的时候,就需要指定读取的编码方式: response.read().decode('utf-8') 二、把中文数据写入到文件的时候 python默认的是按照ACSII的编码往外写,所以中文数据往外写肯定会出现乱码 那么再往外写入文件的...

MongoDB副本集提高读写速率

一、提高副本集写的速率 1、通过设置Write Concern for Replica Sets¶ cfg = rs.conf()##cfg.settings.getLastErrorDefaults = { w: "majority", wtimeout: 5000 } cfg.settings.getLastErrorDefaults ={w:1}r...

html5实现web app摇一摇换歌

微信可以摇歌曲,根据声音识别出歌曲,然后返回歌曲信息,利用html5的deviceOrientation特性和deviceMotion事件也可以在web app上实现类似于微信摇一摇的功能,原生的app实现也有相关接口,这里只考虑web app的情况...... Section One 先来看下demo效果图: 测试地址:http://hcy2367...

网络知识收集

最近学习Ravello虚拟化,遇到一些和网络相关的知识,收集后整理如下: L2其实是指七层网络协议中的第二层数据链路层,它的传输是以mac地址为基础L3指网络层:是以ip地址为基础网络层属于OSI中的较高层次了,从它的名字可以看出,它解决的是网络与网络之间,即网际的通信问题,而不是同一网段内部的事。网络层的主要功能即是提供路由,即选择到达目标主机的最佳...

PivotGridControl控件应用

一、概述        PivotGridControl是DevExpress组件中的一个重要控件,在数据多维分析方面具有强大的功能,它不仅可以分析数据库中的数据,而且还能够做联机分析处理(OLAP),并且支持多种数据仓库。前面的文章中介绍了如何用PivotGridControl制作交叉表部件,在智能窗体上展示交叉报表。本文将继续对PivotGridCon...