# rws1
**Repository Path**: weishaoying/rws1
## Basic Information
- **Project Name**: rws1
- **Description**: 数据库读写分离常用的2种方案
- **Primary Language**: Java
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2018-05-05
- **Last Updated**: 2020-12-18
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
## 读写分离
可以作为Spring MVC项目的简单模板
[读写分离笔记](https://gitee.com/weishaoying/codes/mv9busowkgy8ic12xt6dh54)
### 方案1 手工控制
两个数据源,写两套mapper,一套用于读,一套用于只写
Service中[读方法调用readMapper],[写方法调用writeMapper]
配置两套数据源:读数据源、写数据源
sqlSessionFactoryReadWrite
MapperScannerConfigurer
dao分2个目录 daor_r, dao_rw
UserReadMapper / UserWriteMapper
```java
public class UserService {
@Autowired
private UserReadMapper userReadMapper;
@Autowired
private UserWriteMapper userWriteMapper;
```
## 测试:
准备工作:
```shell
打包:mvn clean package
部署到Tomcat中,并启动
```
注释掉:RwsController及Service上的注解
引入这个配置
```xml
```
数据源1:
```text
http://localhost:8080/rws/queryAllUser
返回:
[{"id":1,"username":"weixiaodou","password":"WXD","nickname":"魏小豆","roles":"ROLE_CHILDREN"},{"id":2,"username":"admin","password":"admin","nickname":"admin","roles":"ROLE_USER,ROLE_ADMIN"},{"id":3,"username":"weishaoying","password":"abc123","nickname":"魏韶颖","roles":"ROLE_USER"},{"id":4,"username":"root","password":"root","nickname":"系统管理员","roles":"ROLE_USER"},{"id":5,"username":"wsy","password":"$2a$10$xgW7sWpDtNj6K6wS8jef9.NzbbUS7QwgisR8Nrh3BqbDbx5ru6nW2","nickname":"韶颖.魏","roles":"ROLE_USER"},{"id":7,"username":"weixiaodou","password":"wxd123456","nickname":"魏小豆","roles":"ROLE_CHILDREN"},{"id":8,"username":"wanchen","password":"wwc123","nickname":"魏菀辰","roles":"ROLE_CHILDREN"},{"id":11,"username":"BaiFeiFei","password":"AJBF","nickname":"白飞","roles":"ROLE_SINGER"},{"id":12,"username":"EEE","password":"WXD","nickname":"魏小豆","roles":"ROLE_CHILDREN"}]
http://localhost:8080/rws/queryAllBook
返回:
[{"id":1000,"name":"Java程序设计","number":0},{"id":1001,"name":"数据结构","number":10},{"id":1002,"name":"设计模式","number":9},{"id":1003,"name":"编译原理","number":10},{"id":1004,"name":"操作系统原理","number":100},{"id":1005,"name":"JavaWeb开发","number":25},{"id":1006,"name":"Spring mvc教程","number":25}]
```
数据源2:
```text
http://localhost:8080/rws/queryAllDog
返回:
[{"name":"泰迪","color":"棕色"},{"name":"哈士奇","color":"黑白色"},{"name":"腊肠","color":"棕红色"},{"name":"斗牛","color":"灰色"}]
新增、修改用户
```
## 方案2 实现AbstractRoutingDataSource类来动态管理数据源
applicationContext-daoRWS.xml中 配置了methodType属性
```xml
```
所以
Spring容器启动创建这个bean时,就会调用setMethodType(Map map) 方法
```java
public class ChooseDataSource extends AbstractRoutingDataSource {
//key=read, value=get,select,count,list,query
//key=write, value=add,save,create,update,delete,remove
public static Map> METHOD_TYPE_MAP = new HashMap<>();
//Spring容器启动创建这个bean时,就会调用这个方法,为属性赋值
public void setMethodType(Map map) {
for (String key : map.keySet()) {
List list = new ArrayList<>();
String[] methods = map.get(key).split(",");
for (String type : methods) {
if (StringUtils.isNotBlank(type)) {
list.add(type);
}
}
System.out.println("========ChooseDataSource.key = " + key);
System.out.println("========ChooseDataSource.list = " + list);
METHOD_TYPE_MAP.put(key, list);
}
}
@Override
protected Object determineCurrentLookupKey() {
return DataSourceHolder.getDataSource();
}
}
```
核心是切面
```java
@Aspect
@Component
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class DataSourceAspect {
protected Logger logger = LoggerFactory.getLogger(DataSourceAspect.class);
@Pointcut("execution(* com.weishaoying.rws.service.*.*(..))")
public void rws() {
}
@Before("rws()")
public void before(JoinPoint point) {
System.out.println("========Aspect before");
String className = point.getTarget().getClass().getName();
String methodName = point.getSignature().getName();
System.out.println("========" + className + "." + methodName + "(" + StringUtils.join(point.getArgs(), ",") + ")");
try {
//遍历Map,其实只有2个Read/Write
for (String key : ChooseDataSource.METHOD_TYPE_MAP.keySet()) {
//通过key获得value,value是List所以遍历
for (String type : ChooseDataSource.METHOD_TYPE_MAP.get(key)) {
//如果方法名等于type
if (methodName.startsWith(type)) {
System.out.println("========" + methodName + " start with " + type + ", Use " + key + " DataSource");
//把read/write放到ThreadLocal中
DataSourceHolder.putDataSource(key);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
//另外一种方式,通过方法上的注解来判断使用哪种数据源
public void before2(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 dataSource = m.getAnnotation(DataSource.class);
DataSourceHolder.putDataSource(dataSource.value());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
```
只有一套Mapper即可
```java
public interface UserMapper {
User queryById(Long id);
List queryAll();
int saveUser(User user);
int updateUser(User user);
int deleteById(Long id);
}
```
### 测试:
```text
测试查询:
http://localhost:8080/query/12
http://localhost:8080/queryAll
删除
http://localhost:8080/delete/31
新增
POST:
http://localhost:8080/save
参数:
{
"username": "BaiFeiFei",
"password": "AJ@B123F",
"nickname": "白飞飞",
"roles": "ROLE_SINGER"
}
修改
http://localhost:8080/update
参数:
{
"id": 27,
"username": "A27",
"password": "WAS",
"nickname": "王安石",
"roles": "ROLE_CHILDREN"
}
```