saas模式下的spring多数据源实现

当我们开发saas或手机应用程序,涉及多公司时,要求每个公司使用一个数据库,如何配置spring数据源使得每个公司使用不同的数据库连接?

1、将公司id存放到一个ThreadLocal变量中,每次请求时设置,使得每次访问数据源可以从ThreadLocal获取当前请求所属的公司id。

/** * 用来存放当前线程的数据 */public class ThreadHolder {//公司idprivate 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 "";elsereturn (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 classString 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;}private void initDsMap()throws Exception{try{if (dsMap==null){dsMap = new HashMap<String,DataSource>();}}catch(Exception e){throw new Exception(e);}}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;elsereturn 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;elsereturn (ComboPooledDataSource)ds;}public DataSource getDefaultDataSource()throws SQLException{return dataSource;}@Overridepublic boolean isWrapperFor(Class<?> arg0) throws SQLException {// TODO Auto-generated method stubreturn false;}@Overridepublic <T> T unwrap(Class<T> arg0) throws SQLException {// TODO Auto-generated method stubreturn null;}}实现原理:自己新建一个MultiClientDataSource类(实现DataSource接口)去代替原来注入dataSource的ComboPooledDataSource,MultiClientDataSource类的实现精华在于使用一个map,用公司id(did)作为key,value为connection,在getConnection方法中判断是否在map中已经有该did的key,如果没有就新建connection,并且加入到map中

最好的节约是珍惜时间,最大的浪费是虚度年华。

saas模式下的spring多数据源实现

相关文章:

你感兴趣的文章:

标签云: