求助帖:spring、mybatis实现动态维护数据源(动态增加或动态删除)

willowyxd 2013-07-02 04:34:08
最近在做spring3+mybatis3整合,遇上个比较难解决的问题,特此发帖向广大技术牛求助,废话不表,直接说问题。网上搜索了很多技术文章,都是实现动态切换数据源、或者是动态配置多数据源的,这里先声明一下,如果还是提供这种解决方案的牛们就不要回帖了,很浪费时间的。

最终效果希望能够实现成为系统配置中维护中心库的数据源,其他库通过中心库统一维护,并可以实现这些数据源的动态新增、删除、动态切换。希望有相关经验的大牛能帮忙结贴。

以下是问题描述:
系统有一个默认的datasource(这个数据源是通过spring进行配置的),在这个datasource中维护了一张datasource表,用来维护其他的数据源(DynamicDataSource),表结构如下:

动态数据源DynamicDataSource在spring的applicationContext.xml配置文件中定义如下:

线程安全的ThreadLocal:DBContextHolder.java

/**
* @author williams
* @descrice 多个登录用户可能需要同时切换数据源,所以这里需要写一个线程安全的ThreadLocal
* @more 用户切换数据源只要在程序中使用 DBContextHolder.setDBType("1") 即可完成数据源切换
*/
public class DBContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
public static void setDBType(String dbType) {
contextHolder.set(dbType);
}
public static String getDBType() {
return (String) contextHolder.get();
}
public static void clearDBType() {
contextHolder.remove();
}
}


实现动态数据源切换逻辑

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.log4j.Logger;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
* @author williams
* @describe 实现动态数据源切换逻辑
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
private Logger log = Logger.getLogger(this.getClass());
private Map<Object, Object> _targetDataSources;

/**
* @see org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource#determineCurrentLookupKey()
* @describe 数据源为空或者为0时,自动切换至默认数据源,即在配置文件中定义的dataSource数据源
*/
@Override
protected Object determineCurrentLookupKey() {
String dataSourceName = DBContextHolder.getDBType();
if (dataSourceName == null) {
dataSourceName = "dataSource";
} else {
this.selectDataSource(Integer.valueOf(dataSourceName));
if (dataSourceName.equals("0"))
dataSourceName = "dataSource";
}
log.debug("--------> use datasource " + dataSourceName);
return dataSourceName;
}

public void setTargetDataSources(Map<Object, Object> targetDataSources) {
this._targetDataSources = targetDataSources;
super.setTargetDataSources(this._targetDataSources);
afterPropertiesSet();
}

public void addTargetDataSource(String key, BasicDataSource dataSource) {
this._targetDataSources.put(key, dataSource);
this.setTargetDataSources(this._targetDataSources);
}

public BasicDataSource createDataSource(String driverClassName, String url,
String username, String password) {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setTestWhileIdle(true);
return dataSource;
}

/**
* @param serverId
* @describe 数据源存在时不做处理,不存在时创建新的数据源链接,并将新数据链接添加至缓存
*/
public void selectDataSource(Integer serverId) {
Object sid = DBContextHolder.getDBType();
if ("0".equals(serverId + "")) {
DBContextHolder.setDBType("0");
return;
}
Object obj = this._targetDataSources.get(serverId);
if (obj != null && sid.equals(serverId + "")) {
return;
} else {
BasicDataSource dataSource = this.getDataSource(serverId);
if (null != dataSource)
this.setDataSource(serverId, dataSource);
}
}

/**
* @describe 查询serverId对应的数据源记录
* @param serverId
* @return
*/
public BasicDataSource getDataSource(Integer serverId) {
this.selectDataSource(0);
this.determineCurrentLookupKey();
Connection conn = null;
HashMap<String, Object> map = null;
try {
conn = this.getConnection();
PreparedStatement ps = conn
.prepareStatement("SELECT * FROM bas_datasource WHERE DBS_ID = ?");
ps.setInt(1, serverId);
ResultSet rs = ps.executeQuery();
map = new HashMap<String, Object>();
if (rs.next()) {
map.put("DBS_ID", rs.getInt("DBS_ID"));
map.put("DBS_DriverClassName", rs
.getString("DBS_DriverClassName"));
map.put("DBS_URL", rs.getString("DBS_URL"));
map.put("DBS_UserName", rs.getString("DBS_UserName"));
map.put("DBS_Password", rs.getString("DBS_Password"));
}
rs.close();
ps.close();
} catch (SQLException e) {
log.error(e);
} finally {
try {
conn.close();
} catch (SQLException e) {
log.error(e);
}
}
if (null != map) {
String driverClassName = map.get("DBS_DriverClassName").toString();
String url = map.get("DBS_URL").toString();
String userName = map.get("DBS_UserName").toString();
String password = map.get("DBS_Password").toString();
BasicDataSource dataSource = this.createDataSource(driverClassName,
url, userName, password);
return dataSource;
}
return null;
}

/**
* @param serverId
* @param dataSource
*/
public void setDataSource(Integer serverId, BasicDataSource dataSource) {
this.addTargetDataSource(serverId + "", dataSource);
DBContextHolder.setDBType(serverId + "");
}

}


在应用场景做数据源动态加载并切换的时候报错:

[DEBUG]--------> use datasource 2 (DynamicDataSource.java:46):determineCurrentLookupKey[com.ehofy.base.db.DynamicDataSource]
[ERROR]获取用户列表时发生错误! (MasterAction.java:59):getStore[com.ehofy.action.global.MasterAction]
org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (Access denied for user 'root'@'56.108.114.130' (using password: YES))
at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:80)
at org.mybatis.spring.SqlSessionUtils.getSqlSession(SqlSessionUtils.java:117)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:345)
at $Proxy12.selectList(Unknown Source)
at org.mybatis.spring.SqlSessionTemplate.selectList(SqlSessionTemplate.java:190)
at org.apache.ibatis.binding.MapperMethod.executeForList(MapperMethod.java:100)
at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:70)
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:25)
at $Proxy29.selectList(Unknown Source)
at com.ehofy.db.service.MasterService.getMasters(MasterService.java:60)
at com.ehofy.controller.global.MasterController.getMasters(MasterController.java:23)
at com.ehofy.controller.global.MasterController$$FastClassByCGLIB$$37e7b273.invoke(<generated>)
at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:191)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:617)
at com.ehofy.controller.global.MasterController$$EnhancerByCGLIB$$dcbd4599.getMasters(<generated>)
at com.ehofy.action.global.MasterAction.getStore(MasterAction.java:51)
......
Caused by: org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (Access denied for user 'root'@'56.108.114.130' (using password: YES))
at org.apache.commons.dbcp.BasicDataSource.createPoolableConnectionFactory(BasicDataSource.java:1549)
at org.apache.commons.dbcp.BasicDataSource.createDataSource(BasicDataSource.java:1388)
at org.apache.commons.dbcp.BasicDataSource.getConnection(BasicDataSource.java:1044)
at org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource.getConnection(AbstractRoutingDataSource.java:148)
at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:111)
at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:77)
... 46 more
Caused by: java.sql.SQLException: Access denied for user 'root'@'56.108.114.130' (using password: YES)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:946)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:2870)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:812)
at com.mysql.jdbc.MysqlIO.secureAuth411(MysqlIO.java:3269)
at com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:1182)
at com.mysql.jdbc.Connection.createNewIO(Connection.java:2670)
at com.mysql.jdbc.Connection.<init>(Connection.java:1531)
at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:266)
at org.apache.commons.dbcp.DriverConnectionFactory.createConnection(DriverConnectionFactory.java:38)
at org.apache.commons.dbcp.PoolableConnectionFactory.makeObject(PoolableConnectionFactory.java:582)
at org.apache.commons.dbcp.BasicDataSource.validateConnectionFactory(BasicDataSource.java:1556)
at org.apache.commons.dbcp.BasicDataSource.createPoolableConnectionFactory(BasicDataSource.java:1545)
... 51 more

...全文
1499 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
  • 打赏
  • 举报
回复
你遇到的问题和我一样,这几天也在折腾,你解决了吗,求告诉思路,我现在只有一种很笨的解决方法,就是把一类用户的数据源hibernate配置文件放在固定的文件夹下,用户登录时查找链接,我想问问大神你的解决方法。
tuoweizhuang2 2013-07-04
  • 打赏
  • 举报
回复
靠,lz这个需求跟我一模一样,这几天也在折腾,有啥好的想法可以分享下
willowyxd 2013-07-03
  • 打赏
  • 举报
回复
打个比方说 default datasource的properties配置里面维护的是jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8,这个数据源是D Bas_datasource里面维护了两个动态地址,A是jdbc:mysql://localhost:3306/a?useUnicode=true&characterEncoding=UTF-8,B是jdbc:mysql://212.23.33.23:3306/b?useUnicode=true&characterEncoding=UTF-8, 启动项目的时候default是被自动加载的,而A、B则是运行期间动态切换的,切换的时候就会出问题,A可以正常链接,B则被拒绝。
willowyxd 2013-07-03
  • 打赏
  • 举报
回复
new出来的datasource看似没什么不同,如果第二数据源数据库里面存的和properties里面设置的ip地址是一样的,只是default scheme不同,是可以连接的;如果新数据源是另一个ip,在方法public BasicDataSource createDataSource(String driverClassName, String url, String username, String password)里面加datasource.getConnection()测试的时候也会报上面一样的错,获取不到connection链接。
a147963147963 2013-07-03
  • 打赏
  • 举报
回复
换sqlserver试看看,还不行的话建议去断点看下spring给你的default target datasource对象和你自己new出来的datasource属性有啥不同。。。
willowyxd 2013-07-03
  • 打赏
  • 举报
回复
用户名、密码、链接地址都正确,default target datasource直接配成这些属性是可以连通的。
kimmking 2013-07-02
  • 打赏
  • 举报
回复
或者没有授权。
kimmking 2013-07-02
  • 打赏
  • 举报
回复
Access denied for user 'root'@'56.108.114.130' (using password: YES) 用户名密码不对。
willowyxd 2013-07-02
  • 打赏
  • 举报
回复
分布式环境,有多少数据库服务器不一定,是什么类型的数据库也不一定,所以我维护的是驱动名称、地址、用户名、密码,需要动态创建数据源点并且建立链接
小丑哥_V5 2013-07-02
  • 打赏
  • 举报
回复
你应该存在各种数据源吧...例如db1,db2,db3...你的数据库表表里面存放这个数据源名称就可以了,如果不使用这种jndi方式,你自己链接通过普通的jdbc读取你的那个表的记录信息来获取connection,然后lookup就可以拉,主要是做好事务控制就可以了,思路大概就这样咯...

67,513

社区成员

发帖
与我相关
我的任务
社区描述
J2EE只是Java企业应用。我们需要一个跨J2SE/WEB/EJB的微容器,保护我们的业务核心组件(中间件),以延续它的生命力,而不是依赖J2SE/J2EE版本。
社区管理员
  • Java EE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧