SAAS系统的数据库设计,动态数据源动态切换

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jinsedeme0881/article/details/79171151

概念

数据源:指的是一个具体的数据库连接。例如 localhost:3306  或者 localhost:3306/sysdb

具体的数据库:当前打开的数据源里的数据库对象的集合,一般称之为database或者schema

这里的SAAS系统考虑数据库的扩展性,使用了多数据源

考虑到一个数据源可以容纳多个租户的数据库,因此1个数据源是要容纳30到100个租户数据库的。


具体是参考  https://github.com/helloworlde/SpringBoot-DynamicDataSource/tree/aspect_dao

上边这个git工程可以解决动态数据源的创建,动态切换的问题。

动态创建数据源的方法

private DataSource getDataSource(String name) {
		DataSourceBuilder builder = DataSourceBuilder.create().driverClassName("com.mysql.jdbc.Driver");
		if ("master".equals(name)) {
			builder.url("jdbc:mysql://localhost:3306/product_master?useSSL=false");
		} else {
			builder.url("jdbc:mysql://localhost:3306/product_slave?useSSL=false");	
		}
		builder.username("root");
		builder.password("root");
		
		return builder.build();
	}
动态切换数据源依赖于DynamicRoutingDataSource

@Bean("dynamicDataSource")
    public DataSource dynamicDataSource() {
        DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();
        Map<Object, Object> dataSourceMap = new HashMap<>(2);
        /*dataSourceMap.put("master", master());
        dataSourceMap.put("slave", slave());*/
        DataSource master = getDataSource("master");
        dataSourceMap.put("master", master);
        dataSourceMap.put("slave", getDataSource("slave"));
        // Set master datasource as default
        dynamicRoutingDataSource.setDefaultTargetDataSource(master);
        // Set master and slave datasource as target datasource
        // 可动态路由的数据源里装载了所有可以被路由的数据源
        dynamicRoutingDataSource.setTargetDataSources(dataSourceMap);

        // To put datasource keys into DataSourceContextHolder to judge if the datasource is exist
        DynamicDataSourceContextHolder.dataSourceKeys.addAll(dataSourceMap.keySet());
        return dynamicRoutingDataSource;
    }
以及在ThreadLocal里保存当前需要的数据源名字,然后在合适的切面(service )方法执行前进行切换即可

切换的方法

public class DynamicRoutingDataSource extends AbstractRoutingDataSource {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * Set dynamic DataSource to Application Context 设置当前应用上下文所使用的数据源
     * @return
     */
    @Override
    protected Object determineCurrentLookupKey() {
        logger.info("Current DataSource is [{}]", DynamicDataSourceContextHolder.getDataSourceKey());
        return DynamicDataSourceContextHolder.getDataSourceKey();
    }
}


还需要一个动态打开具体的数据库(schema)的方法。在dao方法里的sql实际执行前进行拦截

mybatis拦截器

@Intercepts(@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}))
public class MyBatisInterceptor implements Interceptor {

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		Connection o = (Connection)invocation.getArgs()[0];
		o.createStatement().executeQuery("use sys_0001");//schema 隔离 threalocal sys_0001
		System.out.println(o.getClass());
		Object result = invocation.proceed();
		System.out.println("Invocation.proceed()");
		return result;
	}

	@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	@Override
	public void setProperties(Properties properties) {
		// TODO Auto-generated method stub
	}
}




猜你喜欢

转载自blog.csdn.net/jinsedeme0881/article/details/79171151