实现spring多数据源以支持saas或手机App的多公司模式
2015-03-30 20:37
387 查看
当我们开发saas或手机应用程序,涉及多公司时,要求每个公司使用一个数据库,如何配置spring数据源使得每个公司使用不同的数据库连接?
1、将公司id存放到一个ThreadLocal变量中,每次请求时设置,使得每次访问数据源可以从ThreadLocal获取当前请求所属的公司id。
可以定义一个filter,在doFilter方法中调用即可达到每次请求设置的效果。至于客户端请求的公司id参数放在哪里,这个可以灵活设置,可以放在每个请求的request参数中,也可以放在cookie中
3、修改spring数据源配置
原配置(一般使用c0p3数据原的配置)
自己新建一个MultiClientDataSource类(实现DataSource接口)去代替原来注入dataSource的ComboPooledDataSource,MultiClientDataSource类的实现精华在于使用一个map,用公司id(did)作为key,value为connection,在getConnection方法中判断是否在map中已经有该did的key,如果没有就新建connection,并且加入到map中
1、将公司id存放到一个ThreadLocal变量中,每次请求时设置,使得每次访问数据源可以从ThreadLocal获取当前请求所属的公司id。
/** * 用来存放当前线程的数据 */ public class ThreadHolder { //公司id private static ThreadLocal<String> localDid = new ThreadLocal<String>(); public static void putSp(String sp) { localDid.set(sp); } public static String getSp() { if (localDid.get() == null) return ""; else return (String)localDid.get(); } }2、在每次请求时调用ThreadHolder.putSp方法设置公司id。
可以定义一个filter,在doFilter方法中调用即可达到每次请求设置的效果。至于客户端请求的公司id参数放在哪里,这个可以灵活设置,可以放在每个请求的request参数中,也可以放在cookie中
3、修改spring数据源配置
原配置(一般使用c0p3数据原的配置)
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="xxxx" /> <property name="jdbcUrl" value="xxx" /> <property name="user" value="user" /> <property name="password" value="password" /> <property name="minPoolSize" value="1" /> <property name="maxPoolSize" value="800" /> <property name="maxIdleTime" value="25000" /> <property name="acquireIncrement" value="1" /> <property name="maxStatements" value="0" /> <property name="initialPoolSize" value="100" /> <property name="idleConnectionTestPeriod" value="18000" /> <property name="acquireRetryAttempts" value="10" /> <property name="acquireRetryDelay" value="1000" /> <property name="breakAfterAcquireFailure" value="false" /> <property name="checkoutTimeout" value="10000" /> <property name="testConnectionOnCheckout" value="false" /> </bean>修改成:
<bean id="dataSource" class="com.sangfor.frame.multiclient.MultiClientDataSource"> <property name="dataSource"> <ref bean="dataSourceDefault" /> </property> </bean> <bean id="dataSourceDefault" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="xxxx" /> <property name="jdbcUrl" value="xxx" /> <property name="user" value="user" /> <property name="password" value="password" /> <property name="minPoolSize" value="1" /> <property name="maxPoolSize" value="800" /> <property name="maxIdleTime" value="120" /> <property name="acquireIncrement" value="1" /> <property name="maxStatements" value="0" /> <property name="initialPoolSize" value="100" /> <property name="idleConnectionTestPeriod" value="65" /> <property name="acquireRetryAttempts" value="10" /> <property name="acquireRetryDelay" value="1000" /> <property name="breakAfterAcquireFailure" value="false" /> <property name="checkoutTimeout" value="10000" /> </bean>MultiClientDataSource:
public class MultiClientDataSource implements DataSource { public static final String DBNAME_PREFIX = "CLIENT_"; private ComboPooledDataSource dataSource = null; private Map<String,DataSource> dsMap = new HashMap<String,DataSource>(); private static Object lock=new Object(); public DataSource addDataSource(String did){ try{ synchronized (lock) { if (dsMap==null) dsMap = new HashMap<String,DataSource>(); DataSource ds = dsMap.get(did); if (ds != null) return ds; ComboPooledDataSource newDs = getNewDataSource(did); dsMap.put(did, newDs); } }catch(Exception e){ e.printStackTrace(); } return dsMap.get(did); } public void removeDataSource(String did){ if (dsMap==null) return; dsMap.remove(did); } private ComboPooledDataSource getNewDataSource(String did) throws Exception{ ComboPooledDataSource ds = new ComboPooledDataSource(); ds.setDriverClass("xxx");//driver class String jdbcUrl = "jdbc:mysql://127.0.0.1" //一般将ip配置在配置文件 +":3306"+"/"+ DBNAME_PREFIX + did + "?useUnicode=true&characterEncoding=utf-8"; ds.setJdbcUrl(jdbcUrl); ds.setUser("user"); ds.setPassword("password"); ds.setMinPoolSize(1); ds.setMaxPoolSize(800); ds.setMaxIdleTime(2000); ds.setAcquireIncrement(1); ds.setMaxStatements(0); ds.setInitialPoolSize(100); ds.setIdleConnectionTestPeriod(1800); ds.setAcquireRetryAttempts(10); ds.setAcquireRetryDelay(1000); ds.setBreakAfterAcquireFailure(false); ds.setCheckoutTimeout(10000); ds.setPreferredTestQuery(" select FORMID from FLOW_FORM where 1 = 2"); ds.setTestConnectionOnCheckout(false); return ds; } public Connection getConnection() throws SQLException { return getDataSource().getConnection(); } public Connection getConnection(String arg0, String arg1) throws SQLException { return getDataSource().getConnection(arg0, arg1); } public PrintWriter getLogWriter() throws SQLException { return getDataSource().getLogWriter(); } public int getLoginTimeout() throws SQLException { return getDataSource().getLoginTimeout(); } public void setLogWriter(PrintWriter arg0) throws SQLException { getDataSource().setLogWriter(arg0); } public void setLoginTimeout(int arg0) throws SQLException { getDataSource().setLoginTimeout(arg0); } public DataSource getDataSource(String dataSourceName)throws SQLException { try{ if(dataSourceName==null||dataSourceName.equals("")){ return this.dataSource; }else{ DataSource ds = dsMap.get(dataSourceName); if (ds!=null) return ds; else return null; } }catch(NoSuchBeanDefinitionException ex){ throw new SQLException("There is not the dataSource <name:"+dataSourceName+"> in the applicationContext!"); } } public void setDataSource(ComboPooledDataSource dataSource) { this.dataSource = dataSource; } public ComboPooledDataSource getDataSource()throws SQLException{ String did = ThreadHolder.getSp(); DataSource ds = getDataSource(did); if (ds == null) return null; else return (ComboPooledDataSource)ds; } public DataSource getDefaultDataSource()throws SQLException{ return dataSource; } @Override public boolean isWrapperFor(Class<?> arg0) throws SQLException { // TODO Auto-generated method stub return false; } @Override public <T> T unwrap(Class<T> arg0) throws SQLException { // TODO Auto-generated method stub return null; } }实现原理:
自己新建一个MultiClientDataSource类(实现DataSource接口)去代替原来注入dataSource的ComboPooledDataSource,MultiClientDataSource类的实现精华在于使用一个map,用公司id(did)作为key,value为connection,在getConnection方法中判断是否在map中已经有该did的key,如果没有就新建connection,并且加入到map中
相关文章推荐
- Spring实现动态数据源,支持动态添加、删除和设置权重及读写分离
- atomikos实现多数据源支持分布式事务管理(spring、tomcat、JTA)
- Spring实现动态数据源,支持动态添加、删除和设置权重及读写分离
- Spring实现动态数据源,支持动态加入、删除和设置权重及读写分离
- atomikos实现多数据源支持分布式事务管理(spring、tomcat、JTA)
- spring+mybatis 多数据源切换,动态数据源增长,saas多租户模式方案
- atomikos实现多数据源支持分布式事务管理(spring、tomcat、JTA)
- Spring实现动态数据源,支持动态添加、删除和设置权重及读写分离
- Spring 配置多数据源实现数据库读写分离
- ActiveMQ与spring集成实现Queue模式
- 反射实现 AOP 动态代理模式(Spring AOP 的实现 原理)
- .Net语言 APP开发平台——Smobiler学习日志:如何在手机上实现扇形图表
- JSP中实现判断客户端手机类型并跳转到app下载页面
- Spring标签@Aspect-实现面向方向编程(@Aspect的多数据源自动加载)——SKY
- 被忽略的Spring3小改进——支持多数据源的@Transactional事务注解
- Android-----代码实现打开手机第三方应用APP
- iOS App设计模式开发中策略模式的实现示例
- Spring 配置多数据源实现数据库读写分离
- Spring+MyBatis+Velocity+BootStrap实现Android混合App快速高效服务端
- 中国首个 SaaS 模式的云告警平台 iOS 版 APP 上线