MyBatis多数据源配置实现读写分离 发表于 2017-09-29 | 分类于 Database | 常见的数据库连接池有C3P0、DBCP和阿里巴巴的druid,后两个在实际场景中用的比较多
来源:互联网 发布:java中static修饰变量 编辑:程序博客网 时间:2024/06/06 02:25
MyBatis多数据源配置实现读写分离
常见的数据库连接池有C3P0、DBCP和阿里巴巴的druid,后两个在实际场景中用的比较多,这个案例简单介绍Spring+Druid+MyBatis
实现多数据源配置,基本原理是继承自Spring提供的AbstractRoutingDataSource这个抽象类,把所有的DataSource放到Map里面,
然后重写determineCurrentLookupKey()这个方法,Spring的AbstractRoutingDataSource在获取数据库连接时会先调用
determineCurrentLookupKey()方法来找到数据库的key值,然后从Map中找到对应的DataSource获取数据库连接。
数据源配置
数据库连接池我用的阿里的druid,首先配置一个数据源的父类,定义一些公共的连接池参数,然后配置了两个继承自AbstractDataSource的
读写datasource,配置如下所示:
12345678910111213141516171819202122232425262728293031323334353637383940
<!-- applicationContext.xml --><!-- 数据源参数配置 --><context:property-placeholder location="classpath:datasource.properties" /><bean id="abstractDataSource" abstract="true" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <!-- 基本属性 url、user、password --> <!-- 配置初始化大小、最小、最大 --> <property name="initialSize" value="10"/> <property name="minIdle" value="10"/> <property name="maxActive" value="10"/> <!-- 配置获取连接等待超时的时间 --> <property name="maxWait" value="6000"/> <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --> <property name="timeBetweenEvictionRunsMillis" value="60000"/> <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --> <property name="minEvictableIdleTimeMillis" value="300000"/> <property name="validationQuery" value="SELECT 1"/> <property name="testWhileIdle" value="true"/> <property name="testOnBorrow" value="false"/> <property name="testOnReturn" value="false"/> <!-- 打开PSCache,并且指定每个连接上PSCache的大小 --> <property name="poolPreparedStatements" value="true"/> <property name="maxPoolPreparedStatementPerConnectionSize" value="20"/> <property name="filters" value="config"/> <property name="connectionProperties" value="config.decrypt=false" /> </bean> <bean id="DataSourceRead" parent="abstractDataSource"> <property name="url" value="${read_url}"/> <property name="username" value="${read_userName}"/> <property name="password" value="${read_userValue}"/> </bean> <bean id="DataSourceWrite" parent="abstractDataSource"> <property name="url" value="${write_url}"/> <property name="username" value="${write_userName}"/> <property name="password" value="${write_userValue}"/> </bean>
然后实现一个类RWDataSource继承自AbstractRoutingDataSource,readDataSource和writeDataSource通过Spring注入,然后重写
afterPropertiesSet()和determineCurrentLookupKey()这两个方法,关键代码如下,setter和getter方法我这里省略了。
12345678910111213141516171819202122232425262728293031323334
public class RWDataSource extends AbstractRoutingDataSource { private Object writeDataSource; //写数据源 private Object readDataSource; //读数据源 public void afterPropertiesSet() { if (this.writeDataSource == null) { throw new IllegalArgumentException("Property 'writeDataSource' is required"); } setDefaultTargetDataSource(writeDataSource); Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put(RWDataSourceType.WRITE.name(), writeDataSource); if(readDataSource != null) { targetDataSources.put(RWDataSourceType.READ.name(), readDataSource); } //调用父类的方法把数据源注入 setTargetDataSources(targetDataSources); super.afterPropertiesSet(); } protected Object determineCurrentLookupKey() { RWDataSourceType dynamicDataSourceGlobal = RWDataSourceHolder.getDataSource(); if(dynamicDataSourceGlobal == null || dynamicDataSourceGlobal == RWDataSourceType.WRITE) { return RWDataSourceType.WRITE.name(); } return RWDataSourceType.READ.name(); }
determineCurrentLookupKey()方法这里主要是从RWDataSourceHolder这个类里取出RWDataSourceType(枚举类,包含Read和Write),
RWDataSourceHolder类里只有一个ThreadLocal的RWDataSourceType对象,用于保存每个线程选择的数据源,在使用Mybatis时要
根据执行的SQL语句类型动态修改当前线程的RWDataSourceType。
1234567891011
public class RWDataSourceHolder { private static final ThreadLocal<RWDataSourceType> holder = new ThreadLocal<RWDataSourceType>(); public static void putDataSource(RWDataSourceType dataSource){ holder.set(dataSource); } public static RWDataSourceType getDataSource(){ return holder.get(); }}
接下来就是重点了,怎么根据MyBatis要执行的语句类型来动态修改数据源类型呢,这里就要用到MyBatis提供的插件的能力,MyBatis
里的数据库增删改查操作最后都是执行的Executor的query()或者update()方法,因此我们需要做的就是拦截Executor的query和update
方法,根据执行的SQL语句类型来动态修改数据源的key值,插件的代码如下:
MyBatis拦截插件
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
({ "update", args = { (type = Executor.class, method = MappedStatement.class, Object.class }), "query", args = { (type = Executor.class, method = MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class }) })public class RWDataSourceMybatisPlugin implements Interceptor { private static final String REGEX = ".*insert\\u0020.*|.*delete\\u0020.*|.*update\\u0020.*"; private static final Map<String, RWDataSourceType> cacheMap = new ConcurrentHashMap<>(); public Object intercept(Invocation invocation) throws Throwable { Object[] objects = invocation.getArgs(); MappedStatement ms = (MappedStatement) objects[0]; RWDataSourceType dataSourceType = null; if((dataSourceType = cacheMap.get(ms.getId())) == null) { //读方法 if(ms.getSqlCommandType().equals(SqlCommandType.SELECT)) { //!selectKey 为自增id查询主键(SELECT LAST_INSERT_ID() )方法,使用主库 if(ms.getId().contains(SelectKeyGenerator.SELECT_KEY_SUFFIX)) { dataSourceType = RWDataSourceType.WRITE; } else { BoundSql boundSql = ms.getSqlSource().getBoundSql(objects[1]); String sql = boundSql.getSql().toLowerCase().replaceAll("[\\t\\n\\r]", " "); if(sql.matches(REGEX)) { dataSourceType = RWDataSourceType.WRITE; } else { dataSourceType = RWDataSourceType.READ; } } }else{ dataSourceType = RWDataSourceType.WRITE; } cacheMap.put(ms.getId(), dataSourceType); } //修改当前线程要选择的数据源的key RWDataSourceHolder.putDataSource(dataSourceType); return invocation.proceed(); } public Object plugin(Object target) { if (target instanceof Executor) { return Plugin.wrap(target, this); } else { return target; } }
最后在mybatis的配置文件里配置拦截插件就可以了,通过以上的步骤就实现了数据库读写分离的功能,有些步骤我省略了,有疑问的可以给我留言,
晚一点我把附件上传上来。
12345
<!-- mybatis-config.xml --><plugins> <plugin interceptor="RWDataSourceMybatisPlugin"> </plugin></plugins>
- MyBatis多数据源配置实现读写分离 发表于 2017-09-29 | 分类于 Database | 常见的数据库连接池有C3P0、DBCP和阿里巴巴的druid,后两个在实际场景中用的比较多
- dbcp,c3p0,druid数据库连接池的浅析
- spring boot+mybatis+通用mapper+pageHelper+druid的多数据源(非读写分离)
- c3p0,druid,dbcp的性能比较
- c3p0和dbcp进行配置数据库连接池上的区别
- 数据库连接池 (DBCP、c3p0、Druid) 配置说明和对比
- C3P0和DBCP数据库连接池的
- 数据库连接池优化配置(druid,dbcp,c3p0)
- 数据库连接池优化配置(druid,dbcp,c3p0)
- dbcp、c3p0、druid连接池的简单配置
- 数据库连接池的使用(C3P0实现多数据源的数据库连接池)
- c3p0和dbcp的比较
- spring下,druid,c3p0,proxool,dbcp四个数据连接池的使用和配置
- asp.net2.0中用于数据库连接的配置
- MyBatis多数据源配置实现读写分离
- 数据库链接池的使用,对应于多数据库(用c3p0实现Apache—DBUtils框架)
- java-durid、mybatis、spring 整合基于 AbstractRoutingDataSource 的多数据源读写分离配置
- MyBatis多数据源配置(读写分离)
- 如何判断js的数据类型
- 大端小端
- 每天一python 题 0006
- springboot的注解@EnableAspectJAutoProxy讲解
- MySQL查看数据库/表所占磁盘空间大小
- MyBatis多数据源配置实现读写分离 发表于 2017-09-29 | 分类于 Database | 常见的数据库连接池有C3P0、DBCP和阿里巴巴的druid,后两个在实际场景中用的比较多
- 图像处理
- Linux下的crontab定时执行任务命令详解
- 32位centos运行yum报错:There was a problem importing one of the Python modules
- 单链表基本操作实现
- HDOJ 1009 FatMouse' Trade
- Hadoop DistributedCache使用及原理
- LeetCode——Hamming Distance
- 重拾并再次养成记录的好习惯