01MyBatis启动流程分析

database

目录

  • MyBatis简单介绍
  • 启动流程分析
  • 简单总结
  • 附录

    • MyBatis内置别名转换
  • 参考



MyBatis简单介绍

MyBatis是一个持久层框架,使用简单,学习成本较低。可以执行自己手写的SQL语句,比较灵活。但是MyBatis的自动化程度不高,移植性也不高,有时从一个数据库迁移到另外一个数据库的时候需要自己修改配置。

一个Mybatis最简单的使用列子如下:

public class UserDaoTest {

private SqlSessionFactory sqlSessionFactory;

@Before

public void setUp() throws Exception{

ClassPathResource resource = new ClassPathResource("mybatis-config.xml");

InputStream inputStream = resource.getInputStream();

sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

}

@Test

public void selectUserTest(){

String id = "{0003CCCA-AEA9-4A1E-A3CC-06D884BA3906}";

SqlSession sqlSession = sqlSessionFactory.openSession();

CbondissuerMapper cbondissuerMapper = sqlSession.getMapper(CbondissuerMapper.class);

Cbondissuer cbondissuer = cbondissuerMapper.selectByPrimaryKey(id);

System.out.println(cbondissuer);

sqlSession.close();

}

}

  • 从配置文件(通常是XML文件)得到SessionFactory;
  • 从SessionFactory得到SQLSession;
  • 通过SqlSession进行CRUD和事务的操作;
  • 执行完相关操作之后关闭Session。

启动流程分析

本博客只涉及创建SessionFactory,以及从SessionFactory获取SqlSession的流程。具体执行Sql的流程会在其他博客中分析。

ClassPathResource resource = new ClassPathResource("mybatis-config.xml");

InputStream inputStream = resource.getInputStream();

sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

通过上面代码发现,创建SqlSessionFactory的代码在SqlSessionFactoryBuilder中,进去一探究竟:

//整个过程就是将配置文件解析成Configration对象,然后创建SqlSessionFactory的过程

//Configuration是SqlSessionFactory的一个内部属性

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {

try {

XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);

return build(parser.parse());

} catch (Exception e) {

throw ExceptionFactory.wrapException("Error building SqlSession.", e);

} finally {

ErrorContext.instance().reset();

try {

inputStream.close();

} catch (IOException e) {

// Intentionally ignore. Prefer previous error.

}

}

}

public SqlSessionFactory build(Configuration config) {

return new DefaultSqlSessionFactory(config);

}

下面我们看下解析配置文件过程中的一些细节。

先给出一个配置文件的列子:

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE configuration

PUBLIC "-//mybatis.org//DTD Config 3.0//EN"

"http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

<!--SqlSessionFactoryBuilder中配置的配置文件的优先级最高;config.properties配置文件的优先级次之;properties标签中的配置优先级最低 -->

<properties resource="org/mybatis/example/config.properties">

<property name="username" value="dev_user"/>

<property name="password" value="F2Fa3!33TYyg"/>

</properties>

<!--一些重要的全局配置-->

<settings>

<setting name="cacheEnabled" value="true"/>

<!--<setting name="lazyLoadingEnabled" value="true"/>-->

<!--<setting name="multipleResultSetsEnabled" value="true"/>-->

<!--<setting name="useColumnLabel" value="true"/>-->

<!--<setting name="useGeneratedKeys" value="false"/>-->

<!--<setting name="autoMappingBehavior" value="PARTIAL"/>-->

<!--<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>-->

<!--<setting name="defaultExecutorType" value="SIMPLE"/>-->

<!--<setting name="defaultStatementTimeout" value="25"/>-->

<!--<setting name="defaultFetchSize" value="100"/>-->

<!--<setting name="safeRowBoundsEnabled" value="false"/>-->

<!--<setting name="mapUnderscoreToCamelCase" value="false"/>-->

<!--<setting name="localCacheScope" value="STATEMENT"/>-->

<!--<setting name="jdbcTypeForNull" value="OTHER"/>-->

<!--<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>-->

<!--<setting name="logImpl" value="STDOUT_LOGGING" />-->

</settings>

<typeAliases>

</typeAliases>

<plugins>

<plugin interceptor="com.github.pagehelper.PageInterceptor">

<!--默认值为 false,当该参数设置为 true 时,如果 pageSize=0 或者 RowBounds.limit = 0 就会查询出全部的结果-->

<!--如果某些查询数据量非常大,不应该允许查出所有数据-->

<property name="pageSizeZero" value="true"/>

</plugin>

</plugins>

<environments default="development">

<environment id="development">

<transactionManager type="JDBC"/>

<dataSource type="POOLED">

<property name="driver" value="com.mysql.jdbc.Driver"/>

<property name="url" value="jdbc:mysql://10.59.97.10:3308/windty"/>

<property name="username" value="windty_opr"/>

<property name="password" value="windty!234"/>

</dataSource>

</environment>

</environments>

<databaseIdProvider type="DB_VENDOR">

<property name="MySQL" value="mysql" />

<property name="Oracle" value="oracle" />

</databaseIdProvider>

<mappers>

<!--这边可以使用package和resource两种方式加载mapper-->

<!--<package name="包名"/>-->

<!--<mapper resource="./mappers/SysUserMapper.xml"/>-->

<mapper resource="./mappers/CbondissuerMapper.xml"/>

</mappers>

</configuration>

下面是解析配置文件的核心方法:

private void parseConfiguration(XNode root) {

try {

//issue #117 read properties first

//解析properties标签,并set到Configration对象中

//在properties配置属性后,在Mybatis的配置文件中就可以使用${key}的形式使用了。

propertiesElement(root.evalNode("properties"));

//解析setting标签的配置

Properties settings = settingsAsProperties(root.evalNode("settings"));

//添加vfs的自定义实现,这个功能不怎么用

loadCustomVfs(settings);

//配置类的别名,配置后就可以用别名来替代全限定名

//mybatis默认设置了很多别名,参考附录部分

typeAliasesElement(root.evalNode("typeAliases"));

//解析拦截器和拦截器的属性,set到Configration的interceptorChain中

//MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

//Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)

//ParameterHandler (getParameterObject, setParameters)

//ResultSetHandler (handleResultSets, handleOutputParameters)

//StatementHandler (prepare, parameterize, batch, update, query)

pluginElement(root.evalNode("plugins"));

//Mybatis创建对象是会使用objectFactory来创建对象,一般情况下不会自己配置这个objectFactory,使用系统默认的objectFactory就好了

objectFactoryElement(root.evalNode("objectFactory"));

objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));

reflectorFactoryElement(root.evalNode("reflectorFactory"));

//设置在setting标签中配置的配置

settingsElement(settings);

//解析环境信息,包括事物管理器和数据源,SqlSessionFactoryBuilder在解析时需要指定环境id,如果不指定的话,会选择默认的环境;

//最后将这些信息set到Configration的Environment属性里面

environmentsElement(root.evalNode("environments"));

//

databaseIdProviderElement(root.evalNode("databaseIdProvider"));

//无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。解析typeHandler。

typeHandlerElement(root.evalNode("typeHandlers"));

//解析Mapper

mapperElement(root.evalNode("mappers"));

} catch (Exception e) {

throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);

}

}

上面解析流程结束后会生成一个Configration对象,包含所有配置信息,然后会创建一个SqlSessionFactory对象,这个对象包含了Configration对象。

下面是openSession的过程:

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {

Transaction tx = null;

try {

final Environment environment = configuration.getEnvironment();

final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);

tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);

//获取执行器,这边获得的执行器已经代理拦截器的功能(见下面代码)

final Executor executor = configuration.newExecutor(tx, execType);

//根据获取的执行器创建SqlSession

return new DefaultSqlSession(configuration, executor, autoCommit);

} catch (Exception e) {

closeTransaction(tx); // may have fetched a connection so lets call close()

throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);

} finally {

ErrorContext.instance().reset();

}

}

//interceptorChain生成代理类,具体参见Plugin这个类的方法

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {

executorType = executorType == null ? defaultExecutorType : executorType;

executorType = executorType == null ? ExecutorType.SIMPLE : executorType;

Executor executor;

if (ExecutorType.BATCH == executorType) {

executor = new BatchExecutor(this, transaction);

} else if (ExecutorType.REUSE == executorType) {

executor = new ReuseExecutor(this, transaction);

} else {

executor = new SimpleExecutor(this, transaction);

}

if (cacheEnabled) {

executor = new CachingExecutor(executor);

}

executor = (Executor) interceptorChain.pluginAll(executor);

return executor;

}

到此为止,我们已经获得了SqlSession,拿到SqlSession就可以执行各种CRUD方法了。

简单总结

对于MyBatis启动的流程(获取SqlSession的过程)这边简单总结下:

  • SqlSessionFactoryBuilder解析配置文件,包括属性配置、别名配置、拦截器配置、环境(数据源和事务管理器)、Mapper配置等;解析完这些配置后会生成一个Configration对象,这个对象中包含了MyBatis需要的所有配置,然后会用这个Configration对象创建一个SqlSessionFactory对象,这个对象中包含了Configration对象;
  • 拿到SqlSessionFactory对象后,会调用SqlSessionFactory的openSesison方法,这个方法会创建一个Sql执行器(Executor),这个Sql执行器会代理你配置的拦截器方法。
  • 获得上面的Sql执行器后,会创建一个SqlSession(默认使用DefaultSqlSession),这个SqlSession中也包含了Configration对象,所以通过SqlSession也能拿到全局配置;
  • 获得SqlSession对象后就能执行各种CRUD方法了。

SQL的具体执行流程见后续博客。

一些重要类总结:

  • SqlSessionFactory
  • SqlSessionFactoryBuilder
  • SqlSession(默认使用DefaultSqlSession)
  • Plugin、InterceptorChain的pluginAll方法

附录

MyBatis内置别名转换

//TypeAliasRegistry

registerAlias("string", String.class);

registerAlias("byte", Byte.class);

registerAlias("long", Long.class);

registerAlias("short", Short.class);

registerAlias("int", Integer.class);

registerAlias("integer", Integer.class);

registerAlias("double", Double.class);

registerAlias("float", Float.class);

registerAlias("boolean", Boolean.class);

registerAlias("byte[]", Byte[].class);

registerAlias("long[]", Long[].class);

registerAlias("short[]", Short[].class);

registerAlias("int[]", Integer[].class);

registerAlias("integer[]", Integer[].class);

registerAlias("double[]", Double[].class);

registerAlias("float[]", Float[].class);

registerAlias("boolean[]", Boolean[].class);

registerAlias("_byte", byte.class);

registerAlias("_long", long.class);

registerAlias("_short", short.class);

registerAlias("_int", int.class);

registerAlias("_integer", int.class);

registerAlias("_double", double.class);

registerAlias("_float", float.class);

registerAlias("_boolean", boolean.class);

registerAlias("_byte[]", byte[].class);

registerAlias("_long[]", long[].class);

registerAlias("_short[]", short[].class);

registerAlias("_int[]", int[].class);

registerAlias("_integer[]", int[].class);

registerAlias("_double[]", double[].class);

registerAlias("_float[]", float[].class);

registerAlias("_boolean[]", boolean[].class);

registerAlias("date", Date.class);

registerAlias("decimal", BigDecimal.class);

registerAlias("bigdecimal", BigDecimal.class);

registerAlias("biginteger", BigInteger.class);

registerAlias("object", Object.class);

registerAlias("date[]", Date[].class);

registerAlias("decimal[]", BigDecimal[].class);

registerAlias("bigdecimal[]", BigDecimal[].class);

registerAlias("biginteger[]", BigInteger[].class);

registerAlias("object[]", Object[].class);

registerAlias("map", Map.class);

registerAlias("hashmap", HashMap.class);

registerAlias("list", List.class);

registerAlias("arraylist", ArrayList.class);

registerAlias("collection", Collection.class);

registerAlias("iterator", Iterator.class);

registerAlias("ResultSet", ResultSet.class);

参考

https://blog.csdn.net/luanlouis/article/details/40422941

以上是 01MyBatis启动流程分析 的全部内容, 来源链接: utcz.com/z/531341.html

回到顶部