spring mybatis 多数据源读写分离实际应用

通过继承spring AbstractRoutingDataSource父类来进行动态的切换数据源,结合注解和spring aop来实现。

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * 自定义的多数据源路由器.
 * 
 */
public class CustomerRoutingDataSource extends AbstractRoutingDataSource {
    private final transient Logger log = LoggerFactory.getLogger(CustomerRoutingDataSource.class);

    @Override
    protected Object determineCurrentLookupKey() {
        final String dbType = CustomerDBContextHolder.getDataSourceType();
        if (log.isInfoEnabled()) {
            log.info("获取了KEY={}的数据源", dbType);
        }
        return dbType;
    }
}

 

/**
 * 自定义的上下文储存器
 */
public class CustomerDBContextHolder {

    //线程安全的ThreadLocal  
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();

    public static void setDataSourceType(String dbType) {
        Assert.notNull(dbType, "DBType cannot be null");
        contextHolder.set(dbType);
    }

    public static String getDataSourceType() {
        String str = (String) contextHolder.get();
        if (StringUtils.isBlank(str))
            str = DataSource.master;
        return str;
    }

    public static void clearDataSourceType() {
        contextHolder.remove();
    }

}




import java.lang.annotation.*;
 
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented

/**
 *注解
 */
public @interface DataSource {
    String name() default DataSource.master;
 
    public static String master = "master";
 
    public static String slave = "slave";
 
 
}



import java.lang.reflect.Method;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
 *切点
 */
@Aspect
@Component
public class DataSourceAspect {
	
	//定义切点
	@Pointcut("@annotation(org.pos.manager.data.utils.spring.DataSource)")    
    public void daoAspect() {
		
	} 
	
	@Before("daoAspect()")
	public void doBefore(JoinPoint joinPoint){
		try {
			CustomerDBContextHolder.setDataSourceType(getDataSource(joinPoint));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	@SuppressWarnings("rawtypes")
	public static String getDataSource(JoinPoint joinPoint) throws Exception {
		String targetName = joinPoint.getTarget().getClass().getName();
		String methodName = joinPoint.getSignature().getName();
		Object[] arguments = joinPoint.getArgs();
		Class targetClass = Class.forName(targetName);
		Method[] methods = targetClass.getMethods();
		String name = "";
		for (Method method : methods) {
			if (method.getName().equals(methodName)) {
				Class[] clazzs = method.getParameterTypes();
				if (clazzs.length == arguments.length) {
					name = method.getAnnotation(DataSource.class).name();
					break;
				}
			}
		}
		return name;
	}  
}


数据源配置
<bean id="dataSourceWrite" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
		<property name="driverClass" value="${master.jdbc.driverClassName}" />
		<property name="jdbcUrl" value="${master.jdbc.url}" />
		<property name="user" value="${master.jdbc.userDS.username}" />
		<property name="password" value="${master.jdbc.userDS.password}" />
		<!-- 连接池中保留的最小连接数    -->
		<property name="minPoolSize" value="1" />
		<!-- 连接池中保留的最大连接数    -->
		<property name="maxPoolSize" value="200" />
		<!-- 最大空闲时间,1800秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0-->
		<property name="maxIdleTime" value="1800" />
		<!-- 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3-->
		<property name="acquireIncrement" value="5" />
		<!-- 最大的PreparedStatement的数量    -->
		<property name="maxStatements" value="1000" />
		<!-- 每60秒检查所有连接池中的空闲连接。Default: 0 -->
		<property name="idleConnectionTestPeriod" value="60" />
		<!-- 定义在从数据库获取新连接失败后重复尝试的次数。Default: 30-->
		<property name="acquireRetryAttempts" value="30" />
		<property name="breakAfterAcquireFailure" value="true" />
		<property name="testConnectionOnCheckout" value="false" />
	</bean>
	
	<bean id="dataSourceRead" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
		<property name="driverClass" value="${slave.jdbc.driverClassName}" />
		<property name="jdbcUrl" value="${slave.jdbc.url}" />
		<property name="user" value="${slave.jdbc.userDS.username}" />
		<property name="password" value="${slave.jdbc.userDS.password}" />
		<!-- 连接池中保留的最小连接数    -->
		<property name="minPoolSize" value="1" />
		<!-- 连接池中保留的最大连接数    -->
		<property name="maxPoolSize" value="200" />
		<!-- 最大空闲时间,1800秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0-->
		<property name="maxIdleTime" value="1800" />
		<!-- 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3-->
		<property name="acquireIncrement" value="5" />
		<!-- 最大的PreparedStatement的数量    -->
		<property name="maxStatements" value="1000" />
		<!-- 每60秒检查所有连接池中的空闲连接。Default: 0 -->
		<property name="idleConnectionTestPeriod" value="60" />
		<!-- 定义在从数据库获取新连接失败后重复尝试的次数。Default: 30-->
		<property name="acquireRetryAttempts" value="30" />
		<property name="breakAfterAcquireFailure" value="true" />
		<property name="testConnectionOnCheckout" value="false" />
	</bean>
	
	<bean id="dataSource" class="org.pos.manager.data.utils.spring.CustomerRoutingDataSource">  
        <property name="targetDataSources">  
             <map key-type="java.lang.String">  
               <entry key="master" value-ref="dataSourceWrite" />
               <entry key="slave" value-ref="dataSourceRead" />   
            </map>  
        </property>
         <property name="defaultTargetDataSource" ref="dataSourceWrite"/>  
   </bean>  

applicationContext.xml中加入aop代理配置
<aop:aspectj-autoproxy proxy-target-class="true" />


最后在service层实现动态设置

@Component(value = "accountBankService")
public class AccountBankServiceImpl implements AccountBankService {
	
	@Autowired
	@Qualifier(value = "accountBankDao")
	private AccountBankDao accountBankDao;
	
	@DataSource(name = DataSource.slave)
	@Override
	public List<AccountBank> getAccountBankList() throws Exception {
		return accountBankDao.getAccountBankList();
	}

}

猜你喜欢

转载自gongjiayun.iteye.com/blog/2301108
今日推荐