1. 定时检查数据库连接是否可用(手动切换)

​ 昨天生产库一台装有数据库的服务器关机了。虽然另一台服务器上还有服务,但是共用的一个数据库。所以。。。主服务器挂了另一台也没啥用。

​ 所有为避免再次出现这种情况,决定配置双数据库。我的需求是主数据库服务挂掉之后,副数据库能随时顶上。废话不多说,直接上代码

1. applicationContext-hibernate.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"xmlns:task="http://www.springframework.org/schema/task"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsdhttp://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.1.xsd"><context:component-scan base-package="cn.com.itsea.insurance"><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/></context:component-scan><bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"><property name="fileEncoding" value="UTF-8"/><property name="locations"><list><value>classpath:jdbc.properties</value></list></property></bean><!-- 主数据库配置 --><bean id="primary_dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"><property name="url" value="${primary.jdbc.url}" /><property name="driverClassName" value="${primary.jdbc.driverClassName}" /><property name="username" value="${primary.jdbc.username}" /><property name="password" value="${primary.jdbc.password}" /><property name="initialSize" value="20" /><property name="minIdle" value="20" /><property name="maxActive" value="200" /><property name="maxWait" value="60000" /><property name="timeBetweenEvictionRunsMillis" value="60000" /><property name="minEvictableIdleTimeMillis" value="300000" /><property name="validationQuery" value="SELECT 1 FROM DUAL" /><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" /><property name="filters" value="stat" /></bean><!-- 副数据库配置 -->   <bean id="second_dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"><property name="url" value="${second.jdbc.url}" /><property name="driverClassName" value="${second.jdbc.driverClassName}" /><property name="username" value="${second.jdbc.username}" /><property name="password" value="${second.jdbc.password}" /><property name="initialSize" value="20" /><property name="minIdle" value="20" /><property name="maxActive" value="200" /><property name="maxWait" value="60000" /><property name="timeBetweenEvictionRunsMillis" value="60000" /><property name="minEvictableIdleTimeMillis" value="300000" /><property name="validationQuery" value="SELECT 1 FROM DUAL" /><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" /><property name="filters" value="stat" /></bean><!-- 编写spring 配置文件的配置多数源映射关系 --><bean id="dataSource" class="cn.com.itsea.insurance.config.DynamicDataSourceConfig"><property name="targetDataSources"><map key-type="java.lang.String"><entry key="primary_dataSource" value-ref="primary_dataSource"></entry><entry key="second_dataSource" value-ref="second_dataSource"></entry></map></property><property name="defaultTargetDataSource" ref="primary_dataSource"></property></bean><bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"><property name="dataSource" ref="dataSource" /><property name="packagesToScan"><list><value>cn.com.itsea.insurance.model</value></list></property><property name="hibernateProperties"><props><prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop><prop key="hibernate.show_sql">false</prop><prop key="hibernate.format_sql">false</prop></props></property></bean><aop:aspectj-autoproxy expose-proxy="true"/><tx:annotation-driven proxy-target-class="true" transaction-manager="txManager"/><bean id="txManager"class="org.springframework.orm.hibernate4.HibernateTransactionManager"><property name="sessionFactory" ref="sessionFactory"/></bean><tx:advice id="txAdvice" transaction-manager="txManager"><tx:attributes><tx:method name="save*" propagation="REQUIRED" /><tx:method name="add*" propagation="REQUIRED" /><tx:method name="create*" propagation="REQUIRED" /><tx:method name="insert*" propagation="REQUIRED" /><tx:method name="update*" propagation="REQUIRED" /><tx:method name="merge*" propagation="REQUIRED" /><tx:method name="del*" propagation="REQUIRED" /><tx:method name="remove*" propagation="REQUIRED" /><tx:method name="put*" propagation="REQUIRED" /><tx:method name="use*" propagation="REQUIRED"/><!--hibernate4必须配置为开启事务 否则 getCurrentSession()获取不到--><tx:method name="get*" propagation="REQUIRED" read-only="true" /><tx:method name="count*" propagation="REQUIRED" read-only="true" /><tx:method name="find*" propagation="REQUIRED" read-only="true" /><tx:method name="list*" propagation="REQUIRED" read-only="true" /><tx:method name="*" read-only="true" /></tx:attributes></tx:advice><aop:config expose-proxy="true"><!-- 只对业务逻辑层实施事务 --><aop:pointcut id="txPointcut" expression="execution(* cn.com.itsea.insurance..service..*.*(..))" /><aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/></aop:config>
</beans>
2. jdbc.xml
# Properties file with JDBC-related settings.
# Applied by PropertyPlaceholderConfigurer from "dataAccessContext-local.xml".
# Targeted at system administrators, to avoid touching the context XML files.#Hangzhou
primary.jdbc.driverClassName=oracle.jdbc.OracleDriver
primary.jdbc.url=jdbc:oracle:thin:@192.168.1.2:1521:RLSBDB
primary.jdbc.username=root
primary.jdbc.password=rootsecond.jdbc.driverClassName=oracle.jdbc.OracleDriver
second.jdbc.url=jdbc:oracle:thin:@192.168.1.3:1521:RLSBDB
second.jdbc.username=root
second.jdbc.password=root
3. DynamicDataSourceConfig.java
package cn.com.itsea.insurance.config;import cn.com.itsea.insurance.utils.LogUtils;
import cn.hutool.core.util.StrUtil;
import com.google.common.base.Stopwatch;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;@Slf4j
public class DynamicDataSourceConfig extends AbstractRoutingDataSource implements ApplicationContextAware {private static final String PRIMARY_DATASOURCE = "primary_dataSource";private static final String SECOND_DATASOURCE = "second_dataSource";private static String USER_DATASOURCE = PRIMARY_DATASOURCE;private static final long SLEEP_TIME = 1000 * 60;private static final Lock lock = new ReentrantLock();private static ApplicationContext applicationContext = null;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}@Overrideprotected Object determineCurrentLookupKey() {return USER_DATASOURCE;}public void initialContext(){new Thread(()->{while (true){try {Thread.sleep(SLEEP_TIME);dynamicallySwitchDataSources();} catch (InterruptedException e) {e.printStackTrace();}}}).start();}/*** 监听当前使用数据源是否可用* 如果不可用切换数据源* 默认为主数据源*/private void dynamicallySwitchDataSources() {// 是否需要切换数据源boolean isItSwitchDataSources = false;Stopwatch stopwatch = Stopwatch.createStarted();StringBuilder logUtil = new StringBuilder("dataSources connection check");lock.lock();try {// 当前使用的数据源类型String userDataSourceType = (String) determineCurrentLookupKey();logUtil.append(StrUtil.format("userDataSourceType={}",userDataSourceType));boolean primaryDatasource = dataSourceIsClosed(PRIMARY_DATASOURCE);boolean secondDatasource = dataSourceIsClosed(SECOND_DATASOURCE);// 主数据源未关闭且正在使用副数据源if(!primaryDatasource && !USER_DATASOURCE.equals(PRIMARY_DATASOURCE)){USER_DATASOURCE = PRIMARY_DATASOURCE;logUtil.append(StrUtil.format("userDataSourceType Switch to {}",userDataSourceType));return;}// 主数据源已关闭且副数据源保持连接if(primaryDatasource && !secondDatasource && !USER_DATASOURCE.equals(SECOND_DATASOURCE)){USER_DATASOURCE = SECOND_DATASOURCE;logUtil.append(StrUtil.format("userDataSourceType Switch to {}",userDataSourceType));return;}}catch (Exception e){e.printStackTrace();logUtil.append(LogUtils.getTrace(e));}finally {// 日志处理logUtil.append(StrUtil.format("cost.time={}",stopwatch.elapsed(TimeUnit.MILLISECONDS)));log.info(logUtil.toString());lock.unlock();}}/*** 数据源是否关闭* @param dataSourceType DataSource 类型* @return*/private boolean dataSourceIsClosed(String dataSourceType){DataSource dataSource = (DataSource) applicationContext.getBean(dataSourceType);if(null == dataSource){return true;}try {Connection connection = dataSource.getConnection();boolean isClosed = connection.isClosed();if(isClosed){return true;}} catch (SQLException e) {e.printStackTrace();log.error(StrUtil.format("dataSourceType={} connection closed",dataSourceType));return true;}return false;}
}
4. SpringContextHolder.java
package cn.com.itsea.insurance.utils;import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;/*** 以静态变量保存Spring ApplicationContext, 可在任何代码任何地方任何时候取出ApplicaitonContext.* @author <a href="mailto:912349441@qq.com">tangzx</a>* @version V1.0* @ClassName: SpringContextHolder* @date 创建时间:2019-12-2 10:37:44* @since JDK 1.7*/
@Service
@Lazy(false)
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {private static ApplicationContext applicationContext = null;private static Logger logger = LoggerFactory.getLogger(SpringContextHolder.class);/*** 取得存储在静态变量中的ApplicationContext.*/public static ApplicationContext getApplicationContext() {assertContextInjected();return applicationContext;}/*** 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.*/@SuppressWarnings("unchecked")public static <T> T getBean(String name) {assertContextInjected();return (T) applicationContext.getBean(name);}/*** 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.*/public static <T> T getBean(Class<T> requiredType) {assertContextInjected();return applicationContext.getBean(requiredType);}/*** 清除SpringContextHolder中的ApplicationContext为Null.*/public static void clearHolder() {if (logger.isDebugEnabled()) {logger.debug("清除SpringContextHolder中的ApplicationContext:" + applicationContext);}applicationContext = null;}/*** 实现ApplicationContextAware接口, 注入Context到静态变量中.*/@Overridepublic void setApplicationContext(ApplicationContext applicationContext) {SpringContextHolder.applicationContext = applicationContext;}/*** 实现DisposableBean接口, 在Context关闭时清理静态变量.*/@Overridepublic void destroy() throws Exception {SpringContextHolder.clearHolder();}/*** 检查ApplicationContext不为空.*/private static void assertContextInjected() {Validate.validState(applicationContext != null,"applicaitonContext属性未注入, 请在applicationContext.xml中定义SpringContextHolder.");}
}

需要在系统启动完成之后,需要调用 initialContext 方法

SpringContextHolder.getBean(DynamicDataSourceConfig.class).initialContext();

每隔一段时间获取主数据源连接心跳,如果连接不可用切换到副数据库。等到主数据库启动之后再切换回去。

2. 动态切换

2.1 DynamicDataSourceConfig

package cn.com.itsea.insurance.config;import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;public class DynamicDataSourceConfig extends AbstractRoutingDataSource {@Overrideprotected Object determineCurrentLookupKey() {return DynamicDataSourceHolder.getDataSourceType();}}

2.2 DynamicDataSourceHolder.java

package cn.com.itsea.insurance.config;public class DynamicDataSourceHolder {// 线程本地环境private static final ThreadLocal contextHolder = new ThreadLocal();// 设置数据源类型public static void setDataSourceType(String dataSourceType) {contextHolder.set(dataSourceType);}// 获取数据源类型public static String getDataSourceType() {return (String) contextHolder.get();}// 清除数据源类型public static void clearDataSourceType() {contextHolder.remove();}}

程序会随机访问两个数据库(建议数据库之间做了主从同步)

DynamicDataSourceHolder.setDataSourceType("second_dataSource");

转载请注明:https://blog.csdn.net/CSDN_Ty/article/details/108519274

spring + hibernate + 双数据源 动态(或手动)切换相关推荐

  1. Spring+Hibernate双数据源测试Mysql集群读写分离(转自http://blog.csdn.net/hzw2312/article/details/9083519)

    进行测试!环境就是SH框架.当然这只是一个简单的测试! 准备环境就是Spring框架跟Hibernate框架的整合! 然后在Spring配置文件中配置两个数据源.这里我采用的是从c3po数据源配置: ...

  2. Spring 与 Log4J 进行动态日志配置切换

    引言:     在开发与生产环境中,我们有时候需要对日志的配置进行动态切换,要调试.监控和检查系统的运行时信息.     一般有两种方法     1.通过 Spring 的 Log4jConfigLi ...

  3. 用Spring与Log4J进行动态日志配置切换

    利用 Spring 与 Log4J 巧妙地进行动态日志配置切换并立即生效 引言: 在开发与生产环境中,我们有时候需要对日志的配置进行动态切换,要调试.监控和检查系统的运行时信息. 一般有两种方法 1. ...

  4. spring boot多数据源动态切换, 多数据源事务管理

    1 . 项目目标 实现 不同数据源的切换 (使用AbstractRoutingDataSource) 不同数据源之间,事物管理,多个数据源之间同时commit或者同时rollback 兼容不同的连接池 ...

  5. mysql双数据源动态切换_Spring boot+Mybatis多数据源动态切换

    通过重写AbstractRoutingDataSource类中方法determineTargetDataSource,determineCurrentLookupKey,afterProperties ...

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

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

  7. linux虚拟机双显卡,LINUX下手动切换双显卡或禁用其中一块显卡的解决办法

    首先声明下,该文属于参考网络其他相关资料所撰,本人对原资料作了很多测试和实验,下列办法仅适用本人的系统环境,其他仅供参考. ====================================== ...

  8. 动态切换数据源(spring+hibernate)

    起因:在当前我手上的一个项目中需要多个数据源,并且来自于不同类型的数据库... 因为很多历史原因.这个项目的住数据源是MySQL,整个系统的CURD都是操作的这个数据库. 但是还有另外两个用于数据采集 ...

  9. Spring+Mybatis多数据源配置(四)——AbstractRoutingDataSource实现数据源动态切换

    欢迎支持笔者新作:<深入理解Kafka:核心设计与实践原理>和<RabbitMQ实战指南>,同时欢迎关注笔者的微信公众号:朱小厮的博客. 欢迎跳转到本文的原文链接:https: ...

最新文章

  1. 关于equals与hashcode的重写
  2. 爱立信总裁表示欧洲网络始终趋于落后,网站推广之下5G发展需加快步伐
  3. Java - 基本语法
  4. 安卓电视版linux,MythTV 30.0 发布,前端支持选择Android电视设备
  5. 寻找百度翻译参数,实现百度翻译
  6. Nginx 服务并发过10万的Linux内核优化配置
  7. 【云栖大会】“天”与“人”的大数据
  8. 【Android综合编程】CH2EventBus
  9. Tapestry5的基础知识
  10. (7) PyQt 设计并实现【工厂扫码装箱系统】- Python代码实现BarTender自动化打印条码标签
  11. 人工智能——支持向量机(SVM)思维导图
  12. 【经典传颂】人月神话The Mythical Man-Month
  13. 【Python | 入门】 从输出打印到面对对象(五分钟速通Python)
  14. QueryDSL 大于、小于、大于等于、小于等于、等于、不等于方法
  15. window7中出现 你需要权限来执行此操作 的解决方法
  16. 初窥门径:认识C语言
  17. 纺织品有害物质三项检测
  18. iOS NSFileManager文件管理(沙盒)
  19. 学习平面设计的三个入门步骤
  20. One_hot和Word2Vec两种词向量方法的原理及比较

热门文章

  1. 微信小程序使用Symbol类型彩色图标(symbol引用)(iconfont)
  2. DOM操作之getAttribute方法
  3. python getattr和getattribute_Python的__getattr__和__getattribute__
  4. 生产图纸管理防泄密解决方案,完整的图纸安全管理体系
  5. linux命令 ll 搜索_linux命令——ll详解
  6. 为 Ubuntu22.04 系统添加中文输入法
  7. 全迹科技推出与iPhone适配的UWB标签授权合作
  8. 生日祝福小程序_96华诞,祝我的海大生日快乐!
  9. 什么产品适合做私域流量?品牌都适合做私域吗
  10. 关于Python模块安装和卸载的几种方法,第一种初学者常用...