SSH架构操作多数据库注意事项建议:不要多个系统同时对一个数据库进行增删改操作,但是可以有多个系统同时查询同一个数据库。 原理分析绝大多数情况下,我们使用SSH(Struts+spring+hibernate)架构只需要操作一个数据库,操作数据库原理为: 在spring的配置文件中,配置一个数据源DataSource,数据源中配置一个数据库连接池; 在spring的配置文件中,配置一个会话工厂SessionFactory,再将数据源DataSource关联到会话工厂sessionFactory上; 在持久层(Dao层),使用spring注入SessionFactory对象,使用SessionFactory创建Session,在进行数据库操作。 其中,Spring中DataSource和sessionFactory的配置如下: <!-- 数据源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"destroy-method="close"> <property name="driverClass" value="com.MySQL.jdbc.Driver" /> <property name="jdbcUrl"value="jdbc:mysql:///**数据库名称**?characterEncoding=utf8" /> <property name="user" value="A_username" /> <property name="password" value="A_password"/> </bean> <!-- 配置SessionFactory(与Hibernate整合) --> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <!-- 指定Hibernate配置文件路径 --> <property name="configLocation" value="classpath:hibernate.cfg.xml"></property> <!-- 指定数据库连接池 --> <property name="dataSource" ref="dataSource"></property> </bean>
由上述所说,我们可以简单的表示为:
这是spring提供的操作单个数据库的方法,spring同时还提供了操作多数据库的方式(此处以两个数据库为例),其原理为:
此处我引入一个动态数据源的概念,其实就是动态的切换数据源,先看一下动态数据源的用法,其实原理也就不言而喻了。 <!-- A数据源 --> <bean id="dataSourceA" class="com.mchange.v2.c3p0.ComboPooledDataSource"destroy-method="close"> <property name="driverClass" value="com.mysql.jdbc.Driver" /> <property name="jdbcUrl"value="jdbc:mysql:///**数据库名称**?characterEncoding=utf8" /> <property name="user" value="A_username" /> <property name="password" value="A_password"/> </bean> <!-- B数据源 --> <bean id="dataSourceB" class="com.mchange.v2.c3p0.ComboPooledDataSource"destroy-method="close"> <property name="driverClass" value=" com.mysql.jdbc.Driver" /> <property name="jdbcUrl" value="jdbc:mysql:///**数据库名称**?characterEncoding=utf8" /> <property name="user" value="B_username"/> <property name="password" value="B_password"/> </bean> <!-- 动态数据源 --> <bean id="dataSource" class="a.core.DynamicDataSource"> <property name="targetDataSources"> <map key-type="Java.lang.String"> <entry key="dataSourceA" value-ref="dataSourceA" /> <entry key="dataSourceB" value-ref="dataSourceB" /> </map> </property> <property name="defaultTargetDataSource" ref="dataSourceA" /> </bean> <!-- 配置SessionFactory(与Hibernate整合) --> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <!-- 指定Hibernate配置文件路径 --> <property name="configLocation" value="classpath:hibernate.cfg.xml"></property> <!-- 指定数据库连接池 --> <property name="dataSource" ref="dataSource"></property> </bean> 通过对比,我们可以看出:其实从本质上来说,动态数据源也是一个数据源,区别在与每次访问的数据源不同。因此,当决定了使用哪个数据源后,其他工作原理与上面单个数据库的原理一样。现在的问题就剩下了如何确定数据源以及什么时候确定数据源两个问题了。我们先来研究一下什么时候确定数据源。 首先,我们使用的是spring配合hibernate的架构,在使用hibernate作为持久层框架时,为避免出现懒加载异常,在web.xml中,都会配置OpenSessionInViewFilter——在表现层开启session的过滤器,保障在表现层调用时,不会出现异常。 我们如果配置了这一项,也就意味着项目在请求到达表现层之前就已经开启了session,而创建session需要使用sessionFactory创建,sessionFactory又依赖DataSource,因此dynamicDataSource使用哪个DataSource,应该在执行过滤器OpenSessionInViewFilter之前,就已经确定了。 因此,我的解决方案是:在web.xml中,过滤器OpenSessionInViewFilter之前配置一个DataSourceFIlter过滤器,用来确定动态数据源中使用哪个数据源。 如何确定使用哪个DataSource的步骤: 1. 在web.xml中,配置一个过滤器在OpenSessionInViewFilter; 2. 新建一个类DynamicDataSource,继承AbstractRoutingDataSource,重写determineCurrentLookupKey方法,用于决定使用哪个数据源DataSource; 3. Copy操作动态数据源的工具类DatabaseContextHolder的代码; 4. 有了2和3中的两个类,只需要一行代码,即可设置数据源:DatabaseContextHolder.setCustomerType("dataSourceB"); 5. 在过滤器中根据路径判断使用哪个数据源 6. 具体代码如下:
代码展示Web.xml中的配置:<filter> <filter-name>DynamicDataSourceFilter</filter-name> <filter-class>a.core.DynamicDataSourceFilter</filter-class> </filter> <filter-mapping> <filter-name>DynamicDataSourceFilter</filter-name> <url-pattern>*.action</url-pattern> </filter-mapping> <filter> <filter-name>OpenSessionInViewFilter</filter-name> <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class> </filter> <filter-mapping> <filter-name>OpenSessionInViewFilter</filter-name> <url-pattern>*.action</url-pattern> </filter-mapping> 过滤器a.core.DynamicDataSourceFilter的代码:package a.core; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; importjavax.servlet.http.HttpServletRequest;
public class DynamicDataSourceFilterimplements Filter {
@Override publicvoid destroy() {
}
@Override publicvoid doFilter(ServletRequest req, ServletResponse resp, FilterChainfilterChain) throws IOException, ServletException { HttpServletRequestrequest = (HttpServletRequest) req; Stringuri = request.getRequestURI(); System.out.println(uri); if(uri.contains("person")){ DatabaseContextHolder.setCustomerType("dataSourceB"); // DatabaseContextHolder是一个工具类,下附代码 System.out.println("B"); }else{ DatabaseContextHolder.setCustomerType("dataSourceA"); System.out.println("A"); } filterChain.doFilter(req,resp); }
@Override publicvoid init(FilterConfig arg0) throws ServletException {
}
} 动态数据源DynamicDataSource的代码:package a.core; importjava.sql.SQLFeatureNotSupportedException; import java.util.logging.Logger; importorg.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extendsAbstractRoutingDataSource {
@Override protectedObject determineCurrentLookupKey() { returnDatabaseContextHolder.getCustomerType(); }
@Override publicLogger getParentLogger() throws SQLFeatureNotSupportedException { returnnull; } } 操作动态数据源的工具类DatabaseContextHolder的代码:package a.core;
public classDatabaseContextHolder{
private static finalThreadLocal<String> contextHolder = newThreadLocal<String>();
public static void setCustomerType(StringcustomerType) { contextHolder.set(customerType); }
public static StringgetCustomerType() { return contextHolder.get(); }
public static void clearCustomerType() { contextHolder.remove(); } } Spring的配置文件<!-- A数据源 --> <bean id="dataSourceA" class="com.mchange.v2.c3p0.ComboPooledDataSource"destroy-method="close"> <property name="driverClass" value="com.mysql.jdbc.Driver" /> <property name="jdbcUrl"value="jdbc:mysql:///**数据库名称**?characterEncoding=utf8" /> <property name="user" value="A_username" /> <property name="password" value="A_password"/> </bean> <!-- B数据源 --> <bean id="dataSourceB" class="com.mchange.v2.c3p0.ComboPooledDataSource"destroy-method="close"> <property name="driverClass" value=" com.mysql.jdbc.Driver" /> <property name="jdbcUrl" value="jdbc:mysql:///**数据库名称**?characterEncoding=utf8" /> <property name="user" value="B_username"/> <property name="password" value="B_password"/> </bean> <!-- 动态数据源 --> <bean id="dataSource" class="a.core.DynamicDataSource"> <property name="targetDataSources"> <map key-type="java.lang.String"> <entry key="dataSourceA" value-ref="dataSourceA" /> <entry key="dataSourceB" value-ref="dataSourceB" /> </map> </property> <property name="defaultTargetDataSource" ref="dataSourceA" /> </bean> <!-- 配置SessionFactory(与Hibernate整合) --> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <!-- 指定Hibernate配置文件路径 --> <property name="configLocation" value="classpath:hibernate.cfg.xml"></property> <!-- 指定数据库连接池 --> <property name="dataSource" ref="dataSource"></property> </bean>
|
|