最近公司做一个saas模式的项目。在多数据源问题上卡了几天。有两个方案,这是1.0。
你们不知道我多辛苦,翻遍了中国网站,翻遍了外国网站。
前者不全面,不严谨,后者看不懂。
我们先从,多数据源切换开始。
上面这个类,我只是贪图方便然后就继承了,来使用。
同理。
这些都不重要。
重要的是对于 ThreadLocal<String> contextHolder,这就是一个线程,对于当前变量的保存,克隆。各个线程之间互不影响。
只要每次调用setCustomerType方法,而且前提是,你的那个数据源的集合里面有,才行。
那么切换数据源就成功了。
如何动态增长数据源呢,map,就是突破口。
利用spring,得到对象
详细就不给出了,抛砖引玉。
3.如何满足saas多租户模式的需求。
通过拦截器interceptor。SaasInterceptor
不断拦截用户的请求,不断更换。
这个方案很低级,不高效。
sqlsession,也还是单例的,没有saas的思想。就是一个伪saas。
/**
* Retrieve the current target DataSource. Determines the
* {@link #determineCurrentLookupKey() current lookup key}, performs
* a lookup in the {@link #setTargetDataSources targetDataSources} map,
* falls back to the specified
* {@link #setDefaultTargetDataSource default target DataSource} if necessary.
* @see #determineCurrentLookupKey()
*/
protected DataSource determineTargetDataSource() {
Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
Object lookupKey = determineCurrentLookupKey();
DataSource dataSource = this.resolvedDataSources.get(lookupKey);
if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
dataSource = this.resolvedDefaultDataSource;
}
if (dataSource == null) {
throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
}
return dataSource;
}
真正实现自动切换的地方。。
现在,记录相关的dynamicdatasource与spring 的相关特性,
每一次,进入spring,都会自动执行一边这个方法。
还发现,就算又是明明切换,但是我的框架,切实数据源切换失败的,着实有点蛋疼。。
既然这样,我就改写底层框架,使他变成,适应多数据源的spring。
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection);
handler.parameterize(stmt);
return stmt;
}
都是在这里执行connection的
进去看看,怎么得到的
protected Connection getConnection(Log statementLog) throws SQLException {
Connection connection = transaction.getConnection();
if (statementLog.isDebugEnabled()) {
return ConnectionLogger.newInstance(connection, statementLog, queryStack);
} else {
return connection;
}
}
我的乖乖,越来越复杂,,
/*
* Creates a logging version of a connection
*
* @param conn - the original connection
* @return - the connection with logging
*/
public static Connection newInstance(Connection conn, Log statementLog, int queryStack) {
InvocationHandler handler = new ConnectionLogger(conn, statementLog, queryStack);
ClassLoader cl = Connection.class.getClassLoader();
return (Connection) Proxy.newProxyInstance(cl, new Class[]{Connection.class}, handler);
}
最后总结,connection,跟statementlog没有半毛钱关系,只跟transaction有关系。
protected BaseExecutor(Configuration configuration, Transaction transaction) {
this.transaction = transaction;
this.deferredLoads = new ConcurrentLinkedQueue<DeferredLoad>();
this.localCache = new PerpetualCache("LocalCache");
this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
this.closed = false;
this.configuration = configuration;
this.wrapper = this;
}
翻了整个类,transaction,只是初始的时候,完成了初始化。。
transaction又是一个接口来的,必然有实现类,不翻了,猜想应该就是xml里配置的transactionManager,有关。
其实就是线索断了。。
那么实现就是,要么硬来,可以实现,我测试了。代码,很简单。
就是改变原有的connection,变成自定义。
这样的解决办法,相当暴力,那么耦合性就会很强。
其实还有下一篇的,等我BY了,我就写出来了。