一、 目的
不同的用户登录到具有表结构相同的各自数据库(一个oracle 服务器),来操作各自的数据,而dao 不知道这些改变。
二、 实现方法
1、分析applicationContext.xml
数据源bean配置
xml 代码
<bean id="dataSource" singleton="true" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName">
<value>oracle.jdbc.driver.OracleDrivervalue</value>
</property>
<property name="url">
<value>jdbc:oracle:thin:@192.168.0.254:1521:CARRYDL</value>
</property>
<property name="username">
<value>oa</value>
</property>
<property name="password">
<value>oa</value>
</property>
</bean>
<!-- Session 工厂的配置 -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="mappingDirectoryLocations">
<list>
<value>classpath:/com/carry/hibernate</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle9Dialect</prop>
<prop key="hibernate.c3po.min_size">5</prop>
<prop key="hibernate.c3po.max_size">20</prop>
<prop key="hibernate.c3po.timeout">1800</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.cglib.use_reflection_optimizer">true/prop>
</props>
</property>
</bean>
从上面配置文件可以看出,数据源和session 工厂是一一对应的。
如果配置多个数据源,就要配置多个session工厂,事务的代理就更加的麻烦,所以这样的方法不可取的。所以只能从数据源入手。
2、动态数据源框架
由于spring 使用javax.sql.DataSource 统一接口。所以可以复写DataSource具体如下所示:
java 代码
package com.carry.spring.datasource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
public class MultiDataSource implements DataSource {
private static final Map<String, DataSource> dataSources = new HashMap<String, DataSource>(); //数据源对象池
private DataSource dataSource = null; //默认数据源用于测试的数据源
public DataSource getDataSource(String dataSourceName) {
if (dataSourceName == null || dataSourceName.equals("")) {
return this.dataSource;
}
if (dataSources.get(dataSourceName) != null) {
return dataSources.get(dataSourceName);
} else {
return createDataSource(SpObserver.getDBType(), SpObserver.getUrl(), SpObserver
.getName(), SpObserver.getPassword());
}
}
/**
* 动态创建数据源
*
* @param dbType
* 数据库类型
* @param url
* 数据库URL
* @param userName
* 用户名
* @param password
* 密码
* @return 数据源
*/
public DataSource createDataSource(String dbType, String url, String userName, String password) {
BasicDataSource s = new BasicDataSource();
// 获取已有配置的信息
BasicDataSource ts = (BasicDataSource) dataSource;
System.out.println(dbType + " -- " + url + " -- " + userName + " -- " + password);
s.setUsername(userName);
s.setPassword(password);
s.setUrl(url);
// 根据数据类型获取驱动。这里可以直接适用默认的驱动
s.setDriverClassName(ts.getDriverClassName());
dataSources.put(userName, s);
return s;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public DataSource getDataSource() {
String sp = SpObserver.getName();
return getDataSource(sp);
}
@Override
public Connection getConnection() throws SQLException {
return getDataSource().getConnection();
}
@Override
public Connection getConnection(String arg0, String arg1) throws SQLException {
return getDataSource().getConnection(arg0, arg1);
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return getDataSource().getLogWriter();
}
@Override
public int getLoginTimeout() throws SQLException {
return getDataSource().getLoginTimeout();
}
@Override
public void setLogWriter(PrintWriter arg0) throws SQLException {
getDataSource().setLogWriter(arg0);
}
@Override
public void setLoginTimeout(int arg0) throws SQLException {
getDataSource().setLoginTimeout(arg0);
}
@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;
}
}
共享数据安全为MultiDataSource.java添加如下图所的辅助类
java 代码
package com.carry.spring.datasource;
public class SpObserver {
private static ThreadLocal<String> localDBType = new ThreadLocal<String>();
private static ThreadLocal<String> localUrl = new ThreadLocal<String>();
private static ThreadLocal<String> localName = new ThreadLocal<String>();
private static ThreadLocal<String> localPassword = new ThreadLocal<String>();
public static void putDBType(String sp) {
localDBType.set(sp);
}
public static String getDBType() {
return (String) localDBType.get();
}
public static void putUrl(String sp) {
localUrl.set(sp);
}
public static String getUrl() {
return (String) localUrl.get();
}
public static void putName(String sp) {
localName.set(sp);
}
public static String getName() {
return (String) localName.get();
}
public static void putPassword(String sp) {
localPassword.set(sp);
}
public static String getPassword() {
return (String) localPassword.get();
}
}
该类使用ThreadLocal 来存储数据库用户和密码保证数据的不冲突
MultiDataSource 需要和MVC 通讯来了解用户需要登录使用那一个数据库,所以必须有一个Filter 来对HttpServletRequest 对象进行监听,来获得登录数据的用户和密码。这里也可以直接在登录页面中进行设置。
如下代码
java 代码
package com.carry.spring.datasource;
public class MyFilter implements Filter {
public MyFilter() {
}
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
Object nobj = httpRequest.getSession().getAttribute("name");
Object pobj = httpRequest.getSession().getAttribute("pass");
if (nobj != null && pobj != null) {
String name = nobj.toString();
String pass = pobj.toString();
SpObserver.putName(name);
SpObserver.putPass(pass);
System.out.println("使数据源" + name);
} else {
SpObserver.putName("");
SpObserver.putPass("");
System.out.println("用户未登录,使用默认数据源");
}
chain.doFilter(request, response);
}
public void destroy() {
}
}
新的applicationContext.xml 的配置文件如下
<bean id="dataSource" singleton="true"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName">
<value>oracle.jdbc.driver.OracleDriver</value>
</property>
<property name="url">
<value>jdbc:oracle:thin:@192.168.0.254:1521:CARRYDL</value>
</property>
<property name="username">
<value>oa</value>
</property>
<property name="password">
<value>oa</value>
</property>
</bean>
<bean id="multiDataSource" singleton="true"
class="com.carry.spring.datasource.MultiDataSource">
<property name="dataSource">
<ref bean="dataSource" />
</property>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="multiDataSource" />
</property>
<property name="mappingDirectoryLocations">
<list>
<value>classpath:/com/carry/hibernate/</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle9Dialect</prop>
<prop key="hibernate.c3po.min_size">5</prop>
<prop key="hibernate.c3po.max_size">20</prop>
<prop key="hibernate.c3po.timeout">1800</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.cglib.use_reflection_optimizer">true</prop>
</props>
</property>
<property name="lobHandler" ref="lobHandler" />
</bean>
Web.xml 的配置增加如下filter
<filter>
<filter-name>dataSourceFilter</filter-name>
<filter-class>com.carry.spring.datasource.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>dataSourceFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>