数据库配置为一个主库 多个从库 主库用于写操作 从库只读操作
读写分离实现即为配置两个数据源,一个用于读写 连接主库 假设为ds_wr,一个用于只读 连接从库 假设为ds_r。
对数据库读操作时,操作ds_r数据源。
对数据源写操作时,操作ds_wr数据源。
读写分离可以有两种实现方式
第一种为写两套mappper
mapper写两套 一套用于读写 一套用于只读
<bean id="sqlSessionFactory_wr" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource_wr"/>
<!-- 自动扫描mapping.xml文件-->
<!--
<property name="mapperLocations" value="classpath:com/ifeng/auto/we_provider/mapping/*.xml" />
-->
<property name="mapperLocations" value="classpath:mapping1/*.xml"/>
</bean>
<bean id="sqlSessionFactory_r" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource_r"/>
<!-- 自动扫描mapping.xml文件-->
<!--
<property name="mapperLocations" value="classpath:com/ifeng/auto/we_provider/mapping2/*.xml" />
-->
<property name="mapperLocations" value="classpath:mapping/*.xml"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.ifeng.auto.we_provider.dao1"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory_wr"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.ifeng.auto.we_provider.dao2"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory_r"/>
</bean>
dao1包中mapper是读写方法 对应xml文件在mapping1文件夹中
dao2包中mapper是只读方法 对应xml文件在mapping2文件夹中
User user = userMapper2.get(1);
user.setName("Susan")
userMapper1.update(user);
第二种为通过实现AbstractRoutingDataSource类来动态管理数据源,本篇博客主要讲这部分内容
利用面向切面思维,每一次进入service方法前,选择数据源
首先pom.xml中添加依赖
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.7.4</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.7.4</version>
</dependency>
实现AbstractRoutingDataSource类 作为数据源
package com.ifeng.auto.we_provider.common.db;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* Created by Terry on 2016/7/23.
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceHolder.getDataSouce();
}
}
用ThreadLcoal管理当前数据源
package com.ifeng.auto.we_provider.common.db;
public class DynamicDataSourceHolder {
public static final ThreadLocal<String> holder = new ThreadLocal<String>();
public static void putDataSource(String name) {
holder.set(name);
}
public static String getDataSouce() {
return holder.get();
}
}
用注解的形式实现AOP管理数据源
package com.ifeng.auto.we_provider.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataSource {
String value();
}
切面类
package com.ifeng.auto.we_provider.common.proxy;
import com.ifeng.auto.we_provider.annotation.DataSource;
import com.ifeng.auto.we_provider.common.db.DynamicDataSourceHolder;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import java.lang.reflect.Method;
/**
* Created by Terry on 2016/7/23.
*/
public class DataSourceAspect {
public void before(JoinPoint point)
{
Object target = point.getTarget();
System.out.println(target.toString());
String method = point.getSignature().getName();
System.out.println(method);
Class<?> classz = target.getClass();
Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())
.getMethod().getParameterTypes();
try {
Method m = classz.getMethod(method, parameterTypes);
System.out.println(m.getName());
if (m != null && m.isAnnotationPresent(DataSource.class)) {
DataSource data = m.getAnnotation(DataSource.class);
DynamicDataSourceHolder.putDataSource(data.value());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
将注解放在service实现类的方法前,自动设置当前数据源为注解中数据源。
接下来配置aop以及数据源。
mybatis.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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<!-- 引入配置文件 -->
<context:property-placeholder location="classpath:db.properties" ignore-unresolvable="true"/>
<!-- 数据源配置 -->
<bean id="dataSource_wr" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
<property name="connectionProperties" value="${db.driver}"></property>
<!-- 配置初始化大小、最小、最大 -->
<property name="initialSize" value="1"/>
<property name="minIdle" value="1"/>
<property name="maxActive" value="20"/>
<!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="60000"/>
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="60000"/>
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="300000"/>
<property name="validationQuery" value="SELECT 'x'"/>
<property name="testWhileIdle" value="true"/>
<property name="testOnBorrow" value="true"/>
<property name="testOnReturn" value="false"/>
</bean>
<bean id="dataSource_r" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="${db1.url}"/>
<property name="username" value="${db1.username}"/>
<property name="password" value="${db1.password}"/>
<property name="connectionProperties" value="${db.driver}"></property>
<!-- 配置初始化大小、最小、最大 -->
<property name="initialSize" value="1"/>
<property name="minIdle" value="1"/>
<property name="maxActive" value="20"/>
<!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="60000"/>
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="60000"/>
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="300000"/>
<property name="validationQuery" value="SELECT 'x'"/>
<property name="testWhileIdle" value="true"/>
<property name="testOnBorrow" value="true"/>
<property name="testOnReturn" value="false"/>
</bean>
<bean id="dataSource" class="com.ifeng.auto.we_provider.common.db.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<!-- write -->
<entry key="write" value-ref="dataSource_wr"/>
<!-- read -->
<entry key="read" value-ref="dataSource_r"/>
</map>
</property>
<property name="defaultTargetDataSource" ref="dataSource_wr"/>
</bean>
<!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- 自动扫描mapping.xml文件-->
<!--
<property name="mapperLocations" value="classpath:com/ifeng/auto/we_provider/mapping/*.xml" />
-->
<property name="mapperLocations" value="classpath:mapping/*.xml"/>
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<aop:aspectj-autoproxy proxy-target-class="true"/>
<bean id="dataSourceAspect" class="com.ifeng.auto.we_provider.common.proxy.DataSourceAspect"/>
<aop:config>
<aop:aspect id="c" ref="dataSourceAspect">
<aop:pointcut id="tx" expression="execution(* com.ifeng.auto.we_provider.service..*.*(..))"/>
<aop:before pointcut-ref="tx" method="before"/>
</aop:aspect>
</aop:config>
<!-- DAO接口所在包名,Spring会自动查找其下的类 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.ifeng.auto.we_provider.dao"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
</beans>
在service实现类中
@DataSource("write")
public void savetag(UserTag userTag) {
userTagMapper.addUserTag(userTag);
}
@DataSource("read")
public UserTag getUserTagByUUID(String uuid) {
return userTagMapper.getUserTagByUUID(uuid);
}
至此 读写分离实现
分享到:
相关推荐
一般来说,读写分离有两种实现方式。第一种是依靠中间件MyCat,也就是说应用程序连接到中间件,中间件帮我们做SQL分离,去选择指定的数据源;第二种是应用程序自己去做分离。这里我用程序自己来做,主要是利用Spring...
JAVA数据库读写分离项目源码(MYSQL),非常详细的注解,ssm框架开发,能使你快速掌握读写分离技术!!!
突破Java面试(50)-MySQL读写分离及主从同步延时解决方案.docx
简单的redis读写分离demo 用的是依赖注入的方式,redis用的是主从复制
我感觉很不错的技术文档,现在和大家分享,希望能够帮到大家,如果你需要可以下载看看,很适合喜欢研究技术的人员
springboot+mybatis: 1,yml配置多个数据源 2,数据库主从配置 3,分包形式处理读写分离
最近项目要支持读写分离, 网上找了很多,但都是不太完整,我自己整理了下供大家参考。 我的项目使用的框架: springMvc+spring+hibernate+springJPA+maven, 数据库连接池用阿里的druid。
MySQL 读写分离 MySQL读写分离又一好办法 使用 com.mysql.jdbc.ReplicationDriver 在用过Amoeba 和 Cobar,还有dbware 等读写分离组件后,今天我的一个好朋友跟我讲,MySQL自身的也是可以读写分离的,因为他们提供了...
java mysql 读写分离demo mybatis作为数据库访问层,实现数据库读写分离的解决方案
使用JAVA aop技术,实现mysql 数据库读写分离(密码:111111)
mysql5.7 双主互备、高可用、读写分离 + keepalived 实战演练
本篇文章主要介绍了java使用spring实现读写分离的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
数据库读写分离实现方法
读写分离springMVC项目
对spring整合mybatis的SqlSessionTemplate进行修改,对数据库数据库垂直、水平拆分及读写分离进行支持,并构建相应的模型。同时实现一致性哈希的分表策略。
Sharding-JDBC教程:Spring Boot整合Sharding-JDBC实现读写分离
基于Mycat的MySQL主从读写分离配置详解与示例
springboot集成shardingJDBC实现读写分离demo工程,亲测可用 使用方式见博客:https://maoqizhi.blog.csdn.net/article/details/123149268?spm=1001.2014.3001.5502
2018数据库直播大讲堂峰会-Redis专场阿里云Redis开发者午光对Redis读写分离进行介绍的PPT。
Amoeba搞定mysql主从读写分离