直接在全局配置文件配置各种属性是一种比较简单的方式,其实的任何组件的整合都有不少于两种的配置方式,下面来介绍下配置类如何配置。
MybatisAutoConfiguration自动配置类有如下一断代码:
@Bean @ConditionalOnMissingBean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {}@ConditionalOnMissingBean和@Bean真是老搭档了,意味着我们又可以覆盖,只需要在IOC容器中注入SqlSessionFactory(Mybatis六剑客之一生产者)。
在自定义配置类中注入即可,如下:
/** * 注入SqlSessionFactory */ @Bean("sqlSessionFactory1") public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSource); sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:/mapper/**/*.xml")); org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration(); // 自动将数据库中的下划线转换为驼峰格式 configuration.setMapUnderscoreToCamelCase(true); configuration.setDefaultFetchSize(100); configuration.setDefaultStatementTimeout(30); sqlSessionFactoryBean.setConfiguration(configuration); return sqlSessionFactoryBean.getObject(); }以上介绍了配置Mybatis的两种方式,其实在大多数场景中使用第一种已经够用了,至于为什么介绍第二种呢?当然是为了多数据源的整合而做准备了。
在MybatisAutoConfiguration中有一行很重要的代码,如下:
@ConditionalOnSingleCandidate(DataSource.class)@ConditionalOnSingleCandidate这个注解的意思是当IOC容器中只有一个候选Bean的实例才会生效。
这行代码标注在Mybatis的自动配置类中有何含义呢?下面介绍,哈哈哈~
多数据源如何整合?上文留下的问题:为什么的Mybatis自动配置上标注如下一行代码:
@ConditionalOnSingleCandidate(DataSource.class)以上这行代码的言外之意:当IOC容器中只有一个数据源DataSource,这个自动配置类才会生效。
哦?照这样搞,多数据源是不能用Mybatis吗?
可能大家会有一个误解,认为多数据源就是多个的DataSource并存的,当然这样说也不是不正确。
多数据源的情况下并不是多个数据源并存的,Spring提供了AbstractRoutingDataSource这样一个抽象类,使得能够在多数据源的情况下任意切换,相当于一个动态路由的作用,作者称之为动态数据源。因此Mybatis只需要配置这个动态数据源即可。
什么是动态数据源?动态数据源简单的说就是能够自由切换的数据源,类似于一个动态路由的感觉,Spring 提供了一个抽象类AbstractRoutingDataSource,这个抽象类中哟一个属性,如下:
private Map<Object, Object> targetDataSources;targetDataSources是一个Map结构,所有需要切换的数据源都存放在其中,根据指定的KEY进行切换。当然还有一个默认的数据源。
AbstractRoutingDataSource这个抽象类中有一个抽象方法需要子类实现,如下:
protected abstract Object determineCurrentLookupKey();determineCurrentLookupKey()这个方法的返回值决定了需要切换的数据源的KEY,就是根据这个KEY从targetDataSources取值(数据源)。
数据源切换如何保证线程隔离?数据源属于一个公共的资源,在多线程的情况下如何保证线程隔离呢?不能我这边切换了影响其他线程的执行。
说到线程隔离,自然会想到ThreadLocal了,将切换数据源的KEY(用于从targetDataSources中取值)存储在ThreadLocal中,执行结束之后清除即可。
单独封装了一个DataSourceHolder,内部使用ThreadLocal隔离线程,代码如下:
/** * 使用ThreadLocal存储切换数据源后的KEY */ public class DataSourceHolder { //线程 本地环境 private static final ThreadLocal<String> dataSources = new InheritableThreadLocal(); //设置数据源 public static void setDataSource(String datasource) { dataSources.set(datasource); } //获取数据源 public static String getDataSource() { return dataSources.get(); } //清除数据源 public static void clearDataSource() { dataSources.remove(); } } 如何构造一个动态数据源?上文说过只需继承一个抽象类AbstractRoutingDataSource,重写其中的一个方法determineCurrentLookupKey()即可。代码如下:
/** * 动态数据源,继承AbstractRoutingDataSource */ public class DynamicDataSource extends AbstractRoutingDataSource { /** * 返回需要使用的数据源的key,将会按照这个KEY从Map获取对应的数据源(切换) * @return */ @Override protected Object determineCurrentLookupKey() { //从ThreadLocal中取出KEY return DataSourceHolder.getDataSource(); } /** * 构造方法填充Map,构建多数据源 */ public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) { //默认的数据源,可以作为主数据源 super.setDefaultTargetDataSource(defaultTargetDataSource); //目标数据源 super.setTargetDataSources(targetDataSources); //执行afterPropertiesSet方法,完成属性的设置 super.afterPropertiesSet(); } }上述代码很简单,分析如下:
一个多参的构造方法,指定了默认的数据源和目标数据源。