spring 配置多数据源 + 事务

由于项目原因,做了一下配置多数据源的调查,结果如下:

项目是标准的spring + mybatis

注:配置方面相对来说简化过,不是完整版,我只展示出重要部分

数据库连接配置文件:

#数据库(1)
jdbc_url=jdbc:mysql://localhsot:3306/first?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc_username=root
jdbc_password=root
#数据库(2)
jdbc_url_second=jdbc:mysql://localhost:3306/second?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc_username_second=root
jdbc_password_second=root

数据源(1)配置:

<!-- 数据源(1) -->	
<bean name="firstDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
    <property name="url" value="${jdbc_url}" />
	<property name="username" value="${jdbc_username}" />
	<property name="password" value="${jdbc_password}" />

	<!-- 初始化连接大小 -->
	<property name="initialSize" value="1" />
	<!-- 连接池最大使用连接数量 -->
	<property name="maxActive" value="200" />
	<!-- 连接池最大空闲 -->
	<property name="maxIdle" value="100" />
	<!-- 连接池最小空闲 -->
	<property name="minIdle" value="1" />
	<!-- 获取连接最大等待时间 -->
	<property name="maxWait" value="60000" />
</bean>

数据源(2)配置:

<!-- 数据源(2) -->
<bean name="secondDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
	<property name="url" value="${jdbc_url_second}" />
	<property name="username" value="${jdbc_username_second}" />
	<property name="password" value="${jdbc_password_second}" />

	<!-- 初始化连接大小 -->
	<property name="initialSize" value="1" />
	<!-- 连接池最大使用连接数量 -->
	<property name="maxActive" value="200" />
	<!-- 连接池最大空闲 -->
	<property name="maxIdle" value="100" />
	<!-- 连接池最小空闲 -->
	<property name="minIdle" value="1" />
	<!-- 获取连接最大等待时间 -->
	<property name="maxWait" value="60000" />
</bean>

数据源切换配置:

<bean id="dynamicDataSource" class="com.test.common.dataSource.DynamicDataSource">
	<property name="targetDataSources">
		<map key-type="java.lang.String">
			<!-- 指定lookupKey和与之对应的数据源 -->
			<entry key="firstDataSource" value-ref="firstDataSource"></entry>
			<entry key="secondDataSource" value-ref="secondDataSource"></entry>
		</map>
	</property>
	<!-- 这里可以指定默认的数据源 -->
	<property name="defaultTargetDataSource" ref="firstDataSource" />
</bean>

DynamicDataSource.java

public class DynamicDataSource extends AbstractRoutingDataSource{

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

	public static String getDataSource() {
		return dataSources.get();
	}

	public static void setDataSource(String dataSource) {
		dataSources.set(dataSource);
	}

	public static void clearDataSource() {
		dataSources.remove();
	}

	@Override
	protected Object determineCurrentLookupKey() {
		return dataSources.get();
	}

}

mybatis 相关配置:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dynamicDataSource" />
    <!-- 自动扫描entity目录, 省掉Configuration.xml里的手工配置 -->
    <property name="mapperLocations" value="classpath:com/test/mapping/*.xml" />
</bean>

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.test.dao" />
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>

 

写的过程中想到,如果在 controller 中调用某方法需要切换库的时候,还需要在这个方法前 写一句

DynamicDataSource.setDataSource("配置文件中配置的数据源的key")

(业务需要)切换后还需要将刚才设置的源删除(要不然之后就会是刚才切换的数据源),这样的话 就还需要在 刚才的方法后写一句 (DynamicDataSource.clearDataSource()

这样太麻烦了,所以就想了一种处理方式,用注解,嘿嘿嘿,果然适当的懒才能让人进步 

事务相关配置:

注:

  • 事务相关的部分,本项目配置的是 用 *Impl 结束的实现类,这些 实现类的方法中 有满足下面配置的 才会开启事务 
  • 一旦进入事务后就无法修改数据源,所以只能在开启事务前去修改数据源
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dynamicDataSource" />
</bean>

<!-- 拦截器方式配置事物 -->
<tx:advice id="transactionAdvice" transaction-manager="transactionManager">
	<tx:attributes>
		<tx:method name="create*" propagation="REQUIRED" />
		<tx:method name="add*" propagation="REQUIRED" />
		<tx:method name="append*" propagation="REQUIRED" />
		<tx:method name="insert*" propagation="REQUIRED" />
		<tx:method name="save*" propagation="REQUIRED" />
		<tx:method name="update*" propagation="REQUIRED" />
		<tx:method name="modify*" propagation="REQUIRED" />
		<tx:method name="edit*" propagation="REQUIRED" />
		<tx:method name="delete*" propagation="REQUIRED" />
		<tx:method name="remove*" propagation="REQUIRED" />

		<tx:method name="*" propagation="SUPPORTS" />
		</tx:attributes>
	</tx:advice>

<bean id="dataSourceExchange" class="com.test.common.dataSource.DataSourceExchange"/>
<aop:config>
	<aop:pointcut id="transactionPointcut" expression="execution(* com.test.*.service..*Impl.*(..))" />
	<!-- 开启事务前先判断是否需要切换数据源 -->
	<aop:advisor pointcut-ref="transactionPointcut" advice-ref="dataSourceExchange" order="1"/>
	<aop:advisor pointcut-ref="transactionPointcut" advice-ref="transactionAdvice" order="2"/>
</aop:config>

DataSource.java

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
	String value();
}

DataSourceExchange.java

@Component
public class DataSourceExchange implements MethodBeforeAdvice, AfterReturningAdvice {

	Logger log = LoggerFactory.getLogger("【数据源切换器】");

	/**
	 * 拦截目标方法,获取由 @DataSource 指定的数据源标识,设置到线程存储中以便切换数据源
	 */
	@Override
	public void before(Method method, Object[] args, Object target) throws Throwable {
		if(method.isAnnotationPresent(DataSource.class)){
			DataSource dataSource = method.getAnnotation(DataSource.class);
			DynamicDataSource.setDataSource(dataSource.value());
			log.info("数据源切换至:" + DynamicDataSource.getDataSource());
		}
	}

	/**
	 * 方法结束后(移除数据源)
	 */
	@Override
	public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
		DynamicDataSource.clearDataSource();
		log.info("数据源已移除!");
	}
}

简单示例:

@Controller
@RequestMapping("/test")
public class TestController {
    
	@Autowired
	TestService testService;

	/**
	 * 纯测试
	 */
	@RequestMapping(value = "/justTest ", produces = "application/json;charset=utf-8", method = RequestMethod.POST)
	@ResponseBody
	public String justTest (){
        log.info("此时连接的是默认的数据源(1)");

	    /**
	     * 如果没有配置 aop 相关部分,切换数据源写法:
	     * DynamicDataSource.setDataSource("secondDataSource"); -- 切换第二数据源
	     * testService.justTest();
	     * DynamicDataSource.clearDataSource(); -- 移除已设置数据源,恢复到默认数据源
	     */

        testService.justTest();
        return null;
	}
}
public interface TestService {
    
    @DataSource("secondDataSource")
    void justTest();

}
@Service
public class TestServiceImpl implements TestService {

    @Override
	public void justTest() {
        //往下我就不写了,按照上面的配置
        //进到这里后,已经切换到了第二数据源
    }
}

大概就是这样,我尽量写明白,写的不好别见怪

哪位大神有其他见解,请留下评论,大家一起交流 ~ 拜拜 ~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值