spring+mybatis多数据源配置

应开发需求需要,根据项目业务,不同业务下的数据存入不同的数据库中,且须读写分离。目前常用的有两种方式:

    第一种:定义多个个数据库连接,分别是AReadDataSource,AWriteDataSource,BReadDataSource,BWriteDataSource,CReadDataSource, CWriteDataSource来实现;

   第二种:动态数据源切换,就是在程序运行时,把数据源动态织入到程序中,从而选择读取对应的数据库。主要使用的技术是:annotation,Spring AOP ,反射。具体实现方式如下:

    先定义一个annotation的注解类:

@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.METHOD)  
@Documented 
public @interface DataSourceNote {
	String dbSource() default ""; 
}

    添加一个dataSource.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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd  
                        http://www.springframework.org/schema/context
                        http://www.springframework.org/schema/context/spring-context-4.0.xsd">
     
    <!-- testA读库写库  start   -->                   
    <bean id="testAReadDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    	<!-- 读库个性化配置   -->
        <property name="driverClassName" value="${jdbc.testA_read.driver}"/>
        <property name="url" value="${jdbc.testA_read.url}"/>
        <property name="username" value="${jdbc.testA_read.username}"/>
        <property name="password" value="${jdbc.testA_read.password}"/>
    </bean>
    <bean id="testAWriteDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    	<!-- 写库个性化配置   -->
        <property name="driverClassName" value="${jdbc.testA_write.driver}"/>
        <property name="url" value="${jdbc.testA_write.url}"/>
        <property name="username" value="${jdbc.testA_write.username}"/>
        <property name="password" value="${jdbc.testA_write.password}"/>
    </bean>
     <!-- testA读库写库   end   -->  
    
    <!-- testB读库写库     start -->                   
    <bean id="testBReadDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    	<!-- 读库个性化配置   -->
        <property name="driverClassName" value="${jdbc.testB_read.driver}"/>
        <property name="url" value="${jdbc.testB_read.url}"/>
        <property name="username" value="${jdbc.testB_read.username}"/>
        <property name="password" value="${jdbc.testB_read.password}"/>
    </bean>
    <bean id="testBWriteDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    	<!-- 写库个性化配置   -->
        <property name="driverClassName" value="${jdbc.testB_write.driver}"/>
        <property name="url" value="${jdbc.testB_write.url}"/>
        <property name="username" value="${jdbc.testB_write.username}"/>
        <property name="password" value="${jdbc.testB_write.password}"/>
    </bean>
    <!-- testB读库写库     end --> 
    
    
    <!-- testC读库 写库 	start	-->                   
    <bean id="testCReadDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    	<!-- 读库个性化配置   -->
        <property name="driverClassName" value="${jdbc.testC_read.driver}"/>
        <property name="url" value="${jdbc.testC_read.url}"/>
        <property name="username" value="${jdbc.testC_read.username}"/>
        <property name="password" value="${jdbc.testC_read.password}"/>
    </bean>
    <bean id="testCWriteDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    	<!-- 写库个性化配置   -->
        <property name="driverClassName" value="${jdbc.testC_write.driver}"/>
        <property name="url" value="${jdbc.testC_write.url}"/>
        <property name="username" value="${jdbc.testC_write.username}"/>
        <property name="password" value="${jdbc.testC_write.password}"/>
    </bean>
    <!-- testC读库 写库 	end	--> 
    
     
	<bean id="dataSource" class="com.test.dao.aspect.ChooseDataSource" lazy-init="true">
		<description>数据源</description>
		<property name="targetDataSources">
			<map key-type="java.lang.String" value-type="javax.sql.DataSource">
				<!-- write -->
				<entry key="testAWrite" value-ref="testAWriteDataSource" />
				<entry key="testBWrite" value-ref="testBWriteDataSource" />
				<entry key="testCWrite" value-ref="testCWriteDataSource" />
				<!-- read -->
				<entry key="testARead" value-ref="testAReadDataSource" />
				<entry key="testBRead" value-ref="testBReadDataSource" />
				<entry key="testCRead" value-ref="testCReadDataSource" />
			</map>
		</property>
		<property name="methodType">
			<map key-type="java.lang.String">
				<!-- read -->
				<entry key="testARead" value=",get,select,count,list,query," />
				<entry key="testBRead" value=",get,select,count,list,query," />
				<entry key="testCRead" value=",get,select,count,list,query," />
				<!-- write -->
				<entry key="testAWrite" value=",add,insert,create,update,delete,remove," />
				<entry key="testBWrite" value=",add,insert,create,update,delete,remove," />
				<entry key="testCWrite" value=",add,insert,create,update,delete,remove," />
			</map>
		</property>
	</bean>
	<!-- 切面   -->
	<bean class="com.test.dao.aspect.DataSourceAspect" />
	<!-- jdbc模板  -->
	<bean class="org.springframework.jdbc.core.JdbcTemplate">
		<constructor-arg ref="dataSource" />
	</bean>
</beans>

    然后将dataSource.xml配置到spring-config.xml里面去,具体参考如下文件:    

<import resource="classpath*:spring/dataSource.xml"/>

   

    mybatis.xml配置如下:

     

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:cache="http://www.springframework.org/schema/cache"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd  
                        http://www.springframework.org/schema/context
                        http://www.springframework.org/schema/context/spring-context-4.0.xsd
                        http://www.springframework.org/schema/tx
                        http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
                        http://www.springframework.org/schema/cache
                        http://www.springframework.org/schema/cache/spring-cache-4.0.xsd">
     <!-- 开启注解配置 -->
	<context:annotation-config />
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="configLocation" value="classpath:mybatis/mybatis-config.xml" />
		<!-- 自动扫描mapping.xml文件 -->
		<property name="mapperLocations" value="classpath:sqlmaps/**/*.xml" />
	</bean>
	<!-- 事务管理   -->
    <bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
</beans>

    现在再来看看,dataSource.xml里面相关的两个类的代码,第一个DataSourceAspect,用于动态切换数据库源:

@Aspect
@EnableAspectJAutoProxy(proxyTargetClass = true)
@Log4j2
public class DataSourceAspect {

	@Pointcut("execution(* com.test.dao.*..*Impl.*(..))")
	public void aspect() {
	}

	/**
	 * 配置前置通知,使用在方法aspect()上注册的切入点
	 */
	@Before("aspect()")
	public void before(JoinPoint point) {
		String method = point.getSignature().getName();
		Log4j2Util.printLog4j2(log,this, method, Level.INFO, "(" + StringUtils.join(point.getArgs(), ",") + ")");
		//获取到对象的类
		Object obj = point.getTarget();
		Method methods[] = obj.getClass().getMethods();
		for (int  i = 0;  i< methods.length; i++) {
			 if(method.equals(methods[i].getName())){
				 DataSourceNote note = methods[i].getAnnotation(DataSourceNote.class);
				 String dbSource = note.dbSource();
				 if(StringUtils.isNotEmpty(dbSource)){
					 Log4j2Util.printLog4j2(log,this, "before", Level.DEBUG, "---------数据库类型:"+dbSource);
					 HandleDataSource.putDataSource(dbSource);
				 }else{
					 Log4j2Util.printLog4j2(log,this, "before", Level.ERROR, "---------数据库类型:"+dbSource);
				 }
			 }
		}
	}

	@After("aspect()")
	public void after(JoinPoint point) {
		HandleDataSource.clear();
	}
}

   

   下面再来看看ChooseDataSource类的处理,获取对应的dataSource里面的数据源:

 

public class ChooseDataSource extends AbstractRoutingDataSource {
	public static Map<String, List<String>> METHODTYPE = new HashMap<String, List<String>>();

	// 获取数据源名称
	protected Object determineCurrentLookupKey() {
		return HandleDataSource.getDataSource();
	}

	// 设置方法名前缀对应的数据源
	public void setMethodType(Map<String, String> map) {
		for (String key : map.keySet()) {
			List<String> v = new ArrayList<String>();
			String[] types = map.get(key).split(",");
			for (String type : types) {
				if (StringUtils.isNotBlank(type)) {
					v.add(type);
				}
			}
			METHODTYPE.put(key, v);
		}
	}
}

 

 代码实际使用示例如下:

public class TestDataDaoImpl extends BaseDao implements TestDataDao{
	
	private static final String NAME_SPACE = "com.test.dao.TestDataDao.";

	@Override
	@DataSourceNote(dbSource = "testAWrite")
	public boolean addTestAUserMsg(List<UserDO> list)
			throws DaoException {
		
		Integer count = this.insert(NAME_SPACE + "addTestAUserMsg", list);
		
		return (count == null || count == 0) ? false : true;
	}

	@Override
	@DataSourceNote(dbSource = "testBWrite")
	public boolean addTestBUserMsgLog(List<UserMsgLogDO> list)
			throws DaoException {
		
		Integer count = this.insert(NAME_SPACE + "addTestBUserMsgLog", list);
		
		return (count == null || count == 0) ? false : true;
	}

	@Override
	@DataSourceNote(dbSource = "testCRead")
	public UserDetail queryTestCUserDetail(Map<String, String> param)
			throws DaoException {
		
		UserDetail temp = (UserDetail ) this.queryForObject(NAME_SPACE + "queryTestCUserDetail", param);
		return temp;
	}

}

 

 按步骤依顺配置下来即可,不是很明白的地方可以去看看org.apache.commons.dbcp.BasicDataSource里的createConnectionFactory方法。及其他相关方法

  

猜你喜欢

转载自ping12132200.iteye.com/blog/2366695