首页 > 代码库 > 动态添加数据源,根据用户登录切换数据库.编程式Spring事务.
动态添加数据源,根据用户登录切换数据库.编程式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事务.
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。