http://blog.csdn.net/zl3450341/article/details/20150687
以前的项目经历中,基本上都是Spring + Hibernate + Spring JDBC这种组合用的多。至于MyBatis,也就这个项目才开始试用,闲话不多说,进入正题。
以前的这种框架组合中,动态数据源切换可谓已经非常成熟了,网上也有非常多的博客介绍,都是继承AbstractRoutingDataSource,重写determineCurrentLookupKey()方法。具体做法就不在此废话了。
所以当项目中碰到这个问题,我几乎想都没有想,就采用了这种做法,但是一测试,一点反应都没有。当时觉得不可能,于是断点,加log调试,发现determineCurrentLookupKey()根本没有调用。
为什么列? 这不可能啊。静下心来,仔细想想,才想到一个关键的问题: Mybatis整合Spring,而不是Spring整合的Mybatis! 直觉告诉我,问题就出在这里。
于是花时间去研究一下mybatis-spring.jar 这个包,发现有SqlSession这东西,很本能的就注意到了这一块,然后大致看一下他的一些实现类。于是就发现了他的实现类里面有一个内部类SqlSessionInterceptor(研究过程就不多说了,毕竟是个痛苦的过程)
好吧,这个类的作用列,就是产生sessionProxy。关键代码如下
- final SqlSession sqlSession = getSqlSession(
- SqlSessionTemplate.this.sqlSessionFactory,
- SqlSessionTemplate.this.executorType,
- SqlSessionTemplate.this.exceptionTranslator);
这个sqlSessionFactory 我们就很眼熟啦,是我们在spring配置文件中配了的,是吧,也就是说这东西是直接从我们配置文件中读进来,但这东西,就关联了Datasource。所以就想到,如果能把这东西,做到动态,那么数据源切换,也就动态了。
于是第一反应就是写了一个类,然后在里面定义一个Map,用来存放多个SqlSessionFactory,并采用Setter方法进行属性注入。
- public class EjsSqlSessionTemplate extends SqlSessionTemplate {
- private Map<String, SqlSessionFactory> targetSqlSessionFactory = new HashMap<String, SqlSessionFactory>();
- public void setTargetSqlSessionFactory(Map<String, SqlSessionFactory> targetSqlSessionFactory) {
- this.targetSqlSessionFactory = targetSqlSessionFactory;
- }
所以Spring的配置文件就变成了这样:
- <bean id="sqlSessionTemplate" class="com.ejushang.spider.datasource.EjsSqlSessionTemplate">
- <constructor-arg ref="sqlSessionFactory" />
- <property name="targetSqlSessionFactory">
- <map>
- <entry value-ref="sqlSessionFactory" key="spider"/>
- <entry value-ref="sqlSessionFactoryTb" key="sysinfo"/>
- </map>
- </property>
- </bean>
- <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
- <property name="basePackage" value="com.foo.bar.**.mapper*" />
- <property name="sqlSessionTemplateBeanName" value="sqlSessionTemplate"/>
- </bean>
那么这个思想是那里来的列? 当然就是借鉴了Spring的动态数据源的思想啦,对比一下Spring动态数据源的配置,看看是不是差不多?
然后重写了个关键的方法:
- /**
- * 重写得到SqlSessionFactory的方法
- * @return
- */
- @Override
- public SqlSessionFactory getSqlSessionFactory() {
- SqlSessionFactory targetSqlSessionFactory = this.targetSqlSessionFactory.get(SqlSessionContextHolder.getDataSourceKey());
- if (targetSqlSessionFactory != null) {
- return targetSqlSessionFactory;
- } else if ( this.getSqlSessionFactory() != null) {
- return this.getSqlSessionFactory();
- }
- throw new IllegalArgumentException("sqlSessionFactory or targetSqlSessionFactory must set one at least");
- }
而SqlSessionContextHolder就很简单,就是一个ThreadLocal的思想
- public class SqlSessionContextHolder {
- private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
- private static Logger logger = LoggerFactory.getLogger(SqlSessionContextHolder.class);
- public static void setSessionFactoryKey(String dataSourceKey) {
- contextHolder.set(dataSourceKey);
- }
- public static String getDataSourceKey() {
- String key = contextHolder.get();
- logger.info("当前线程Thread:"+Thread.currentThread().getName()+" 当前的数据源 key is "+ key);
- return key;
- }
- }
博主信心满满就开始测试了。。结果发现不行,切换不过来,始终都是绑定的是构造函数中的那个默认的sqlSessionFactory,当时因为看了一天源码,头也有点晕。其实为什么列?
看看我们产生sessionProxy的地方代码,他的sqlSessionFactory是直接从构造函数来拿的。而构造函数中的sqlSessionFactory在spring容器启动时,就已经初始化好了,这点也可以从我们Spring配置文件中得到证实。
那这个问题,怎么解决列? 于是博主便想重写那个sqlSessionInterceptor。 擦,问题就来了,这个类是private的,没办法重写啊。于是博主又只能在自己的EjsSqlSessionTemplate类中,也定义了一个内部类,把源码中的代码都copy过来,唯一不同的就是我不是读取构造函数中的sqlSessionFactory.而是每次都去调用 getSqlSessionFactory()方法。代码如下:
- final SqlSession sqlSession = getSqlSession(
- EjsSqlSessionTemplate.this.getSqlSessionFactory(),
- EjsSqlSessionTemplate.this.getExecutorType(),
- EjsSqlSessionTemplate.this.getPersistenceExceptionTranslator());
再试,发现还是不行,再找原因,又回归到了刚才那个问题。因为我没有重写SqlSessionTemplate的构造函数,而sqlSessionProxy是在构函数中初始化的,代码如下:
- public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
- PersistenceExceptionTranslator exceptionTranslator) {
- notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
- notNull(executorType, "Property 'executorType' is required");
- this.sqlSessionFactory = sqlSessionFactory;
- this.executorType = executorType;
- this.exceptionTranslator = exceptionTranslator;
- this.sqlSessionProxy = (SqlSession) newProxyInstance(
- SqlSessionFactory.class.getClassLoader(),
- new Class[] { SqlSession.class },
- new SqlSessionInterceptor());
- }
而SqlSessionInterceptor()这东西都是private。 所以父类压根就不会加载我写的那个SqlSessionInterceptor()。所以问题就出在这,那好吧,博主又重写构函数
- public EjsSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
- super(getSqlSessionFactory(), executorType, exceptionTranslator);
- }
很明显这段代码是编译不通过的,构造函数中,怎么可能调用类实例方法列? 那怎么办列? 又只有把父类的构造函数copy过来,那问题又有了,这些成员属性又没有。那又只得把他们也搬过来。。 后来,这个动态数据数据源的功能,终于完成了。
--------------------------------------------------------------------------------------------------------------------分割线-----------------------------------------------------------------------------------------------------------整个完整的代码如下:
1、重写SqlSessionTemplate (重写的过程已经在上面分析过了)
- public class EjsSqlSessionTemplate extends SqlSessionTemplate {
- private final SqlSessionFactory sqlSessionFactory;
- private final ExecutorType executorType;
- private final SqlSession sqlSessionProxy;
- private final PersistenceExceptionTranslator exceptionTranslator;
- private Map<Object, SqlSessionFactory> targetSqlSessionFactory;
- public void setTargetSqlSessionFactory(Map<Object, SqlSessionFactory> targetSqlSessionFactory) {
- this.targetSqlSessionFactory = targetSqlSessionFactory;
- }
- public EjsSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
- this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
- }
- public EjsSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
- this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration()
- .getEnvironment().getDataSource(), true));
- }
- public EjsSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
- PersistenceExceptionTranslator exceptionTranslator) {
- super(sqlSessionFactory, executorType, exceptionTranslator);
- this.sqlSessionFactory = sqlSessionFactory;
- this.executorType = executorType;
- this.exceptionTranslator = exceptionTranslator;
- this.sqlSessionProxy = (SqlSession) newProxyInstance(
- SqlSessionFactory.class.getClassLoader(),
- new Class[] { SqlSession.class },
- new SqlSessionInterceptor());
- }
- @Override
- public SqlSessionFactory getSqlSessionFactory() {
- SqlSessionFactory targetSqlSessionFactory = this.targetSqlSessionFactory.get(SqlSessionContextHolder.getDataSourceKey());
- if (targetSqlSessionFactory != null) {
- return targetSqlSessionFactory;
- } else if ( this.sqlSessionFactory != null) {
- return this.sqlSessionFactory;
- }
- throw new IllegalArgumentException("sqlSessionFactory or targetSqlSessionFactory must set one at least");
- }
- @Override
- public Configuration getConfiguration() {
- return this.getSqlSessionFactory().getConfiguration();
- }
- public ExecutorType getExecutorType() {
- return this.executorType;
- }
- public PersistenceExceptionTranslator getPersistenceExceptionTranslator() {
- return this.exceptionTranslator;
- }
- /**
- * {@inheritDoc}
- */
- public <T> T selectOne(String statement) {
- return this.sqlSessionProxy.<T> selectOne(statement);
- }
- /**
- * {@inheritDoc}
- */
- public <T> T selectOne(String statement, Object parameter) {
- return this.sqlSessionProxy.<T> selectOne(statement, parameter);
- }
- /**
- * {@inheritDoc}
- */
- public <K, V> Map<K, V> selectMap(String statement, String mapKey) {
- return this.sqlSessionProxy.<K, V> selectMap(statement, mapKey);
- }
- /**
- * {@inheritDoc}
- */
- public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {
- return this.sqlSessionProxy.<K, V> selectMap(statement, parameter, mapKey);
- }
- /**
- * {@inheritDoc}
- */
- public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
- return this.sqlSessionProxy.<K, V> selectMap(statement, parameter, mapKey, rowBounds);
- }
- /**
- * {@inheritDoc}
- */
- public <E> List<E> selectList(String statement) {
- return this.sqlSessionProxy.<E> selectList(statement);
- }
- /**
- * {@inheritDoc}
- */
- public <E> List<E> selectList(String statement, Object parameter) {
- return this.sqlSessionProxy.<E> selectList(statement, parameter);
- }
- /**
- * {@inheritDoc}
- */
- public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
- return this.sqlSessionProxy.<E> selectList(statement, parameter, rowBounds);
- }
- /**
- * {@inheritDoc}
- */
- public void select(String statement, ResultHandler handler) {
- this.sqlSessionProxy.select(statement, handler);
- }
- /**
- * {@inheritDoc}
- */
- public void select(String statement, Object parameter, ResultHandler handler) {
- this.sqlSessionProxy.select(statement, parameter, handler);
- }
- /**
- * {@inheritDoc}
- */
- public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
- this.sqlSessionProxy.select(statement, parameter, rowBounds, handler);
- }
- /**
- * {@inheritDoc}
- */
- public int insert(String statement) {
- return this.sqlSessionProxy.insert(statement);
- }
- /**
- * {@inheritDoc}
- */
- public int insert(String statement, Object parameter) {
- return this.sqlSessionProxy.insert(statement, parameter);
- }
- /**
- * {@inheritDoc}
- */
- public int update(String statement) {
- return this.sqlSessionProxy.update(statement);
- }
- /**
- * {@inheritDoc}
- */
- public int update(String statement, Object parameter) {
- return this.sqlSessionProxy.update(statement, parameter);
- }
- /**
- * {@inheritDoc}
- */
- public int delete(String statement) {
- return this.sqlSessionProxy.delete(statement);
- }
- /**
- * {@inheritDoc}
- */
- public int delete(String statement, Object parameter) {
- return this.sqlSessionProxy.delete(statement, parameter);
- }
- /**
- * {@inheritDoc}
- */
- public <T> T getMapper(Class<T> type) {
- return getConfiguration().getMapper(type, this);
- }
- /**
- * {@inheritDoc}
- */
- public void commit() {
- throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession");
- }
- /**
- * {@inheritDoc}
- */
- public void commit(boolean force) {
- throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession");
- }
- /**
- * {@inheritDoc}
- */
- public void rollback() {
- throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession");
- }
- /**
- * {@inheritDoc}
- */
- public void rollback(boolean force) {
- throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession");
- }
- /**
- * {@inheritDoc}
- */
- public void close() {
- throw new UnsupportedOperationException("Manual close is not allowed over a Spring managed SqlSession");
- }
- /**
- * {@inheritDoc}
- */
- public void clearCache() {
- this.sqlSessionProxy.clearCache();
- }
- /**
- * {@inheritDoc}
- */
- public Connection getConnection() {
- return this.sqlSessionProxy.getConnection();
- }
- /**
- * {@inheritDoc}
- * @since 1.0.2
- */
- public List<BatchResult> flushStatements() {
- return this.sqlSessionProxy.flushStatements();
- }
- /**
- * Proxy needed to route MyBatis method calls to the proper SqlSession got from Spring's Transaction Manager It also
- * unwraps exceptions thrown by {@code Method#invoke(Object, Object...)} to pass a {@code PersistenceException} to
- * the {@code PersistenceExceptionTranslator}.
- */
- private class SqlSessionInterceptor implements InvocationHandler {
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- final SqlSession sqlSession = getSqlSession(
- EjsSqlSessionTemplate.this.getSqlSessionFactory(),
- EjsSqlSessionTemplate.this.executorType,
- EjsSqlSessionTemplate.this.exceptionTranslator);
- try {
- Object result = method.invoke(sqlSession, args);
- if (!isSqlSessionTransactional(sqlSession, EjsSqlSessionTemplate.this.getSqlSessionFactory())) {
- // force commit even on non-dirty sessions because some databases require
- // a commit/rollback before calling close()
- sqlSession.commit(true);
- }
- return result;
- } catch (Throwable t) {
- Throwable unwrapped = unwrapThrowable(t);
- if (EjsSqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
- Throwable translated = EjsSqlSessionTemplate.this.exceptionTranslator
- .translateExceptionIfPossible((PersistenceException) unwrapped);
- if (translated != null) {
- unwrapped = translated;
- }
- }
- throw unwrapped;
- } finally {
- closeSqlSession(sqlSession, EjsSqlSessionTemplate.this.getSqlSessionFactory());
- }
- }
- }
- }
2。自定义了一个注解
- /**
- * 注解式数据源,用来进行数据源切换
- * User:Amos.zhou
- * Date: 14-2-27
- * Time: 下午2:34
- */
- @Target({ElementType.METHOD, ElementType.TYPE})
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- public @interface ChooseDataSource {
- String value() default "";
- }
3.定义一个AspectJ的切面(我习惯用AspectJ,因为spring AOP不支持cflow()这些语法),所以在编译,打包的时候一定要用aspectJ的编译器,不能直接用原生的JDK。有些方法就是我基于以前Hibernate,JDBC动态数据源的时候改动的。
- /**
- * <li>类描述:完成数据源的切换,抽类切面,具体项目继承一下,不需要重写即可使用</li>
- *
- * @author: amos.zhou
- * 2013-8-1 上午11:51:40
- * @since v1.0
- */
- @Aspect
- public abstract class ChooseDataSourceAspect {
- protected static final ThreadLocal<String> preDatasourceHolder = new ThreadLocal<String>();
- @Pointcut("execution(public * *.*(..))")
- public void allMethodPoint() {
- }
- @Pointcut("@within(com.ejushang.spider.annotation.ChooseDataSource) && allMethodPoint()")
- public void allServiceMethod() {
- }
- /**
- * 对所有注解有ChooseDataSource的类进行拦截
- */
- @Pointcut("cflow(allServiceMethod()) && allServiceMethod()")
- public void changeDatasourcePoint() {
- }
- /**
- * 根据@ChooseDataSource的属性值设置不同的dataSourceKey,以供DynamicDataSource
- */
- @Before("changeDatasourcePoint()")
- public void changeDataSourceBeforeMethodExecution(JoinPoint jp) {
- //拿到anotation中配置的数据源
- String resultDS = determineDatasource(jp);
- //没有配置实用默认数据源
- if (resultDS == null) {
- SqlSessionContextHolder.setSessionFactoryKey(null);
- return;
- }
- preDatasourceHolder.set(SqlSessionContextHolder.getDataSourceKey());
- //将数据源设置到数据源持有者
- SqlSessionContextHolder.setSessionFactoryKey(resultDS);
- }
- /**
- * <p>创建时间: 2013-8-20 上午9:48:44</p>
- * 如果需要修改获取数据源的逻辑,请重写此方法
- *
- * @param jp
- * @return
- */
- @SuppressWarnings("rawtypes")
- protected String determineDatasource(JoinPoint jp) {
- String methodName = jp.getSignature().getName();
- Class targetClass = jp.getSignature().getDeclaringType();
- String dataSourceForTargetClass = resolveDataSourceFromClass(targetClass);
- String dataSourceForTargetMethod = resolveDataSourceFromMethod(
- targetClass, methodName);
- String resultDS = determinateDataSource(dataSourceForTargetClass,
- dataSourceForTargetMethod);
- return resultDS;
- }
- /**
- * 方法执行完毕以后,数据源切换回之前的数据源。
- * 比如foo()方法里面调用bar(),但是bar()另外一个数据源,
- * bar()执行时,切换到自己数据源,执行完以后,要切换到foo()所需要的数据源,以供
- * foo()继续执行。
- * <p>创建时间: 2013-8-16 下午4:27:06</p>
- */
- @After("changeDatasourcePoint()")
- public void restoreDataSourceAfterMethodExecution() {
- SqlSessionContextHolder.setSessionFactoryKey(preDatasourceHolder.get());
- preDatasourceHolder.remove();
- }
- /**
- * <li>创建时间: 2013-6-17 下午5:34:13</li> <li>创建人:amos.zhou</li> <li>方法描述 :</li>
- *
- * @param targetClass
- * @param methodName
- * @return
- */
- @SuppressWarnings("rawtypes")
- private String resolveDataSourceFromMethod(Class targetClass,
- String methodName) {
- Method m = ReflectUtil.findUniqueMethod(targetClass, methodName);
- if (m != null) {
- ChooseDataSource choDs = m.getAnnotation(ChooseDataSource.class);
- return resolveDataSourcename(choDs);
- }
- return null;
- }
- /**
- * <li>创建时间: 2013-6-17 下午5:06:02</li>
- * <li>创建人:amos.zhou</li>
- * <li>方法描述 : 确定
- * 最终数据源,如果方法上设置有数据源,则以方法上的为准,如果方法上没有设置,则以类上的为准,如果类上没有设置,则使用默认数据源</li>
- *
- * @param classDS
- * @param methodDS
- * @return
- */
- private String determinateDataSource(String classDS, String methodDS) {
- // if (null == classDS && null == methodDS) {
- // return null;
- // }
- // 两者必有一个不为null,如果两者都为Null,也会返回Null
- return methodDS == null ? classDS : methodDS;
- }
- /**
- * <li>创建时间: 2013-6-17 下午4:33:03</li> <li>创建人:amos.zhou</li> <li>方法描述 : 类级别的 @ChooseDataSource
- * 的解析</li>
- *
- * @param targetClass
- * @return
- */
- @SuppressWarnings({"unchecked", "rawtypes"})
- private String resolveDataSourceFromClass(Class targetClass) {
- ChooseDataSource classAnnotation = (ChooseDataSource) targetClass
- .getAnnotation(ChooseDataSource.class);
- // 直接为整个类进行设置
- return null != classAnnotation ? resolveDataSourcename(classAnnotation)
- : null;
- }
- /**
- * <li>创建时间: 2013-6-17 下午4:31:42</li> <li>创建人:amos.zhou</li> <li>方法描述 :
- * 组装DataSource的名字</li>
- *
- * @param ds
- * @return
- */
- private String resolveDataSourcename(ChooseDataSource ds) {
- return ds == null ? null : ds.value();
- }
- }
那么以上3个类,就可以作为一个公共的组件打个包了。
那么项目中具体 怎么用列?
4. 在项目中定义一个具体的AspectJ切面
- @Aspect
- public class OrderFetchAspect extends ChooseDataSourceAspect {
- }
如果你的根据你的需要重写方法,我这边是不需要重写的,所以空切面就行了。
5.配置spring,在上面的分析过程中已经贴出了,基本上就是每个数据库,一个dataSource,每个DataSource一个SqlSessionFactory。最后配一个SqlSessionTemplate,也就是我们自己重写的。再就是MapperScan了,大致如下(数据库连接信息已去除,包名为杜撰):
- <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
- </bean>
- <bean id="dataSourceTb" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
- </bean>
- <!-- 事务管理 -->
- <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <property name="dataSource" ref="dataSource" />
- </bean>
- <!-- 注解控制事务 -->
- <tx:annotation-driven transaction-manager="transactionManager"/>
- <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
- <property name="dataSource" ref="dataSource"/>
- <property name="configLocation" value="classpath:mybatis.xml" />
- <property name="mapperLocations" value="classpath*:com/foo/bar/**/config/*mapper.xml" />
- </bean>
- <bean id="sqlSessionFactoryTb" class="org.mybatis.spring.SqlSessionFactoryBean">
- <property name="dataSource" ref="dataSourceTb"/>
- <property name="configLocation" value="classpath:mybatis.xml" />
- <property name="mapperLocations" value="classpath*:<span style="font-family: Arial, Helvetica, sans-serif;">com/foo/bar</span><span style="font-family: Arial, Helvetica, sans-serif;">/**/configtb/*mapper.xml" /></span>
- </bean>
- <bean id="sqlSessionTemplate" class="com.foo.bar.template.EjsSqlSessionTemplate">
- <constructor-arg ref="sqlSessionFactory" />
- <property name="targetSqlSessionFactory">
- <map>
- <entry value-ref="sqlSessionFactory" key="spider"/>
- <entry value-ref="sqlSessionFactoryTb" key="sysinfo"/>
- </map>
- </property>
- </bean>
- <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
- <property name="basePackage" value="com.foo.bar.**.mapper*" />
- <property name="sqlSessionTemplateBeanName" value="sqlSessionTemplate"/>
- </bean>
6.具体应用
- @ChooseDataSource("spider")
- public class ShopServiceTest extends ErpTest {
- private static final Logger log = LoggerFactory.getLogger(ShopServiceTest.class);
- @Autowired
- private IShopService shopService;
- @Autowired
- private IJdpTbTradeService jdpTbTradeService;
- @Test
- @Rollback(false)
- public void testFindAllShop(){
- List<Shop> shopList1 = shopService.findAllShop();
- for(Shop shop : shopList1){
- System.out.println(shop);
- }
- fromTestDB();
- }
- @ChooseDataSource("sysinfo")
- private void fromTestDB(){
- List<Shop> shopList = jdpTbTradeService.findAllShop();
- for(Shop shop : shopList){
- System.out.println(shop);
- }
- }
- }
测试发现 shopList1是从spider库查出来的数据,而fromDB则是从sysinfo中查出来的数据。 那么我们就大功告成。
要做到我以上功能,Spring AOP是做不到的,因为他不支持Cflow(),这也就是我为什么要用AspectJ的原因。
-----------------------------------------------------------------------------------------------再次分割线-------------------------------------------------------------------------------------------------------------------
好了,功能我们已经实现了,你有没有觉得很麻烦,这一点也不Spring的风格,Spring的各个地方扩展都是很方便的。那么我们看看,在SqlSessionTemplate中的什么地方改动一下,我们就可以很轻松的实现这个功能列?大家可以理解了,再回去看一下源码。
其实,只要将源码中的那个SqlSessionInterceptor的这句话:
- final SqlSession sqlSession = getSqlSession(
- SqlSessionTemplate.this.sqlSessionFactory,
- SqlSessionTemplate.this.executorType,
- SqlSessionTemplate.this.exceptionTranslator);
改为:
- final SqlSession sqlSession = getSqlSession(
- EjsSqlSessionTemplate.this.getSqlSessionFactory(),
- EjsSqlSessionTemplate.this.executorType,
- EjsSqlSessionTemplate.this.exceptionTranslator);
保证 每次在产生Session代理的时候,传进去的参数都是调用getSqlSessionFactory()获取,那么我们自定义的SqlSessionTemplate,只要重写getSqlSessionFactory(),加多一个以下2句话:
- private Map<Object, SqlSessionFactory> targetSqlSessionFactory;
- public void setTargetSqlSessionFactory(Map<Object, SqlSessionFactory> targetSqlSessionFactory) {
- this.targetSqlSessionFactory = targetSqlSessionFactory;
- }
那么就完全可以实现动态数据源切换。 那么mybatis-spring的项目团队会这样维护么? 我会以mail的方式与他们沟通。至于能否改进,我们不得而知了。
其实这也就引发一个关于面向对象设计时的思想,也是一直争论得喋喋不休的一个问题:
在类的方法中,如果要用到类的属性时,是直接用this.filedName 来操作,还是用 getFiledName() 来进行操作?
其实以前我也是偏向于直接用this.属性来进行操作的,但是经历过这次以后,我想我会偏向于后者。
相关推荐
`SpringBoot` 的 `AbstractRoutingDataSource` 类可以进一步帮助我们动态切换数据源,根据业务逻辑或事务需求透明地使用不同的数据库。 此外,`MyBatis` 的 `MapperScannerConfigurer` 或 `@MapperScan` 注解可以...
在多数据源配置中,Spring能够帮助管理不同的数据源,通过配置bean来切换和控制数据源的使用。 **SpringMVC** 是Spring框架的一部分,专为Web开发设计。它简化了模型-视图-控制器(Model-View-Controller,MVC)的...
Spring3 整合 MyBatis3 配置多数据源动态选择 SqlSessionFactory 详细教程 本教程主要介绍了 Spring3 整合 MyBatis3 配置多数据源动态选择 SqlSessionFactory 的详细教程。下面将详细介绍如何实现 Spring 整合 ...
总结,Spring Boot结合Mybatis和Druid实现多数据源配置的过程包括:配置数据源、配置Mybatis、创建数据源切换器、以及针对不同数据库的测试。这一过程涉及了Spring Boot的自动配置、依赖注入、配置属性绑定等多个...
通过以上步骤,我们就实现了MyBatis与Spring配合下的动态数据源切换。这种机制有助于在多租户系统、读写分离或者高可用架构中灵活地管理数据库访问,提高了系统的可扩展性和灵活性。在实际应用中,还需要考虑数据源...
在SpringBoot项目中,整合Mybatis-Plus并实现多数据源的动态切换,同时支持分页查询是一项常见的需求。以下将详细阐述这个过程中的关键步骤和技术要点。 首先,我们需要引入必要的Maven依赖。这里提到了四个关键...
- 利用 Spring Boot 的 Profile 功能,根据不同环境配置不同的数据源,方便测试和生产环境的切换。 - 使用统一的异常处理,如全局的 HttpExceptionAdvice,统一处理数据库操作中的异常,提高用户体验。 通过上述...
在企业级应用开发中,Spring、...具体实现时,可以根据项目需求选择合适的方式进行数据源切换,以达到优化性能、提高可扩展性的目的。通过合理配置和设计,可以有效地管理和利用多数据源,提升系统的灵活性和可靠性。
在实际开发中,为了实现多数据源的切换,我们可以使用Spring的AbstractRoutingDataSource类,自定义一个动态数据源类,根据业务逻辑或配置选择使用哪个数据源。在Service层或DAO层,通过注解或编程方式指定数据源,...
2. **数据源切换策略** 实现数据源的动态切换,我们需要一个策略类来决定当前请求应使用哪个数据源。可以创建一个`AbstractRoutingDataSource`的子类,根据特定条件(如请求参数、用户角色等)选择合适的数据源。 ...
1. 配置Spring的多数据源:通过Spring的AbstractRoutingDataSource,我们可以动态切换数据源。每个数据源都有自己的DataSource bean,路由策略可以根据业务逻辑或请求参数决定使用哪个数据源。 2. 集成Atomikos:在...
springboot + mybatis-plus + database+ 多数据源 + redis + hutool 框架干净,没有其他冗余的成分; 配置了MP的代码生成器,意见生成代码,节省开发时间! 可用于各种定时任务处理,各种夸库操作, 多数据源支持...
在Spring Boot应用中,整合MyBatis和Druid并实现多数据源切换是一项常见的需求,尤其是在处理多个数据库环境(如开发、测试、生产)时。本文将深入探讨如何配置和使用这一技术栈。 首先,我们需要了解`spring boot`...
"Mybatis+Spring+SpringMVC+quartz多数据源切换"的架构设计就是为了满足这样的场景。这个项目结合了四个关键的技术组件,它们分别是Mybatis、Spring、SpringMVC和Quartz,下面将详细介绍这些技术以及它们在多数据源...
本教程将详细讲解如何使用Spring Boot 2.7.0和MyBatis框架搭建多数据源配置,以实现灵活的数据管理。 首先,我们需要了解Spring Boot的核心特性,它是一个基于Spring框架的微服务开发工具,简化了配置并提供了自动...
这个示例演示了如何整合这两个流行的技术来处理数据库操作,其中数据源包括SQL Server和Oracle。以下是项目的详细知识点: 1. **Spring Boot**: - Spring Boot是一个简化Spring应用程序开发的框架,它通过提供...
使用aop进行多数据源切换 springMVC+spring+mybatis增删改查的使用。dk8+tomcat8+mysql+Eclipse+maven。spring+spring mvc+mybatis+bootstrap+jquery
存储过程等),用Redis做中间缓存,缓存数据 实现异步处理,定时任务,整合Quartz Job以及Spring Task 邮件管理功能, 整合spring-boot-starter-mail发送邮件等, 数据源:druid 用户管理,菜单管理,角色管理,代码...
Spring Boot结合MyBatis框架提供了方便的方式来配置和管理多个数据库连接,实现数据源的动态切换。本文将详细探讨如何在Spring Boot项目中配置多数据源,并利用注解自动切换,以及实现简单的读写分离和负载均衡。 ...