ssm动态切换数据源

ssm动态切换数据源
自定义一个类继承spring的AbstractRoutingDataSource覆盖determineCurrentLookupKey方法

package com.tzc.webapi.dbRouting;

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

        import java.util.Map;

public class DynamicDataSource extends AbstractRoutingDataSource {
    //查找用户上下文变量中设置的数据源
    @Override
    protected Object determineCurrentLookupKey() {
        // TODO Auto-generated method stub
        return DataSourceContextHolder.getDataSourceType();
    }
    //设置默认数据源
    @Override
    public void setDefaultTargetDataSource(Object defaultTargetDataSource) {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
    }
    //设置数据源集合
    @Override
    public void setTargetDataSources(Map targetDataSources) {
        super.setTargetDataSources(targetDataSources);
    }
}

上下文环境的类:

package com.tzc.webapi.dbRouting;

public class DataSourceContextHolder {

    private static final ThreadLocal<String> contextHolder = new ThreadLocal();

    public static void setDataSourceType(String dataSourceType) {
        contextHolder.set(dataSourceType);
    }

    public static String getDataSourceType() {
        return contextHolder.get();
    }

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

}
spring.xml配置多个数据源eg:
<bean id="masterDataSource" class="org.apache.commons.dbcp.BasicDataSource"
          destroy-method="close">
        <property name="driverClassName" value="${master.jdbc.driverClassName}" />
        <property name="url" value="${master.jdbc.url}" />
        <property name="username" value="${master.jdbc.username}" />
        <property name="password" value="${master.jdbc.password}" />


        <!-- 配置初始化大小、最小、最大 -->
        <property name="initialSize" value="${initialSize}" />
        <property name="minIdle" value="${minIdle}" />
        <property name="maxActive" value="${maxActive}" />

        <!-- 配置获取连接等待超时的时间 -->
        <property name="maxWait" value="${maxWait}" />
</bean>



    <bean id="slaveDataSource" class="org.apache.commons.dbcp.BasicDataSource"
          destroy-method="close">
        <property name="driverClassName" value="${slave.jdbc.driverClassName}" />
        <property name="url" value="${slave.jdbc.url}" />
        <property name="username" value="${slave.jdbc.username}" />
        <property name="password" value="${slave.jdbc.password}" />


        <!-- 配置初始化大小、最小、最大 -->
        <property name="initialSize" value="${initialSize}" />
        <property name="minIdle" value="${minIdle}" />
        <property name="maxActive" value="${maxActive}" />

        <!-- 配置获取连接等待超时的时间 -->
        <property name="maxWait" value="${maxWait}" />
    </bean>


    <bean id="dataSource" class="com.tzc.webapi.dbRouting.DynamicDataSource">
        <property name="targetDataSources">
            <map key-type="java.lang.String">
                <entry key="master" value-ref="masterDataSource"/>
                <entry key="slave" value-ref="slaveDataSource"/>
            </map>
        </property>
        <property name="defaultTargetDataSource" ref="masterDataSource"></property>
    </bean>

④基于自定义注解+aop实现数据源的动态切换eg:

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
    String value() default "";
}
#aop实现类
@Aspect
@Component
@Order(1)
public class DynamicDataSourceAspect {
    private static Logger logger = org.slf4j.LoggerFactory.getLogger(DynamicDataSourceAspect.class);

    @Pointcut("@within(com.tzc.webapi.dbRouting.DataSource)||@annotation(com.tzc.webapi.dbRouting.DataSource)")
    public void pointCut() {}

    @Before("pointCut()")
    public void before(JoinPoint point) {
        logger.info("切面捕获到修改数据源信息");
        MethodSignature signa = (MethodSignature) point.getSignature();
        Method method = signa.getMethod();
        DataSource annotationClass = method.getAnnotation(DataSource.class);//获取方法上的注解
        if(annotationClass == null){
            annotationClass = point.getTarget().getClass().getAnnotation(DataSource.class);//获取类上面的注解
            if(annotationClass == null) return;
        }
        //获取注解上的数据源的值的信息
        String dataSourceKey = annotationClass.value();
        System.out.println(dataSourceKey+"已经切入");
        if(dataSourceKey !=null){
            //给当前的执行SQL的操作设置特殊的数据源的信息
            DataSourceContextHolder.setDataSourceType(dataSourceKey);
        }
        logger.info("AOP动态切换数据源,className"+point.getTarget().getClass().getName()+"methodName"+method.getName()+";dataSourceKey:"+dataSourceKey==""?"默认数据源":dataSourceKey);
    }

    @After("pointCut()")
    public void after(JoinPoint point){
        DataSourceContextHolder.clearDataSourceType();
    }
}
#开启aop自动代理
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
#注入bean
    <bean name="myAspectJ" class="com.tzc.webapi.dbRouting.DynamicDataSourceAspect"/>

业务层

@DataSource("master")
	public User getUserMasterById(Long id) {

		return userMapper.selectByPrimaryKey(id);
	}

	@DataSource("slave")
	public User getUserSlaveById(Long id) {

		return userMapper.selectByPrimaryKey(id);
	}

	@DataSource("master")
	public void saveMaster(User user) {
		userMapper.insert(user);
	}

	@DataSource("slave")
	public void saveSlave(User user) {
		userMapper.insert(user);
	}

	public List<User> getUsers() {
		List<User> users = userMapper.selectUsers();
		return users;
	}

测试


import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.tzc.webapi.bean.Book;
import com.tzc.webapi.bean.User;
import com.tzc.webapi.service.BookService;
import com.tzc.webapi.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "classpath:spring-mybatis.xml" })
public class UserServiceTest {

	@Autowired
	private UserService userService;

	@Autowired
	private BookService bookService;

	private ExecutorService service = Executors.newFixedThreadPool(20);

	@Test
	public void testQuery() {

		User masterUser = userService.getUserMasterById(1L);
		System.out.println(masterUser);

		System.out.println("-------------------------------分割线---------------------------------");

		User slaveUser = userService.getUserSlaveById(1L);
		System.out.println(slaveUser);

	}

	@Test
	public void testQueryThread() {

		for (int i = 0; i < 20; i++) {
			if (i % 2 == 0) {
				service.execute(new Runnable() {

					@Override
					public void run() {
						User masterUser = userService.getUserMasterById(1L);
						System.out.println(masterUser);

					}
				});
			} else {

				service.execute(new Runnable() {

					@Override
					public void run() {
						User slaveUser = userService.getUserSlaveById(1L);
						System.out.println(slaveUser);

					}
				});
			}
		}

		try {
			Thread.sleep(6000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		service.shutdown();

	}

	@Test
	public void testSave() {
		User master = new User();
		master.setAge(0);
		master.setPassword("master");
		master.setUserName("主机1");
		userService.saveMaster(master);

		User slave = new User();
		slave.setAge(100);
		slave.setPassword("slave");
		slave.setUserName("备机1");
		userService.saveSlave(slave);
	}

	@Test
	public void testTrans() {
		User master = new User();
		master.setAge(111);
		master.setPassword("1111");
		master.setUserName("testTrans");
		userService.saveMaster(master);

	}

	@Test
	public void testDifTableQuery() {

		for (int i = 0; i < 20; i++) {
			if (i % 2 == 0) {
				service.execute(new Runnable() {

					@Override
					public void run() {
						Book book = bookService.getBookById(1L);
						System.out.println(book);

					}
				});
			} else {

				service.execute(new Runnable() {

					@Override
					public void run() {
						User slaveUser = userService.getUserSlaveById(1L);
						System.out.println(slaveUser);

					}
				});
			}
		}

		try {
			Thread.sleep(6000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		service.shutdown();

	}

}

总结:其实就是在操作的方法的自定义注解上拿到value值(也就是数据源名称),然后传给spring路由的那个覆盖的lookup方法就ok了。

猜你喜欢

转载自blog.csdn.net/qq_40212198/article/details/89359894
今日推荐