title: Hibernate动态数据源

使用目的

出于在审计厅项目建设的需求,我们在项目建设工程先是使用了单一的数据库,经过三个月的代码编写,完成了单机的项目部署,在经过两台loadRunner进行2k的并发访问时,发现数据库的写日志缓冲区已经爆满,导致系统宕机。后来在老师的决策下将数据库分库存储,不同地区的数据利用切分工具进行数据的切分,然后使用ETL、dts配合自己写的脚本完成数据的迁移和各种角色、存储过程、权限的设置。

数据是分开存放了,那如何在保证单机系统的可运行的情况下,使用多数据源呢。经过调研,我们发现hibernate+spring可以使用多数据源,可以在使用中动态切换数据源。

数据源切换的步骤

我们的项目中用到了17个私有数据库实例,117个模式。1个公共数据库实例。

如上图所示,我们系统的数据源的切换过程大概是这种模型。刚开始所有用户的登录数据都是由公共数据源进行统一管理的,后面的审计数据的查询,自由上传的辅助数据都是存放在用户的私有数据源中的。

动态数据源切换的实现

编写动态数据源类

利用springAbstractRoutingDataSource来实现动态数据源的获取。AbstractRoutingDataSource,它的一个作用就是可以根据用户发起的不同请求去转换不同的数据源,比如根据用户的不同地区语言选择不同的数据库


import java.sql.SQLException;import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;public class DynamicDataSource extends AbstractRoutingDataSource {// static Logger log = Logger.getLogger("DynamicDataSource");@Overrideprotected Object determineCurrentLookupKey() {return DbContextHolder.getDbType();}@Overridepublic boolean isWrapperFor(Class<?> iface) throws SQLException {return false;}@Overridepublic <T> T unwrap(Class<T> iface) throws SQLException {return null;}
}

补充:dataSource.properties文件内容

DRIVER_NAME    =    xx.xx.xxx.xxxxDATABASE_PASSWORD =  xxxxx
DATABASE_USER     =  xxxx
#私有数据源
DATABASE_URL_DEFAULT=jdbc:xx://localhost
#共有数据源
DATABASE_URL_COMMON=jdbc:xx://localhost

利用Druid实现私有数据源和公共数据源

    <bean id="dataSource_common" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"><property name="driverClassName" value="${DRIVER_NAME}" /><property name="url" value="${DATABASE_URL_DEFAULT}" /><property name="username" value="${DATABASE_USER}" /><property name="password" value="${DATABASE_PASSWORD}" /><property name="filters" value="stat" /><property name="maxActive" value="20" /><property name="initialSize" value="1" /><property name="maxWait" value="60000" /><property name="minIdle" value="1" /><property name="timeBetweenEvictionRunsMillis" value="3000" /><property name="minEvictableIdleTimeMillis" value="300000" /><property name="validationQuery" value="SELECT 'x'" /><property name="testWhileIdle" value="true" /><property name="testOnBorrow" value="false" /><property name="testOnReturn" value="false" /><property name="poolPreparedStatements" value="true" /><property name="maxPoolPreparedStatementPerConnectionSize" value="20" /></bean><bean id="dataSource_default" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"><property name="driverClassName" value="${DRIVER_NAME}" /><property name="url" value="${DATABASE_URL_COMMON}" /><property name="username" value="${DATABASE_USER}" /><property name="password" value="${DATABASE_PASSWORD}" /><property name="filters" value="stat" /><property name="maxActive" value="20" /><property name="initialSize" value="1" /><property name="connectionProperties" value="druid.stat.slowSqlMillis=5000" /><property name="maxWait" value="60000" /><property name="minIdle" value="1" /><property name="timeBetweenEvictionRunsMillis" value="3000" /><property name="minEvictableIdleTimeMillis" value="300000" /><property name="validationQuery" value="SELECT 'x'" /><property name="testWhileIdle" value="true" /><property name="testOnBorrow" value="false" /><property name="testOnReturn" value="false" /><property name="poolPreparedStatements" value="true" /><property name="maxPoolPreparedStatementPerConnectionSize" value="20" /></bean>

声明动态数据源springBean

map中可以存放多个数据源,在使用的时候指定key就可以获取对应的数据源了.

<bean id="dynamicDataSource" class="dao.DynamicDataSource"><property name="targetDataSources"><map key-type="java.lang.String"><!--私有数据源--><entry key="default" value-ref="dataSource_default" /><!--公共数据源--><entry key="common" value-ref="dataSource_common" /></map></property><!--默认的数据源--><property name="defaultTargetDataSource" ref="dataSource_default" />
</bean>

配置Hibernate的SessionFactory

这里是在spring的配置文件中配置Hibernate的SessionFactorty,这里使用的dataSource已经是动态的数据源了。

    <!-- 配置会话工厂 --><bean id="sessionFactory"class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"><!-- 设置数据源 --><property name="dataSource" ref="dynamicDataSource" /><!-- 接管了hibernate对象映射文件 --><property name="mappingResources"><list><value>xxx/xxx.hbm.xml</value></list></property><!--指定hibernate的属性值 --><property name="hibernateProperties"><props><prop key="hibernate.dialect">org.hibernate.dialect.xxx</prop><!-- <prop key="hibernate.hbm2ddl.auto">update</prop> --><prop key="hibernate.show_sql">false</prop><prop key="hibernate.format_sql">true</prop><!-- --><prop key="javax.persistence.validation.mode">none</prop><prop key="connection.driver_class">xx.xxx.xx.xx</prop></props></property></bean>

测试数据源是否可以连接成功

这里使用的是基于注解的测试类,测试的运行结果如下图所示。

import dao.DynamicDataSource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import java.sql.SQLException;/*** Created by frank on 16-5-30.*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class NIuTest {@AutowiredApplicationContext context;@Testpublic void testContext() throws SQLException {DynamicDataSource ds = (DynamicDataSource) context.getBean("dynamicDataSource");System.out.println("loginTimeOutValue:"+ds.getLoginTimeout());}}

创建线程私有的数据源上下文

创建一个数据源上下文持有者,该类使用了ThreadLocal能够保证线程私有,使得不同地区的用户访问时,不会出现数据源冲突。

public class DbContextHolder {private static final ThreadLocal contextHolder = new ThreadLocal();public static final String DATA_SOURCE_DEFAULT = "default";public static final String DATA_SOURCE_COMMON = "common";public static void setDbType(String dbType) {contextHolder.set(dbType);}public static String getDbType() {return (String) contextHolder.get();}public static void clearDbType() {contextHolder.remove();}}

在程序运行时,如何切换数据源

service类中调用Hibernate进行数据操作之前,如果需要切换数据源到私有的数据源,可以调用下面的方法.然后springDynamicDataSource实例则会调用determineCurrentLookupKey方法进行数据源的切换。

DbContextHolder.setDbType(DbContextHolder.DATA_SOURCE_COMMON);

一个更为实际的例子如下,如果我们想要获取某条用户输入的sql语句可以得到多少条数据,那么我们需将数据源切换到用户私有的数据源。

    public long getAllMessageCount_Common(String sql)throws HibernateException, NullPointerException, SQLException {DbContextHolder.setDbType(DbContextHolder.DATA_SOURCE_COMMON);StringBuilder hql = null;hql = new StringBuilder("SELECT count(*) from (");hql.append(sql);hql.append(")");// logger.info("得到的sql语句:" + hql);long result = basicDao.executeQueryCountSQL_throwEX(hql.toString());logger.info("后台得到的数据count:" + result);return result;}

从上面可以看出,我们使用的是DbContextHolder.setDbType()方法来动态的进行数据源的切换。

总结

数据源的动态切换就整理到这里,项目已经做了很久了,今天在整理项目中使用到的知识时,想到这块自己可以整理一下,才写此文作为记录吧。

Hibernate动态数据源相关推荐

  1. 动态数据源切换--AbstractRoutingDataSource

    转载自http://blog.csdn.net/x2145637/article/details/52461198 在Spring 2.0.1中引入了AbstractRoutingDataSource ...

  2. Spring 的动态数据源实现

    1. 配置多个数据源 这里以两个c3p0数据库连接池的数据源作为实例.在Spring框架下使用c3p0的数据库需要加入c3p0-0.9.1.2.jar(现在最新的)这个支持包.这里以数据同步项目为例: ...

  3. springboot动态切换数据源_Springboot整合Mybatis注解实现动态数据源切换

    AbstractRoutingDataSource AbstractRoutingDataSource是spring-jdbc包提供的一个了AbstractDataSource的抽象类,它实现了Dat ...

  4. 多数据源/动态数据源的解决方案

    多数据源/动态数据源的解决方案 一. DAO层的解决方案 1.配置数据源 2.创建切点注解 3.AbstractRoutingDataSource 4.读取数据源配置 5.切面拦截 6.使用验证 二. ...

  5. SpringBoot2/SpringBoot/Java动态数据源配置、动态连接池配置、多数据源负载均衡

    Java动态数据源配置.动态连接池配置.多数据源负载均衡 大家好,今天给大家推荐一个自产的连接池插件.废话不多说,本文接口分为以下主题: 1. 插件开发背景: 2. 插件提供的能力: 3. 插件的使用 ...

  6. Spring+hibernate多数据源多sessionFactory配置

    主配置文件 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http:// ...

  7. nacos动态配置数据源_Jasper 怎么配置动态数据源

    Jasper 本身是不支持动态数据源的,能用的解决方式是通过 api 自定义数据源,实际操作就是根据条件判断后动态设定 jdbc 的 url.用户名及密码等连接属性.比如: String userNa ...

  8. Spring 下,关于动态数据源的事务问题的探讨

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 作者:青石路 cnblogs.com/youzhibing ...

  9. Spring Boot 动态数据源(多数据源自己主动切换)

    本文实现案例场景: 某系统除了须要从自己的主要数据库上读取和管理数据外.另一部分业务涉及到其它多个数据库,要求能够在不论什么方法上能够灵活指定详细要操作的数据库. 为了在开发中以最简单的方法使用,本文 ...

最新文章

  1. python课时费_python(课时1)
  2. 【290】Python 函数
  3. springboot中使用ApplicationListener和ApplicationEvent /@EventListener监听事件
  4. python进程数上限_python-使用multiprocessing.Process并发进程数最多
  5. 微信公众平台开发(44)历史上的今天
  6. HTML <dfn> 标签的简单介绍
  7. RocketMQ消息的事务架构设计
  8. 图计算-Pregel-Hama
  9. 常用算法一(分治算法)
  10. 阿里云CentOS Linux 7安全基线检查
  11. C语言学习资料和视频
  12. vue+element表格 苹果自带浏览器兼容问题
  13. xshell 导入.xsh 文件
  14. Couldn‘t resolve host
  15. 如何把微信删除的聊天记录恢复正常?你知道吗
  16. 电子稳定程序系统--ESP
  17. MTK平台修改开机动画,开机logo
  18. 2018.12.28
  19. “知乎女神”诈骗被人肉,一场个人隐私权与网络监督权的博弈
  20. vue-video-play 记载视频观看时间和还原视频进度

热门文章

  1. vue项目中使用echarts和china.js实现中国地图
  2. Elasticsearch 7.X SpringBoot 使用 ElasticsearchRestTemplate 操作 ES
  3. 关于window.print打印分页功能
  4. 2021-2027中国电气设备监测市场现状及未来发展趋势
  5. 计算机色盲模式在哪,Win10系统怎么设置开启色盲模式
  6. 面对即将来临的995,京东员工明确表示难受了……
  7. 职高女生能学计算机吗,职高有哪些专业适合女生
  8. 【解决方案】Microsoft Visual C++ 14.0 is required
  9. java简介以及环境安装
  10. hive 非正确json格式字段造成查询错误