1. 程式人生 > >java讀寫分離實現

java讀寫分離實現

資料庫配置為一個主庫 多個從庫 主庫用於寫操作 從庫只讀操作
讀寫分離實現即為配置兩個資料來源,一個用於讀寫 連線主庫 假設為ds_wr,一個用於只讀 連線從庫 假設為ds_r。

對資料庫讀操作時,操作ds_r資料來源。
對資料來源寫操作時,操作ds_wr資料來源。

讀寫分離可以有兩種實現方式

[size=medium]第一種為寫兩套mappper[/size]
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);
}


至此 讀寫分離實現