特别是SAAS应用系统,一般的我们会为每个公司客户创建一个对应的数据库,然而我们肯定不会为每个客户部署一台服务器,这样应用系统就存在如何在多个数据库中自动切换的问题;
首先创建我们自己的数据源实现类,如下:
package whf.framework.jdbc;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import whf.framework.config.Configuration;
import whf.framework.config.PropertiesConfiguration;
import whf.framework.ext.entity.Database;
import whf.framework.ext.service.DatabaseServiceImp;
import whf.framework.log.Log;
import whf.framework.log.LogFactory;
import whf.framework.meta.Meta;
import whf.framework.security.UserContext;
import whf.framework.service.SpringService;
import whf.framework.util.StringUtils;
import whf.framework.util.ThreadContext;
import whf.framework.util.Utils;
/**
* 可以根据上下文,动态分派数据库联接的数据源<br>
* 搜索数据源的顺序:<br>
* 1.线程当前数据源名称(手工输入的,优先级最高)
* 2.从对象定义中获取;
* 3.获取用户当前所在的数据源分支,从UserContext中获取;
* 4.使用缺省
* @author King
*
*/
public class SwitchableDataSource extends SpringService implements DataSource {
private static Log log = LogFactory.getLog(SwitchableDataSource.class);
private Map<String, DataSource> registeredDataSources = Utils.newHashMap();
private static String defaultDataSourceName;
public void setRegisteredDataSources(Map<String, DataSource> registeredDataSource) {
this.registeredDataSources = registeredDataSource;
}
public void setDefaultDataSourceName(String defaultDataSourceName) {
SwitchableDataSource.defaultDataSourceName = defaultDataSourceName;
}
//
private Configuration jdbcConfiguration;
private synchronized void registerDataSource(String dataSourceName) {
if(this.registeredDataSources.containsKey(dataSourceName)) return;
if(this.jdbcConfiguration == null) {
try{
this.jdbcConfiguration = new PropertiesConfiguration(SwitchableDataSource.class.getResource("/conf/jdbc.properties"));;
}catch(Exception e){
throw new RuntimeException(e);
}
}
String driverClassName = this.jdbcConfiguration.getString("jdbc." + dataSourceName + ".driverClassName");
if(StringUtils.isEmpty(driverClassName)) driverClassName = this.jdbcConfiguration.getString("jdbc.driverClassName");
String username = null;
String password = null;
String url = this.jdbcConfiguration.getString("jdbc." + dataSourceName + ".url");
if(!StringUtils.isEmpty(url)) {
username = this.jdbcConfiguration.getString("jdbc." + dataSourceName + ".username");
password = this.jdbcConfiguration.getString("jdbc." + dataSourceName + ".password");
} else {
try{
Database db = DatabaseServiceImp.getDatabaseService().findByCode(dataSourceName);
url = db.getUrl();
username = db.getUsername();
password = db.getPassword();
}catch(Exception e){
throw new RuntimeException(e);
}
}
this.registeredDataSources.put(dataSourceName, new DriverManagerDataSource(driverClassName, url, username, password));
}
public final DataSource getDataSource(String dataSourceName) {
if(!this.registeredDataSources.containsKey(dataSourceName)) {
this.registerDataSource(dataSourceName);
}
return this.registeredDataSources.get(dataSourceName);
}
public final DataSource getDefaultDataSource() {
return this.getDataSource(defaultDataSourceName);
}
private DataSource getCurrentDataSource() {
return this.getDataSource(getCurrentDataSourceName());
}
/**
* @return 获取当前线程的数据源
*/
public final static String getCurrentDataSourceName() {
String dsn = ThreadContext.getCurrentDataSourceName();
Meta meta = ThreadContext.getCurrentMeta();
if(meta != null && StringUtils.isEmpty(dsn)) {
dsn = meta.getBranch();
}
if(StringUtils.isEmpty(dsn)) {
UserContext uc = ThreadContext.getUserContext();
if(uc != null)
dsn = uc.getBranch();
}
if(StringUtils.isEmpty(dsn))
dsn = defaultDataSourceName;
log.debug("DataSourceName: "+dsn + "\n");
return dsn;
}
@Override
public int getLoginTimeout() throws SQLException {
return this.getCurrentDataSource().getLoginTimeout();
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return this.getCurrentDataSource().getLogWriter();
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
this.getCurrentDataSource().setLoginTimeout(seconds);
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
this.getCurrentDataSource().setLogWriter(out);
}
@Override
public Connection getConnection() throws SQLException {
return this.getCurrentDataSource().getConnection();
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return this.getCurrentDataSource().getConnection(username, password);
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return this.getCurrentDataSource().isWrapperFor(iface);
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return this.getCurrentDataSource().unwrap(iface);
}
@Override
public void afterPropertiesSet() throws Exception {
if(StringUtils.isEmpty(defaultDataSourceName))
throw new Exception("Default datasource name can not be empty!");
if(this.registeredDataSources.get(defaultDataSourceName) == null)
throw new Exception("Can not found default datasource for default dataSourceName:" + defaultDataSourceName);
super.afterPropertiesSet();
}
public static void main(String[] args) throws Exception {
System.out.println(SwitchableDataSource.class.getResource("/conf/jdbc.properties"));
}
}
这个数据源切换期可以按照三种方式切换:
1 在线程上下文环境中设置,参考whf.framework.util.ThreadContext.setCurrentDataSourceName, 在具体应用中这个也具有最高优先级,主要满足一些特殊的需要,限定某些操作只能在某个数据库中处理;
2 根据对象配置,如果当前操作的对象必须在某个数据库中,例如一些配置数据,可以限定为从某个自定的数据库中存取;
3 根据当前操作用户;
第二步 部署数据源到Spring
<bean id="dataSource" class="whf.framework.jdbc.SwitchableDataSource">
<property name="defaultDataSourceName">
<value>framework</value>
</property>
<property name="registeredDataSources">
<map>
<entry key="framework">
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>${jdbc.framework.driverClassName}</value>
</property>
<property name="url">
<value>${jdbc.framework.url}</value>
</property>
<property name="username">
<value>${jdbc.framework.username}</value>
</property>
<property name="password">
<value>${jdbc.framework.password}</value>
</property>
</bean>
</entry>
<entry key="monica">
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>${jdbc.monica.driverClassName}</value>
</property>
<property name="url">
<value>${jdbc.monica.url}</value>
</property>
<property name="username">
<value>${jdbc.monica.username}</value>
</property>
<property name="password">
<value>${jdbc.monica.password}</value>
</property>
</bean>
</entry>
</map>
</property>
</bean>
其中参数使用Spring的properties设置,如
<bean id="jdbcConfiguration" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location"
value="classpath:conf/jdbc.properties" />
</bean>
最后就是调用
首先用户的数据库是需要可识别的,因此用户必须有一个(用户所属组织)参数,用以识别对应的数据库;在用户登录时,需要三个参数:所属组织,用户名,密码
ok,上述已经完成;
完整代码参考:
http://whfframework.googlecode.com/svn/trunk/
分享到:
相关推荐
- **Struts**:处理用户请求,根据用户操作跳转到相应的视图,同时与业务层(由Spring管理)进行交互,传递数据。 - **Spring**:作为整个应用的中枢,管理所有Bean(包括Struts的Action类和Hibernate的...
在SSH框架中,Spring通常作为服务层和持久层之间的粘合剂,管理Hibernate SessionFactory,并通过DAO(数据访问对象)模式提供数据库访问。 【Hibernate】 Hibernate是一个对象关系映射工具,它可以将Java对象映射...
SSH框架,全称为Struts2、Spring和Hibernate的组合,是Java Web开发中常用的一套开源框架。...通过这个示例,初学者可以了解到SSH框架在实际项目中的应用,以及如何结合前端技术实现高效、友好的分页功能。
在这个名为"SSH框架的完整Java项目"的压缩包中,我们可以预见到包含了一个完整的基于SSH框架的项目结构,非常适合初学者学习和理解框架的集成与应用。 **Spring框架**: Spring是核心的容器框架,它负责管理对象的...
在这个场景下,动态数据源允许应用程序根据业务需求或条件在多个数据库之间灵活切换,比如在测试和生产环境间切换,或者针对不同的用户群体使用不同的数据源。 Spring框架在SSH组合中承担了依赖注入和管理任务,...
【基于SSH框架的请假系统】是一个综合运用了Struts2、Hibernate3.2和Spring2.5.6三大主流Java企业级开发框架的项目。这个系统设计简洁实用,旨在为用户提供一个高效便捷的请假申请与审批流程。它利用SQL Server 2008...
"JAVA SSH项目(动态数据库,总库分库实时切换,内含sql文件,纯后台)" 这个标题表明我们讨论的是一个基于JAVA SSH框架开发的项目。SSH在这里指的是Spring、Struts和Hibernate三个开源框架的组合,它们是Java Web...
切换到Java视图,导入Hibernate 3.2的jar包。在项目属性中配置Hibernate,选择已有的Spring配置文件,并指定新创建的数据源。 5. **解决jar包冲突** 在项目中可能存在jar包冲突,如asm.jar、asm-attrs.jar和cglib...
SSH2整合分页是Web开发中的一个重要概念,SSH2指的是Spring、Struts2和Hibernate这三个开源框架的组合,它们在Java Web开发中被广泛应用。在这个场景下,"整合"意味着将这三个框架集成到一个项目中,以实现高效、...
在本系统中,SSH2框架被用于构建一个网站内容管理系统,它提供了全面的内容发布、管理以及用户交互功能。 **Spring框架** Spring是Java企业级应用的核心框架,负责依赖注入(DI)和面向切面编程(AOP)。在这个系统...
- 在Driver JARs中添加对应的数据库驱动JAR文件。 - 点击“Test Driver”按钮测试连接。 3. **返回Java Enterprise透视图**,并在项目上点击右键选择“MyEclipse”>“Add Hibernate Capabilities”。 4. **配置...
在本场景中,"ssh实现简单分页带页码显示"是指利用SSH框架来实现Web应用中的分页功能,并在用户界面展示页码,让用户能够方便地浏览大量数据。分页在很多情况下都是必要的,比如论坛、电商网站的商品列表等,它能...
在这个SSH项目中,我们通常会用到一系列的jar包,这些jar包是实现SSH框架功能的基础。以下是对这些jar包及其在项目中的作用的详细解释: 1. **Struts2框架**: - `struts2-core.jar`:这是Struts2的核心库,包含了...
我们将通过一系列步骤,展示如何在MyEclipse中搭建SSH框架的基本环境。 #### 二、项目创建及初始化 1. **创建Web项目**: - 在MyEclipse中新建一个Web项目,命名为`SSHDEMO`。 - 修改项目的Context root URL为`...
在本项目中,只需要修改Hibernate的配置文件以适应不同的数据库,即可实现数据库的切换。 MyEclipse10是一个集成开发环境,集成了对Java EE项目的良好支持,包括对SSH框架的便捷配置和调试。配合JDK6.0,可以构建和...
- **实体类(Entity)**:Hibernate用这些类来表示数据库中的表,每个实体类通常对应数据库中的一个表。 - **DAO(数据访问对象)**:这些类包含了与数据库交互的代码,利用Hibernate API进行CRUD(创建、读取、...
7. **实际项目中的适应性**:在实际项目中,可能需要根据数据库类型和具体需求调整分页SQL的写法,例如Oracle支持ROWNUM,而MySQL则可以使用LIMIT和OFFSET。 **优化技巧**: 1. **缓存**:对于不经常变动的数据,...
这两个类分别对应数据库中的部门表和员工表,包含了相应的属性和getter/setter方法,用于数据的存取。 三、实体类和映射文件 实体类Department和Employee定义了它们的属性,比如Department的id、dname和与之关联的...