1. 程式人生 > >SAAS系統的資料庫設計,動態資料來源動態切換

SAAS系統的資料庫設計,動態資料來源動態切換

概念

資料來源:指的是一個具體的資料庫連線。例如 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
	}
}