mybatis使用其它数据源

情景

在一个仅仅作为入库的项目中使用mybatis,由于不想使用mybatis中自带的数据源而引发的使用其他数据源时该如何做,本次写一个简单的例子,使用阿里开源的durid数据源

源码地址:



pom.xml配置

<dependency>
	<groupId>org.mybatis</groupId>
	<artifactId>mybatis</artifactId>
	<version>3.2.8</version>
</dependency>
<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<version>5.1.26</version>
</dependency>
<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>druid</artifactId>
	<version>1.1.6</version>
</dependency>

mybatis源码分析

mybatis-conf.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "mybatis-3-config.dtd" >
<configuration>
	<environments default="development">
	  <environment id="development">
	    <transactionManager type="JDBC" />
	    <dataSource type="DRUID_POOLED">
	      <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
	      <property name="username" value="******"/>
	      <property name="password" value="******"/>
	    </dataSource>
	  </environment>
	</environments>
	<mappers>
		<mapper resource="mybatis/mapper/studentMapper.xml"/>
	</mappers>
</configuration>


mybatais读取完mybatis-conf.xml配置后真正的解析是有XMLConfigBuilder类中解析,解析的过程在parseConfiguration方法中,解析mybatis使用的数据源是在这个方法的解析mybatis环境配置步骤中environmentsElement(root.evalNode("environments"))

private void parseConfiguration(XNode root) {
    try {
      propertiesElement(root.evalNode("properties")); //issue #117 read properties first
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));  //进行mybatis的事务管理器和数据源的解析
      settingsElement(root.evalNode("settings"));
      environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

这个方法主要是解析mybatis使用的事务管理器和数据源
 private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
      if (environment == null) {
        environment = context.getStringAttribute("default");
      }
      for (XNode child : context.getChildren()) {
        String id = child.getStringAttribute("id");
        if (isSpecifiedEnvironment(id)) {
          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
          DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
          DataSource dataSource = dsFactory.getDataSource();
          Environment.Builder environmentBuilder = new Environment.Builder(id)
              .transactionFactory(txFactory)
              .dataSource(dataSource);
          configuration.setEnvironment(environmentBuilder.build());
        }
      }
    }
  }

child.evalNode("dataSource")获取到mybatis-conf.xml中的dataSource节点交给dataSourceElement方法处理

private DataSourceFactory dataSourceElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type");
      Properties props = context.getChildrenAsProperties();
      DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
      factory.setProperties(props);
      return factory;
    }
    throw new BuilderException("Environment declaration requires a DataSourceFactory.");
  }

dataSourceElement方法获取该节点的type属性去resolveClass方法中获取和type属性字符串对应的DataSourceFactory实现类,跟踪这个方法知道在XMLConfigBuilder父类BaseBuilder中有一个TypeAliasRegistry类型的类型别名注册器typeAliasRegistry,这个对象是从Configuration中获取

public abstract class BaseBuilder {
  protected final Configuration configuration;
  protected final TypeAliasRegistry typeAliasRegistry;

  public BaseBuilder(Configuration configuration) {
    this.configuration = configuration;
    this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
  }
}

在实例化XMLConfigBuilder时同时实例化一个Configuration对象交给父类

 private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
  }

实例化Configuration对象时,向这个别名注册注册可能使用到的类型,加粗的就是mybatis内置的三种数据源工厂

public Configuration() {
    typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
    typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);

    typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
    typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
    typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);

    typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
    typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
    typeAliasRegistry.registerAlias("LRU", LruCache.class);
    typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
    typeAliasRegistry.registerAlias("WEAK", WeakCache.class);

    typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);

    typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
    typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);

    typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
    typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
    typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
    typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
    typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
    typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
    typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);

    typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
    typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);

    languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
    languageRegistry.register(RawLanguageDriver.class);
  }

这样就可以去实现一个DataSourceFactory接口的类,模仿着PooledDataSourceFactory工厂类
public class PooledDataSourceFactory extends UnpooledDataSourceFactory {
  public PooledDataSourceFactory() {
    this.dataSource = new PooledDataSource();
  }
}
写一个DruidDataSourceSqlSessionFactory工厂类,这个数据源是druid数据源
public class DruidDataSourceSqlSessionFactory extends UnpooledDataSourceFactory{
	public DruidDataSourceSqlSessionFactory() {
		this.dataSource = new DruidDataSource();
	}
}

现在需要将DruidDataSourceSqlSessionFactory工厂类注册到Configuration中经过查看,在实例化SqlSessionFactoryBuilder时同时实例化一个XMLConfigBuilder,而这个parser对象中就有需要的Configuration对象,在创建完parser之后和return之前将DruidDataSourceSqlSessionFactory注册到Configuration中
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        reader.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

写一个DataSourceSqlSessionFactoryBuilder类继承SqlSessionFactoryBuilder重写build方法,将DruidDataSourceSqlSessionFactory注册到Configuration对象的typeAliasRegistry对象中
public SqlSessionFactory build(Reader reader, String environment,
			Properties properties) {
		try {
			XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment,
					properties);
			parser.getConfiguration().getTypeAliasRegistry().registerAlias("DRUID_POOLED", DruidDataSourceSqlSessionFactory.class);
			return build(parser.parse());
		} catch (Exception e) {
			throw ExceptionFactory.wrapException("Error building SqlSession.",
					e);
		} finally {
			ErrorContext.instance().reset();
			try {
				reader.close();
			} catch (IOException e) {
				// Intentionally ignore. Prefer previous error.
			}
		}
	}

通过这个配置就可以使用到注册进去的数据源
<dataSource type="DRUID_POOLED">
    <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
    <property name="username" value="******"/>
    <property name="password" value="******"/>
</dataSource>
使用方法
public class MybatisTest {
	@Test
	public void test1(){
		InputStream is = getClass().getClassLoader().getResourceAsStream("mybatis/mybatis-config.xml");
		SqlSessionFactory sqlSessionFactory = new DataSourceSqlSessionFactoryBuilder().build(is);
		SqlSession session = sqlSessionFactory.openSession(true);
		StudentMapper mapper = session.getMapper(StudentMapper.class);
		Student stu = new Student();
		stu.setId(4);
		stu.setName("小明");
		mapper.add(stu);
	}
}

还有一种方法,就是将实现的工厂类全名复制给type这样就需要将DataSourceSqlSessionFactoryBuilder注册进Configuration中

 <environment id="development">
    <transactionManager type="JDBC" />
    <dataSource type="com.ws.extend.mybatis.DruidDataSourceSqlSessionFactory">
    <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
    <property name="username" value="root"/>
    <property name="password" value="root"/>
    </dataSource>
</environment>


因为根据别名获取不到时就会根据类全名反射获取该类型
public <T> Class<T> resolveAlias(String string) {
    try {
      if (string == null) return null;
      String key = string.toLowerCase(Locale.ENGLISH); // issue #748
      Class<T> value;
      if (TYPE_ALIASES.containsKey(key)) {
        value = (Class<T>) TYPE_ALIASES.get(key);
      } else {
        value = (Class<T>) Resources.classForName(string);
      }
      return value;
    } catch (ClassNotFoundException e) {
      throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);
    }
  }




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值