springboot数据源切换
来源:互联网 发布:商标域名价格 编辑:程序博客网 时间:2024/05/20 07:58
一朋友应聘到新公司,结果不会写数据源切换,只好帮他写写。
先说业务需求,和一般的数据源切换不同,这里的需求的数据源配置信息不是先配置好的,而是存在数据库之中,不同的用户登录之后,先查询到的自己的数据源配置信息,然后生成数据源,再进行数据源切换。不忍吐槽,这个架构就出了很大的纰漏,但是毕竟是需求,也就不多说什么了。
那么根据需求来分析,最主要的就是用户数据源获取和获取完之后的再生成一个数据源可供加载。
所以先设计一个关于用户数据源信息的类
public class UserDataConfig implements Serializable { private String key; private String driven; private String username; private String password; private String url; public UserDataConfig() {} public UserDataConfig(String key, String driven, String username, String password, String url) { this.key = key; this.driven = driven; this.username = username; this.password = password; this.url = url; } get/set.... }
然后抽象实现:
public abstract class ToggleUserDataSource extends AbstractDataSource implements InitializingBean { private static Logger logger = LoggerFactory.getLogger(ToggleUserDataSource.class); private DataSource defaultDataSource; //默认的数据源注入 ref private Map<String,DataSource> userDataSource = new ConcurrentHashMap<String, DataSource>(); private ThreadLocal<DataSource> exportDataSource = new ThreadLocal<DataSource>(); //将要输出的DataSource 架构有问题 当用量上来的时候可能发生OutOfMemory private Class<? extends DataSource> poolType; //将要生成的数据库连接池 private Lock lock = new ReentrantLock(); /** * 对外切换数据源接口 * @param userKey 用户标识 * @return 是否切换成功 */ public boolean toggleDataSource(String userKey){ DataSource dataSource = userDataSource.get(userKey); if (dataSource == null){ lock.lock(); try { if(dataSource == null){ dataSource = buildDataSource(userKey); } }finally { lock.unlock(); } if(dataSource == null){ return false; } userDataSource.put(userKey,dataSource); } exportDataSource.set(dataSource); return true; } private DataSource buildDataSource(String userKey){ UserDataConfig userDataConfig = getUserDataConfig(userKey,defaultDataSource); DataSource source = exportDataSourcePoolBuild(userDataConfig); /*DataSource result = DataSourceBuilder.create(CustomUserDataSource.class.getClassLoader()) .driverClassName(userDataConfig.getDriven()) .url(userDataConfig.getUrl()).username(userDataConfig.getUsername()) .password(userDataConfig.getPassword()).build();*/ return source; } @Override public Connection getConnection() throws SQLException { checkExportDtaSource(); return exportDataSource.get().getConnection(); } @Override public void afterPropertiesSet() throws Exception { } @Override public Connection getConnection(String username, String password) throws SQLException { checkExportDtaSource(); return exportDataSource.get().getConnection(username,password); } protected DataSource getDefaultDataSource() { return defaultDataSource; } /** * 暴露的默认数据源注入 * @param defaultDataSource */ public void setDefaultDataSource(DataSource defaultDataSource) { this.defaultDataSource = defaultDataSource; } /** * 当用户查询的config配置返回null 则使用默认的数据库连接池 */ private void checkExportDtaSource(){ if(this.exportDataSource.get() == null){ this.exportDataSource.set(defaultDataSource); } } public void updateCache(String userKey){ DataSource dataSource = null; this.lock.lock(); try { dataSource = buildDataSource(userKey); }finally { this.lock.unlock(); } userDataSource.put(userKey,dataSource); } /** * 用户实现 * @param userKey * @param defaultDataSource * @return */ protected abstract UserDataConfig getUserDataConfig(String userKey,DataSource defaultDataSource); protected abstract DataSource exportDataSourcePoolBuild(UserDataConfig userDataConfig);}
提供一个默认数据源是用来替换DataSource的注入,近似装饰者模式。另外使用模板设计,这里预留了两个抽象方法UserDataConfig和exportDataSourcePoolBuild,顾名思义,UserDataConfig就是通过默认的数据源和用户key来得到用户的数据源信息,而exportDataSourcePoolBuild则是通过生成好的UserDataConfig对象来生成配置生成新的DataSource,这里我是使用了druid。顺带一说,这个子类应该要保持应用中唯一,所以用了单例模式。
public class UserDataSource extends ToggleUserDataSource { private static UserDataSource userDataSource; @Override protected UserDataConfig getUserDataConfig(String userKey, DataSource defaultDataSource) { UserDataConfig config = new UserDataConfig(); config.setDriven("com.mysql.jdbc.Driver"); config.setUrl("jdbc:mysql://localhost:3306/vue?useUnicode=true&characterEncoding=utf8"); config.setUsername("root"); config.setPassword("123456"); return config; } @Override protected DataSource exportDataSourcePoolBuild(UserDataConfig userDataConfig) { //使用DruidDataSource生成数据库连接池 DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.setUrl(userDataConfig.getUrl()); druidDataSource.setUsername(userDataConfig.getUsername()); druidDataSource.setPassword(userDataConfig.getPassword()); druidDataSource.setDriverClassName(userDataConfig.getDriven()); druidDataSource.setMaxActive(3); return druidDataSource; } private UserDataSource(){} public static UserDataSource getSingleton() { if (null == userDataSource) { synchronized (UserDataSource.class) { if (null == userDataSource) { userDataSource = new UserDataSource(); } } } return userDataSource; }}然后再配置DataSource的bean@Configuration@EnableConfigurationProperties(DataSourceProperties.class)public class DataSourceConfig { @Autowired private DataSourceProperties dataSourceProperties; @Bean @Primary public DataSource dataSource(){ DruidDataSource defaultDataSource = new DruidDataSource(); defaultDataSource.setDriverClassName(dataSourceProperties.getDriverClassName()); defaultDataSource.setUsername(dataSourceProperties.getUsername()); defaultDataSource.setPassword(dataSourceProperties.getPassword()); defaultDataSource.setUrl(dataSourceProperties.getUrl()); UserDataSource userDataSource = UserDataSource.getSingleton(); userDataSource.setDefaultDataSource(defaultDataSource); //source.toggleDataSource("s"); return userDataSource; }}
当需要切换的时候,调用toggleDataSource()。这里其实可以配合上aop去给需要切换数据源的方法加注解,比较容易,这里就不实现了。
完整资源就扔这了http://download.csdn.net/download/qq_15125845/10048641
阅读全文
1 0
- springboot数据源切换
- SpringBoot 自定义+动态切换数据源
- SpringBoot学习笔记之动态数据源切换
- Springboot多数据源配置--数据源动态切换
- SpringBoot配置数据源
- springboot动态数据源实现
- SpringBoot 默认数据源
- 数据源切换异常 导致数据源切换失败
- 数据源--Springboot配置使用Druid数据源
- springboot+mybatis手动配置数据源
- Springboot+mybaitsPlus动态数据源配置
- Spring动态切换数据源
- 动态切换数据源
- AbstractRoutingDataSource实现数据源切换
- 动态数据源切换
- spring 动态数据源切换
- AbstractRoutingDataSource动态切换数据源
- hibernate 动态切换数据源
- elasticsearch入门,linux上简单操作
- mysql 备份还原
- hdu 6158 The Designer && 计蒜客 Finding the Radius for an Inserted Circle 笛卡尔定理应用
- Qt:音乐播放器
- route-policy与filter-policy的区别
- springboot数据源切换
- 布料模拟碰撞检测的实施总结
- Android Studio 中 Gradle 依赖的统一管理
- Docker安装初体验(Windows)
- 简单递推
- Java中的内存划分
- vue学习第14天,混合mixin
- spidev
- 网络连接判断