注意: ------引用
1. AOP可以触发数据源字符串的切换
2. 数据源真正切换的关键是 AbstractRoutingDataSource 的 determineCurrentLookupKey() **被调用,此方法是在open connection**时触发
3. 事务是在connection层面管理的,启用事务后,一个事务内部的connection是复用的,所以就算AOP切了数据源字符串,但是数据源并不会被真正修改
综上所述:
如果要使用事务,还是别用determineCurrentLookupKey()这种方法切数据源了,得配置多个才行
在ssm 运用的过程中,有时我们需要在不同数据库中进行切换
进行数据的增删改查,此时我们需要运用spring 的aop 原理进行配置
总体来说过程分三步,1 必要类的建立(放在最后,复制即可) 2 db.properties修改 3 spring-dao.xml 的配置
2 db.properties修改
#数据库1 db.driver=com.microsoft.sqlserver.jdbc.SQLServerDriver db.url=jdbc:sqlserver://127.0.0.1:1433;databaseName=sj1 db.user=js db.password=js #数据库2 db.driver2=com.microsoft.sqlserver.jdbc.SQLServerDriver db.url2=jdbc:sqlserver://127.0.0.1:1433;databaseName=sj2 db.user2=js db.password2=js
3 spring-dao.xml 的配置
<context:property-placeholder location="classpath:db.properties"/> <!--连接数据库1--> <bean id="datasource1" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${db.driver}"/> <property name="jdbcUrl" value="${db.url}"/> <property name="user" value="${db.user}"/> <property name="password" value="${db.password}"/> <!-- 初始化连接池中的连接数,取值应在minPoolSize与maxPoolSize之间,默认为3--> <property name="initialPoolSize" value="3"/> <!--最大空闲时间,30秒内未使用则连接被丢弃。若为0则永不丢弃。默认值: 0--> <property name="maxIdleTime" value="30"/> <!--连接池中保留的最大连接数。默认值: 15 --> <property name="maxPoolSize" value="100"/> <!-- 连接池中保留的最小连接数,默认为:3--> <property name="minPoolSize" value="10"/> </bean> <!-- <!–连接数据库2--> <bean id="datasource2" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${db.driver}"/> <property name="jdbcUrl" value="${db.url2}"/> <property name="user" value="${db.user2}"/> <property name="password" value="${db.password2}"/> <!-- <!– 初始化连接池中的连接数,取值应在minPoolSize与maxPoolSize之间,默认为3–>--> <property name="initialPoolSize" value="3"/> <!-- <!–最大空闲时间,30秒内未使用则连接被丢弃。若为0则永不丢弃。默认值: 0–>--> <property name="maxIdleTime" value="30"/> <!-- <!–连接池中保留的最大连接数。默认值: 15 –>--> <property name="maxPoolSize" value="100"/> <!-- <!– 连接池中保留的最小连接数,默认为:3–>--> <property name="minPoolSize" value="10"/> </bean> <!-- <--;自定义数据源–>--> <bean id="datasource" class="com.js.util.DynamicDataSource">//自己创建的类 <!-- 默认使用sqlite数据库 1;--> <property name="defaultTargetDataSource" ref="datasource1"></property> <property name="targetDataSources"> <map> <entry key="dataSource1" value-ref="datasource1"></entry> <entry key="dateSource2" value-ref="datasource2"></entry> </map> </property> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="datasource"/> <property name="configLocation" value="classpath:mybatis/mybatis_config.xml"/> <property name="mapperLocations" value="classpath:com/js/*/dao/*.xml"/> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.js.*.dao"/> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> </bean> <!-- 切面 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> <bean id="dataSourceAspect" class="com.js.util.DataSourceAspect">//自己创建的类 </bean> <aop:config> <aop:aspect ref="dataSourceAspect"> <!-- 拦截所有service方法,在dao层添加注解 --> <aop:pointcut expression="execution(* com.js..dao..*.*(..))" id="dataSourcePointcut"/> <aop:before method="intercept" pointcut-ref="dataSourcePointcut"/> </aop:aspect> </aop:config>
运用:
在dao 类上加(如果出问题了,你可以加在方法上试试)
@DataSource("dataSource1") 或者 @DataSource("dataSource2")
@Repository @DataSource("dataSource1") public interface JsDao{ }
1 必要类的建立(四个类哦)
一
/** * @deprecated 数据库连接切换类 * */ @Target({ElementType.TYPE,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface DataSource{ String value(); }
二
public class DataSourceAspect { // 拦截目标方法,获取由@DataSource指定的数据源标识,设置到线程存储中以便切换数据源 public void intercept(JoinPoint point) throws Exception { Class<?> target = point.getTarget().getClass(); MethodSignature signature = (MethodSignature) point.getSignature(); // 默认使用目标类型的注解,如果没有则使用其实现接口的注解 for (Class<?> clazz : target.getInterfaces()) { resolveDataSource(clazz, signature.getMethod()); } resolveDataSource(target, signature.getMethod()); } /** * 提取目标对象方法注解和类型注解中的数据源标识 */ public void resolveDataSource(Class<?> clazz, Method method) { try { Class<?>[] types = method.getParameterTypes(); // 默认使用类型注解 if (clazz.isAnnotationPresent(DataSource.class)) { DataSource source = clazz.getAnnotation(DataSource.class); DbContextHolder.setDataSource(source.value()); } // 方法注解可以覆盖类型注解 Method m = clazz.getMethod(method.getName(), types); if (m != null && m.isAnnotationPresent(DataSource.class)) { DataSource source = m.getAnnotation(DataSource.class); DbContextHolder.setDataSource(source.value()); } } catch (Exception e) { System.out.println(clazz + ":" + e.getMessage()); } } }
三
/** * * 切换数据源的工具类 */ public class DbContextHolder { private static final ThreadLocal<String>THREAD_DATA_SOURCE =new ThreadLocal<>(); /** * 设置当前数据库 */ public static void setDataSource(String dataSource) { THREAD_DATA_SOURCE.set(dataSource); } /** * 取得当前数据库 */ public static String getDataSource() { return THREAD_DATA_SOURCE.get(); } /** * 清除上下文数据 */ public static void clearDataSource() { THREAD_DATA_SOURCE.remove(); } }
四
public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DbContextHolder.getDataSource(); } }