mybatis的准绳总括部份,Mybatis职业流程及其规律与
分类:计算机编程

Mybatis简介:

MyBatis的配置

MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架,其主要就完成2件事情:

MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。本文将通过debug的方式来了解其工作原理。

MyBatis框架和其他绝大部分框架一样,需要一个配置文件,其配置文件大致如下:

封装JDBC操作

Mybatis核心类:

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

利用反射打通Java类与SQL语句之间的相互转换

SqlSessionFactory:每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为中心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或通过Java的方式构建出 SqlSessionFactory 的实例。SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,建议使用单例模式或者静态单例模式。一个SqlSessionFactory对应配置文件中的一个环境(environment),如果你要使用多个数据库就配置多个环境分别对应一个SqlSessionFactory。

<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" ";

MyBatis的主要设计目的就是让我们对执行SQL语句时对输入输出的数据管理更加方便,所以方便地写出SQL

SqlSession:SqlSession是一个接口,它有2个实现类,分别是DefaultSqlSession以及SqlSessionManager。SqlSession通过内部存放的执行器来对数据进行CRUD。此外SqlSession不是线程安全的,因为每一次操作完数据库后都要调用close对其进行关闭,官方建议通过try-finally来保证总是关闭SqlSession。

<configuration>

和方便地获取SQL的执行结果才是MyBatis的核心竞争力

Executor:Executor接口有两个实现类,其中BaseExecutor有三个继承类分别是BatchExecutor(重用语句并执行批量更新),ReuseExecutor(重用预处理语句prepared statements),SimpleExecutor。以上三个就是主要的Executor。通过下图可以看到Mybatis在Executor的设计上面使用了装饰者模式,我们可以用CachingExecutor来装饰前面的三个执行器目的就是用来实现缓存。

<settings>

MyBatis的主要成员

图片 1image

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

Configuration        MyBatis所有的配置信息都保存在Configuration对象之中,配置文件中的大部分

MappedStatement:MappedStatement就是用来存放我们SQL映射文件中的信息包括sql语句,输入参数,输出参数等等。一个SQL节点对应一个MappedStatement对象。

<setting name="lazyLoadingEnabled" value="false"/>

    配置都会存储到该类中

Mybatis工作流程:

<!--<setting name="logImpl" value="STDOUT_LOGGING"/> <!– 打印日志信息 –>-->

SqlSession          作为MyBatis工作的主要顶层API,表示和数据库交互时的会话,

图片 2image

</settings>

    完成必要数据库增删改查功能

阅读全文有惊喜哦!!!

<typeAliases>

Configuration        MyBatis所有的配置信息都保存在Configuration对象之中,配置文件中的大部分配置都会存储到该类中

下面将通过debug方式对Mybatis进行一步步解析。首先贴出我的mybatis-config.xml文件以及Mapper.xml文件。

<typeAlias type="com.luo.dao.UserDao" alias="User"/>

SqlSession            作为MyBatis工作的主要顶层API,表示和数据库交互时的会话,完成必要数据库增删改查功能

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

</typeAliases>

Executor               MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护

";

<environments default="development">

StatementHandler 封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数等

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

<environment >

ParameterHandler  负责对用户传递的参数转换成JDBC Statement 所对应的数据类型

";

<transactionManager type="JDBC"/> <!--事务管理类型-->

ResultSetHandler   负责将JDBC返回的ResultSet结果集对象转换成List类型的集合

select * from user

<dataSource type="POOLED">

TypeHandler          负责java数据类型和jdbc数据类型(也可以说是数据表列类型)之间的映射和转换

User where id = #{id}

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

MappedStatement  MappedStatement维护一条节点的封装

insert into User (username,birthday,sex,address)

<property name="password" value="123456"/>

SqlSource              负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回

values (#{name},#{birthday},#{sex},#{address})

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

BoundSql              表示动态生成的SQL语句以及相应的参数信息

update User set username = #{username},birthday = #{birthday},

<property name="url" value="jdbc:mysql://192.168.1.150/ssh_study"/>

以上主要成员在一次数据库操作中基本都会涉及,在SQL操作中重点需要关注的是SQL参数什么时候被设置和结果集怎么转换为JavaBean对象的,这两个过程正好对应StatementHandler和ResultSetHandler类中的处理逻辑。

sex = #{sex},address = #{address} where id = #{id}

</dataSource>

MyBatis的初始化

delete from User where id = #{id}

</environment>

MyBatis的初始化的过程其实就是解析配置文件和初始化Configuration的过程,MyBatis的初始化过程可用以下几行代码来表述:

select * from User where sex = #{param1}

</environments>

String resource = "mybatis.xml";// 加载mybatis的配置文件(它也加载关联的映射文件)InputStream inputStream =null;try {

and username like #{param2}

<mappers>

    inputStream = Resources.getResourceAsStream(resource);

and address = #{parma3}

<mapper resource="userMapper.xml"/>

} catch (IOException e) {

select count from user where username like #{username}

</mappers>

    e.printStackTrace();

username like #{pattern}

</configuration>

}// 构建sqlSession的工厂sessionFactory =newSqlSessionFactoryBuilder().build(inputStream);

and sex = #{sex}

以上配置中,最重要的是数据库参数的配置,比如用户名密码等,如果配置了数据表对应的mapper文件,则需要将其加入到<mappers>节点下。

首先会创建SqlSessionFactory建造者对象,然后由它进行创建SqlSessionFactory。这里用到的是建造者模式,建造者模式最简单的理解就是不手动new对象,而是由其他类来进行对象的创建。

and address = #{address}

MyBatis的主要成员

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

where id in

Configuration:MyBatis所有的配置信息都保存在Configuration对象之中,配置文件中的大部分配置都会存储到该类中。

    try {

图片 3image

SqlSession:作为MyBatis工作的主要顶层API,表示和数据库交互时的会话,完成必要数据库增删改查功能。

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

第一步通过SqlSessionFactoryBuilder创建SqlSessionFactory:

Executor:MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护。

        returnbuild(parser.parse());// 开始进行解析了 :)}catch (Exception e) {

首先在SqlSessionFactoryBuilder的build()方法中可以看到MyBatis内部定义了一个类XMLConfigBuilder用来解析配置文件mybatis-config.xml。针对配置文件中的每一个节点进行解析并将数据存放到Configuration这个对象中,紧接着使用带有Configuration的构造方法发返回一个DefautSqlSessionFactory。

StatementHandler:封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数等。

        throwExceptionFactory.wrapException("Error building SqlSession.", e);

public SqlSessionFactory build(InputStream inputStream) {

ParameterHandler:负责对用户传递的参数转换成JDBC Statement所对应的数据类型。

    } finally {

return build(inputStream, null, null);

ResultSetHandler:负责将JDBC返回的ResultSet结果集对象转换成List类型的集合。

        ErrorContext.instance().reset();

}

TypeHandler:负责java数据类型和jdbc数据类型(也可以说是数据表列类型)之间的映射和转换。

        try {

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

MappedStatement:MappedStatement维护一条<select|update|delete|insert>节点的封装。

            inputStream.close();

try {

SqlSource:负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回。

        } catch (IOException e) {

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

BoundSql:表示动态生成的SQL语句以及相应的参数信息。

            // Intentionally ignore. Prefer previous error.        }

//解析mybatis-config.xml

以上主要成员在一次数据库操作中基本都会涉及,在SQL操作中重点需要关注的是SQL参数什么时候被设置和结果集怎么转换为JavaBean对象的,这两个过程正好对应StatementHandler和ResultSetHandler类中的处理逻辑。

    }

return build(parser.parse;

图片 4

}

} catch (Exception e) {

MyBatis的初始化

XMLConfigBuilder对象会进行XML配置文件的解析,实际为configuration节点的解析操作。

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

MyBatis的初始化的过程其实就是解析配置文件和初始化Configuration的过程,MyBatis的初始化过程可用以下几行代码来表述:

// XMLConfigBuilder类public Configuration parse() {

} finally {

String resource = "mybatis.xml";

    if (parsed) {

ErrorContext.instance;

// 加载mybatis的配置文件(它也加载关联的映射文件)

        thrownewBuilderException("Each XMLConfigBuilder can only be used once.");

try {

InputStream inputStream = null;

    }

inputStream.close();

try {

    parsed =true;

} catch (IOException e) {

inputStream = Resources.getResourceAsStream;

    parseConfiguration(parser.evalNode("/configuration"));

// Intentionally ignore. Prefer previous error.

} catch (IOException e) {

    return configuration;

}

e.printStackTrace();

}privatevoid parseConfiguration(XNode root) {

}

}

    try {

}

// 构建sqlSession的工厂

        //issue #117 read properties firstpropertiesElement(root.evalNode("properties"));

//返回SqlSessionFactory,默认使用的是实现类DefaultSqlSessionFactory

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

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

public SqlSessionFactory build(Configuration config) {

首先会创建SqlSessionFactory建造者对象,然后由它进行创建SqlSessionFactory。这里用到的是建造者模式,建造者模式最简单的理解就是不手动new对象,而是由其他类来进行对象的创建。

        loadCustomVfs(settings);

return new DefaultSqlSessionFactory;

// SqlSessionFactoryBuilder类

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

}

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

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

public Configuration parse() {

try {

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

if {

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

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

throw new BuilderException("Each XMLConfigBuilder can only be used once.");

return build(parser.parse; // 开始进行解析了 :)

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

}

} catch (Exception e) {

        settingsElement(settings);

parsed = true;

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

        // read it after objectFactory and objectWrapperFactory issue #631/* 处理environments节点数据 */        environmentsElement(root.evalNode("environments"));

//获取根节点configuration

} finally {

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

parseConfiguration(parser.evalNode("/configuration"));

ErrorContext.instance;

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

return configuration;

try {

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

}

inputStream.close();

    } catch (Exception e) {

//开始解析mybatis-config.xml,并把解析后的数据存放到configuration中

} catch (IOException e) {

        thrownewBuilderException("Error parsing SQL Mapper Configuration. Cause: " e, e);

private void parseConfiguration(XNode root) {

// Intentionally ignore. Prefer previous error.

    }

try {

}

}

//保存mybatis-config.xml中的标签setting,本例中开启全局缓存cacheEnabled,设置默认执行器defaultExecutorType=REUSE

}

在configuration节点下会依次解析properties/settings/.../mappers等节点配置。在解析environments节点时,会根据transactionManager的配置来创建事务管理器,根据dataSource的配置来创建DataSource对象,这里面包含了数据库登录的相关信息。在解析mappers节点时,会读取该节点下所有的mapper文件,然后进行解析,并将解析后的结果存到configuration对象中。

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

}

// XMLConfigBuilder类privatevoidenvironmentsElement(XNode context)throws Exception {

//issue #117 read properties first

XMLConfigBuilder对象会进行XML配置文件的解析,实际为configuration节点的解析操作。

    if(context !=null) {

//解析是否配置了外部properties,例如本例中配置的jdbc.propertis

// XMLConfigBuilder类

        if(environment ==null) {

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

public Configuration parse() {

            environment = context.getStringAttribute("default");

//查看是否配置了VFS,默认没有,本例也没有使用

if {

        }

loadCustomVfs;

throw new BuilderException("Each XMLConfigBuilder can only be used once.");

        for (XNode child : context.getChildren()) {

//查看是否用了类型别名,减少完全限定名的冗余,本例中使用了别名User代替了com.ctc.Model.User

}

            String id = child.getStringAttribute("id");

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

parsed = true;

            if (isSpecifiedEnvironment(id)) {

//查看是否配置插件来拦截映射语句的执行,例如拦截Executor的Update方法,本例没有使用

parseConfiguration(parser.evalNode("/configuration"));

                /* 创建事务管理器 */                TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));

pluginElement(root.evalNode("plugins"))

return configuration;

                DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));

//查看是否配置了ObjectFactory,默认情况下使用对象的无参构造方法或者是带有参数的构造方法,本例没有使用

}

                DataSource dataSource = dsFactory.getDataSource();

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

private void parseConfiguration(XNode root) {

                /* 建造者模式 设计模式 */                Environment.Builder environmentBuilder =new Environment.Builder(id)

//查看是否配置了objectWrapperFatory,这个用来或者ObjectWapper,可以访问:对象,Collection,Map属性。本例没有使用

try {

                        .transactionFactory(txFactory)

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

//issue #117 read properties first

                        .dataSource(dataSource);

//查看是否配置了reflectorFactory,mybatis的反射工具,提供了很多反射方法。本例没有使用

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

                configuration.setEnvironment(environmentBuilder.build());

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

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

            }

//放入参数到configuration对象中

loadCustomVfs;

        }

settingsElement;

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

    }

// read it after objectFactory and objectWrapperFactory issue #631

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

}// 解析单独的mapper文件privatevoidmapperElement(XNode parent)throws Exception {

//查看数据库环境配置

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

    if(parent !=null) {

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

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

      for (XNode child : parent.getChildren()) {

//查看是否使用多种数据库,本例没有使用

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

        if("package".equals(child.getName())) {

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

settingsElement;

          String mapperPackage = child.getStringAttribute("name");

//查看是否配置了新的类型处理器,如果跟处理的类型跟默认的一致就会覆盖。本例没有使用

// read it after objectFactory and objectWrapperFactory issue #631

          configuration.addMappers(mapperPackage);

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

/* 处理environments节点数据 */

        } else {

//查看是否配置SQL映射文件,有四种配置方式,resource,url,class以及自动扫包package。本例使用package

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

          String resource = child.getStringAttribute("resource");

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

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

          String url = child.getStringAttribute("url");

} catch (Exception e) {

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

          String mapperClass = child.getStringAttribute("class");

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

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

          if(resource !=null&& url ==null&& mapperClass ==null) {

}

} catch (Exception e) {

            ErrorContext.instance().resource(resource);

}

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

            InputStream inputStream = Resources.getResourceAsStream(resource);

第二步通过SqlSessionFactory创建SqlSession:

}

            XMLMapperBuilder mapperParser =new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());

@Override

}

            mapperParser.parse(); // 开始解析mapper文件了 :)}elseif(resource ==null&& url !=null&& mapperClass ==null) {

public SqlSession openSession() {

在configuration节点下会依次解析properties/settings/.../mappers等节点配置。在解析environments节点时,会根据transactionManager的配置来创建事务管理器,根据dataSource的配置来创建DataSource对象,这里面包含了数据库登录的相关信息。在解析mappers节点时,会读取该节点下所有的mapper文件,然后进行解析,并将解析后的结果存到configuration对象中。

            ErrorContext.instance().resource(url);

return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);

// XMLConfigBuilder类

            InputStream inputStream = Resources.getUrlAsStream(url);

}

private void environmentsElement(XNode context) throws Exception {

            XMLMapperBuilder mapperParser =new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());

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

if (context != null) {

            mapperParser.parse();

Transaction tx = null;

if (environment == null) {

          } elseif(resource ==null&& url ==null&& mapperClass !=null) {

try {

environment = context.getStringAttribute("default");

            Class mapperInterface = Resources.classForName(mapperClass);

//拿到前文从mybatis中解析到的数据库环境配置

}

            configuration.addMapper(mapperInterface);

final Environment environment = configuration.getEnvironment();

for (XNode child : context.getChildren {

          } else {

final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);

String id = child.getStringAttribute;

            thrownewBuilderException("A mapper element may only specify a url, resource or class, but not more than one.");

//拿到jdbc的事务管理器,有两种一种是jbc,一种的managed。本例使用的是JdbcTransaction

if (isSpecifiedEnvironment {

          }

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

/* 创建事务管理器 */

        }

//从mybatis配置文件可以看到本例使用了REUSE,因此返回的是ReuseExecutor并把事务传入对象中

TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));

      }

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

DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));

    }

return new DefaultSqlSession(configuration, executor, autoCommit);

DataSource dataSource = dsFactory.getDataSource();

  }

} catch (Exception e) {

/* 建造者模式 设计模式 */

解析完MyBatis配置文件后,configuration就初始化完成了,然后根据configuration对象来创建SqlSession,到这里时,MyBatis的初始化的征程已经走完了。

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

Environment.Builder environmentBuilder = new Environment.Builder

// SqlSessionFactoryBuilder类public SqlSessionFactory build(Configuration config) {

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

.transactionFactory(txFactory)

    returnnew DefaultSqlSessionFactory(config);

} finally {

.dataSource(dataSource);

}

ErrorContext.instance;

configuration.setEnvironment(environmentBuilder.build;

MyBatis的SQL查询流程

}

}

SQL语句的执行才是MyBatis的重要职责,该过程就是通过封装JDBC进行操作,然后使用Java反射技术完成JavaBean对象到数据库参数之间的相互转换,这种映射关系就是有TypeHandler对象来完成的,在获取数据表对应的元数据时,会保存该表所有列的数据库类型,大致逻辑如下所示:

}

}

/* Get resultSet metadata */ResultSetMetaData metaData = resultSet.getMetaData();intcolumn = metaData.getColumnCount();for(inti = 1; i <= column; i ) {

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {

}

    JdbcType jdbcType = JdbcType.forCode(metaData.getColumnType(i));

executorType = executorType == null ? defaultExecutorType : executorType;

}

    typeHandlers.add(TypeHandlerRegistry.getTypeHandler(jdbcType));

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

// 解析单独的mapper文件

    columnNames.add(metaData.getColumnName(i));

Executor executor;

private void mapperElement(XNode parent) throws Exception {

}

if (ExecutorType.BATCH == executorType) {

if (parent != null) {

使用如下代码进行SQL查询操作:

executor = new BatchExecutor(this, transaction);

for (XNode child : parent.getChildren {

sqlSession = sessionFactory.openSession();

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

if ("package".equals(child.getName {

User user = sqlSession.selectOne("com.luo.dao.UserDao.getUserById", 1);

executor = new ReuseExecutor(this, transaction);

String mapperPackage = child.getStringAttribute;

System.out.println(user);

} else {

configuration.addMappers(mapperPackage);

创建sqlSession的过程其实就是根据configuration中的配置来创建对应的类,然后返回创建的sqlSession对象。调用selectOne方法进行SQL查询,selectOne方法最后调用的是selectList,在selectList中,会查询configuration中存储的MappedStatement对象,mapper文件中一个sql语句的配置对应一个MappedStatement对象,然后调用执行器进行查询操作。

executor = new SimpleExecutor(this, transaction);

} else {

// DefaultSqlSession类public T selectOne(String statement, Object parameter) {

}

String resource = child.getStringAttribute("resource");

    // Popular vote was to return null on 0 results and throw exception on too many.List list =this.selectList(statement, parameter);

if (cacheEnabled) {

String url = child.getStringAttribute;

    if(list.size() == 1) {

executor = new CachingExecutor;

String mapperClass = child.getStringAttribute;

        returnlist.get(0);

}

if (resource != null && url == null && mapperClass == null) {

    } elseif(list.size() > 1) {

executor = interceptorChain.pluginAll;

ErrorContext.instance().resource;

        thrownewTooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " list.size());

return executor;

InputStream inputStream = Resources.getResourceAsStream;

    } else {

}

XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments;

        returnnull;

//返回一个SqlSession,默认使用DefaultSqlSession

mapperParser.parse(); // 开始解析mapper文件了 :)

    }

public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {

} else if (resource == null && url != null && mapperClass == null) {

}public List selectList(String statement, Object parameter, RowBounds rowBounds) {

this.configuration = configuration;

ErrorContext.instance().resource;

    try {

this.executor = executor;

InputStream inputStream = Resources.getUrlAsStream;

        MappedStatement ms = configuration.getMappedStatement(statement);

this.dirty = false;

XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments;

        return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);

this.autoCommit = autoCommit;

mapperParser.parse();

    } catch (Exception e) {

}

} else if (resource == null && url == null && mapperClass != null) {

        throwExceptionFactory.wrapException("Error querying database.  Cause: " e, e);

第三步通过SqlSession拿到Mapper对象的代理:

Class<?> mapperInterface = Resources.classForName(mapperClass);

    } finally {

@Override

configuration.addMapper(mapperInterface);

        ErrorContext.instance().reset();

public T getMapper(Class type) {

} else {

    }

return configuration.getMapper(type, this);

throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");

}

}

}

执行器在query操作中,优先会查询缓存是否命中,命中则直接返回,否则从数据库中查询。

public T getMapper(Class type, SqlSession sqlSession) {

}

// CachingExecutor类public List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler)throws SQLException {

//前文解析Mybatis-config.xml的时候,在解析标签mapper就是用configuration对象的mapperRegistry存放数据

}

    /* 获取关联参数的sql,boundSql */    BoundSql boundSql = ms.getBoundSql(parameterObject);

return mapperRegistry.getMapper(type, sqlSession);

}

    /* 创建cache key值 */    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);

}

}

    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

@SuppressWarnings("unchecked")

解析完MyBatis配置文件后,configuration就初始化完成了,然后根据configuration对象来创建SqlSession,到这里时,MyBatis的初始化的征程已经走完了。

}public List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)

public T getMapper(Class type, SqlSession sqlSession) {

如果想学习Java工程化、高性能及分布式、深入浅出。微服务、Spring,MyBatis,Netty源码分析的朋友可以加我的Java高级交流:787707172,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给大家。

      throws SQLException {

//knownMapper是一个HashMap在存放mapperRegistry的过程中,以每个Mapper对象的类型为Key, MapperProxyFactory 为value保存。

// SqlSessionFactoryBuilder类

    /* 获取二级缓存实例 */    Cache cache = ms.getCache();

//例如本例中保存的就是Key:com.ctc.mapper.UserMapper,value就是保存了key的MapperProxyFactory对象

public SqlSessionFactory build(Configuration config) {

    if(cache !=null) {

final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get;

return new DefaultSqlSessionFactory;

        flushCacheIfRequired(ms);

if (mapperProxyFactory == null) {

}

        if(ms.isUseCache() && resultHandler ==null) {

throw new BindingException("Type " type " is not known to the MapperRegistry.");

MyBatis的SQL查询流程

            ensureNoOutParams(ms, parameterObject, boundSql);

}

SQL语句的执行才是MyBatis的重要职责,该过程就是通过封装JDBC进行操作,然后使用Java反射技术完成JavaBean对象到数据库参数之间的相互转换,这种映射关系就是有TypeHandler对象来完成的,在获取数据表对应的元数据时,会保存该表所有列的数据库类型,大致逻辑如下所示:

            @SuppressWarnings("unchecked")

try {

/* Get resultSet metadata */

            List list = (List) tcm.getObject(cache, key);

return mapperProxyFactory.newInstance(sqlSession);

ResultSetMetaData metaData = resultSet.getMetaData();

            if(list ==null) {

} catch (Exception e) {

int column = metaData.getColumnCount();

                list = delegate. query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

throw new BindingException("Error getting mapper instance. Cause: " e, e);

for (int i = 1; i <= column; i ) {

                tcm.putObject(cache, key, list); // issue #578 and #116            }

}

JdbcType jdbcType = JdbcType.forCode(metaData.getColumnType;

            return list;

}

typeHandlers.add(TypeHandlerRegistry.getTypeHandler);

        }

public T newInstance(SqlSession sqlSession) {

columnNames.add(metaData.getColumnName;

    }

//生成一个mapperProxy对象,这个对象实现了InvocationHandler, Serializable。就是JDK动态代理中的方法调用处理器

}

    returndelegate. query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache);

使用如下代码进行SQL查询操作:

}private List queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)throws SQLException {

return newInstance(mapperProxy);

sqlSession = sessionFactory.openSession();

    List list;

}

User user = sqlSession.selectOne("com.luo.dao.UserDao.getUserById", 1);

    /**    * 先往localCache中插入一个占位对象,这个地方

public MapperProxy(SqlSession sqlSession, Class mapperInterface, Map methodCache) {

System.out.println;

    */    localCache.putObject(key, EXECUTION_PLACEHOLDER);

this.sqlSession = sqlSession;

创建sqlSession的过程其实就是根据configuration中的配置来创建对应的类,然后返回创建的sqlSession对象。调用selectOne方法进行SQL查询,selectOne方法最后调用的是selectList,在selectList中,会查询configuration中存储的MappedStatement对象,mapper文件中一个sql语句的配置对应一个MappedStatement对象,然后调用执行器进行查询操作。

    try {

this.mapperInterface = mapperInterface;

// DefaultSqlSession类

        list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);

this.methodCache = methodCache;

public <T> T selectOne(String statement, Object parameter) {

    } finally {

}

// Popular vote was to return null on 0 results and throw exception on too many.

        localCache.removeObject(key);

@SuppressWarnings("unchecked")

List<T> list = this.<T>selectList(statement, parameter);

    }

protected T newInstance(MapperProxy mapperProxy) {

if (list.size {

    /* 往缓存中写入数据,也就是缓存查询结果 */    localCache.putObject(key, list);

//通过JDK动态代理生成一个Mapper的代理,在本例中的就是UserMapper的代理类,它实现了UserMapper接口

return list.get;

    if(ms.getStatementType() == StatementType.CALLABLE) {

return Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);

} else if (list.size {

        localOutputParameterCache.putObject(key, parameter);

}

throw new TooManyResultsException("Expected one result to be returned by selectOne(), but found: " list.size;

    }

第四步通过MapperProxy调用Maper中相应的方法:

} else {

    return list;

@Override

return null;

}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

}

真正的doQuery操作是由SimplyExecutor代理来完成的,该方法中有2个子流程,一个是SQL参数的设置,另一个是SQL查询操作和结果集的封装。

//判断当前调用的method是不是Object中声明的方法,如果是的话直接执行。

}

// SimpleExecutor类public List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)throws SQLException {

if (Object.class.equals(method.getDeclaringClass {

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {

    Statement stmt =null;

try {

try {

    try {

return method.invoke(this, args);

MappedStatement ms = configuration.getMappedStatement(statement);

        Configuration configuration = ms.getConfiguration();

} catch (Throwable t) {

return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);

        StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);

throw ExceptionUtil.unwrapThrowable;

} catch (Exception e) {

        /* 子流程1: SQL查询参数的设置 */        stmt = prepareStatement(handler, ms.getStatementLog());

}

throw ExceptionFactory.wrapException("Error querying database. Cause: "

        /* 子流程2: SQL查询操作和结果集封装 */returnhandler.query(stmt, resultHandler);

}

  • e, e);

    } finally {

final MapperMethod mapperMethod = cachedMapperMethod;

} finally {

        closeStatement(stmt);

return mapperMethod.execute(sqlSession, args);

ErrorContext.instance;

    }

}

}

}

//把当前请求放入一个HashMap中,一旦下次还是同样的方法进来直接返回。

}

子流程1 SQL查询参数的设置:

private MapperMethod cachedMapperMethod(Method method) {

执行器在query操作中,优先会查询缓存是否命中,命中则直接返回,否则从数据库中查询。

首先获取数据库connection连接,然后准备statement,然后就设置SQL查询中的参数值。打开一个connection连接,在使用完后不会close,而是存储下来,当下次需要打开连接时就直接返回。

MapperMethod mapperMethod = methodCache.get;

// CachingExecutor类

// SimpleExecutor类privateStatement prepareStatement(StatementHandler handler, Log statementLog)throws SQLException {

if (mapperMethod == null) {

public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {

    Statement stmt;

mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration;

/* 获取关联参数的sql,boundSql */

    /* 获取Connection连接 */    Connection connection = getConnection(statementLog);

methodCache.put(method, mapperMethod);

BoundSql boundSql = ms.getBoundSql(parameterObject);

    /* 准备Statement */    stmt = handler.prepare(connection, transaction.getTimeout());

}

/* 创建cache key值 */

    /* 设置SQL查询中的参数值 */    handler.parameterize(stmt);

return mapperMethod;

CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);

    return stmt;

}

return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

}// DefaultParameterHandler类publicvoid setParameters(PreparedStatement ps) {

public Object execute(SqlSession sqlSession, Object[] args) {

}

    /**    * 设置SQL参数值,从ParameterMapping中读取参数值和类型,然后设置到SQL语句中

Object result;

public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)

    */    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());

switch (command.getType {

throws SQLException {

    List parameterMappings = boundSql.getParameterMappings();

case INSERT: {

/* 获取二级缓存实例 */

    if(parameterMappings !=null) {

Object param = method.convertArgsToSqlCommandParam;

Cache cache = ms.getCache();

        for(inti = 0; i < parameterMappings.size(); i ) {

result = rowCountResult(sqlSession.insert(command.getName);

if (cache != null) {

            ParameterMapping parameterMapping = parameterMappings.get(i);

break;

flushCacheIfRequired;

            if(parameterMapping.getMode() != ParameterMode.OUT) {

}

if (ms.isUseCache() && resultHandler == null) {

                Object value;

case UPDATE: {

ensureNoOutParams(ms, parameterObject, boundSql);

                String propertyName = parameterMapping.getProperty();

Object param = method.convertArgsToSqlCommandParam;

@SuppressWarnings("unchecked")

                if(boundSql.hasAdditionalParameter(propertyName)) {// issue #448 ask first for additional paramsvalue = boundSql.getAdditionalParameter(propertyName);

result = rowCountResult(sqlSession.update(command.getName);

List<E> list = (List<E>) tcm.getObject(cache, key);

                } elseif(parameterObject ==null) {

break;

if (list == null) {

                    value =null;

}

list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

                } elseif (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {

case DELETE: {

tcm.putObject(cache, key, list); // issue #578 and #116

                    value = parameterObject;

Object param = method.convertArgsToSqlCommandParam;

}

                } else {

result = rowCountResult(sqlSession.delete(command.getName);

return list;

                    MetaObject metaObject = configuration.newMetaObject(parameterObject);

break;

}

                    value = metaObject.getValue(propertyName);

}

}

                }

case SELECT:

return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

                TypeHandler typeHandler = parameterMapping.getTypeHandler();

if (method.returnsVoid() && method.hasResultHandler {

}

                JdbcType jdbcType = parameterMapping.getJdbcType();

executeWithResultHandler(sqlSession, args);

private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {

                if(value ==null&& jdbcType ==null) {

result = null;

List<E> list;

                    jdbcType = configuration.getJdbcTypeForNull();

} else if (method.returnsMany {

/**

                }

result = executeForMany(sqlSession, args);

* 先往localCache中插入一个占位对象,这个地方

                try {

} else if (method.returnsMap {

*/

                    typeHandler.setParameter(ps, i 1, value, jdbcType);

result = executeForMap(sqlSession, args);

localCache.putObject(key, EXECUTION_PLACEHOLDER);

                } catch (TypeException e) {

} else if (method.returnsCursor {

try {

                    thrownewTypeException("Could not set parameters for mapping: " parameterMapping ". Cause: " e, e);

result = executeForCursor(sqlSession, args);

list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);

                } catch (SQLException e) {

} else {

} finally {

                    thrownewTypeException("Could not set parameters for mapping: " parameterMapping ". Cause: " e, e);

//本次案例会执行selectOne

localCache.removeObject;

                }

Object param = method.convertArgsToSqlCommandParam;

}

            }

result = sqlSession.selectOne(command.getName;

/* 往缓存中写入数据,也就是缓存查询结果 */

        }

}

localCache.putObject(key, list);

    }

break;

if (ms.getStatementType() == StatementType.CALLABLE) {

}

case FLUSH:

localOutputParameterCache.putObject(key, parameter);

子流程2 SQL查询结果集的封装:

result = sqlSession.flushStatements();

}

// SimpleExecutor类public List query(Statement statement, ResultHandler resultHandler)throws SQLException {

break;

return list;

    PreparedStatement ps = (PreparedStatement) statement;

default:

}

    // 执行查询操作    ps.execute();

throw new BindingException("Unknown execution method for: " command.getName;

真正的doQuery操作是由SimplyExecutor代理来完成的,该方法中有2个子流程,一个是SQL参数的设置,另一个是SQL查询操作和结果集的封装。

    // 执行结果集封装returnresultSetHandler. handleResultSets(ps);

}

// SimpleExecutor类

}// DefaultReseltSetHandler类publicList handleResultSets(Statement stmt)throws SQLException {

if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid {

public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {

    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

throw new BindingException("Mapper method '" command.getName()

Statement stmt = null;

    finalList multipleResults =newArrayList();

  • " attempted to return null from a method with a primitive return type (" method.getReturnType;

try {

    intresultSetCount = 0;

}

Configuration configuration = ms.getConfiguration();

    /**    * 获取第一个ResultSet,同时获取数据库的MetaData数据,包括数据表列名、列的类型、类序号等。

return result;

StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);

    * 这些信息都存储在了ResultSetWrapper中了

}

/* 子流程1: SQL查询参数的设置 */

    */    ResultSetWrapper rsw = getFirstResultSet(stmt);

@Override

stmt = prepareStatement(handler, ms.getStatementLog;

    List resultMaps = mappedStatement.getResultMaps();

public <T> T selectOne(String statement, Object parameter) {

/* 子流程2: SQL查询操作和结果集封装 */

    intresultMapCount = resultMaps.size();

// Popular vote was to return null on 0 results and throw exception on too many.

return handler.<E>query(stmt, resultHandler);

    validateResultMapsCount(rsw, resultMapCount);

List<T> list = this.<T>selectList(statement, parameter);

} finally {

    while(rsw !=null&& resultMapCount > resultSetCount) {

if (list.size {

closeStatement;

      ResultMap resultMap = resultMaps.get(resultSetCount);

return list.get;

}

      handleResultSet(rsw, resultMap, multipleResults, null);

} else if (list.size {

}

      rsw = getNextResultSet(stmt);

throw new TooManyResultsException("Expected one result to be returned by selectOne(), but found: " list.size;

子流程1 SQL查询参数的设置:

      cleanUpAfterHandlingResultSet();

} else {

首先获取数据库connection连接,然后准备statement,然后就设置SQL查询中的参数值。打开一个connection连接,在使用完后不会close,而是存储下来,当下次需要打开连接时就直接返回。

      resultSetCount ;

return null;

// SimpleExecutor类

    }

}

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {

    String[] resultSets = mappedStatement.getResultSets();

}

Statement stmt;

    if(resultSets !=null) {

@Override

/* 获取Connection连接 */

      while(rsw !=null&& resultSetCount < resultSets.length) {

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {

Connection connection = getConnection(statementLog);

        ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);

try {

/* 准备Statement */

        if(parentMapping !=null) {

MappedStatement ms = configuration.getMappedStatement(statement);

stmt = handler.prepare(connection, transaction.getTimeout;

          String nestedResultMapId = parentMapping.getNestedResultMapId();

return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);

/* 设置SQL查询中的参数值 */

          ResultMap resultMap = configuration.getResultMap(nestedResultMapId);

} catch (Exception e) {

handler.parameterize;

          handleResultSet(rsw, resultMap, null, parentMapping);

throw ExceptionFactory.wrapException("Error querying database. Cause: "

return stmt;

        }

  • e, e);

}

        rsw = getNextResultSet(stmt);

} finally {

// DefaultParameterHandler类

        cleanUpAfterHandlingResultSet();

ErrorContext.instance;

public void setParameters(PreparedStatement ps) {

        resultSetCount ;

}

/**

      }

}

* 设置SQL参数值,从ParameterMapping中读取参数值和类型,然后设置到SQL语句中

    }

//这边调用的是CachingExecutor类的query,还记得前文解析mybatis-config.xml的时候我们指定了REUSE但是因为在配置文件中开启了缓存

*/

    return collapseSingleResultList(multipleResults);

//所以ReuseExecutor被CachingExecotur装饰,新增了缓存的判断,最后还是会调用ReuseExecutor

ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap);

  }

@Override

List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();

ResultSetWrapper是ResultSet的包装类,调用getFirstResultSet方法获取第一个ResultSet,同时获取数据库的MetaData数据,包括数据表列名、列的类型、类序号等,这些信息都存储在ResultSetWrapper类中了。然后调用handleResultSet方法来来进行结果集的封装。

public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {

if (parameterMappings != null) {

// DefaultResultSetHandler类privatevoidhandleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List multipleResults, ResultMapping parentMapping)throws SQLException {

BoundSql boundSql = ms.getBoundSql(parameterObject);

for (int i = 0; i < parameterMappings.size {

    try {

CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);

ParameterMapping parameterMapping = parameterMappings.get;

        if(parentMapping !=null) {

return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

if (parameterMapping.getMode() != ParameterMode.OUT) {

            handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);

}

Object value;

        } else {

@Override

String propertyName = parameterMapping.getProperty();

            if(resultHandler ==null) {

public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)

if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params

                DefaultResultHandler defaultResultHandler =new DefaultResultHandler(objectFactory);

throws SQLException {

value = boundSql.getAdditionalParameter(propertyName);

                handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);

Cache cache = ms.getCache();

} else if (parameterObject == null) {

                multipleResults.add(defaultResultHandler.getResultList());

if (cache != null) {

value = null;

            } else {

flushCacheIfRequired;

} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass {

                handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);

if (ms.isUseCache() && resultHandler == null) {

value = parameterObject;

            }

ensureNoOutParams(ms, parameterObject, boundSql);

} else {

        }

@SuppressWarnings("unchecked")

MetaObject metaObject = configuration.newMetaObject(parameterObject);

    } finally {

List<E> list = (List<E>) tcm.getObject(cache, key);

value = metaObject.getValue(propertyName);

        // issue #228 (close resultsets)        closeResultSet(rsw.getResultSet());

if (list == null) {

}

    }

//如果缓存中没有数据则查询数据库

TypeHandler typeHandler = parameterMapping.getTypeHandler();

}

list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

JdbcType jdbcType = parameterMapping.getJdbcType();

这里调用handleRowValues方法进行结果值的设置。

//结果集放入缓存

if (value == null && jdbcType == null) {

// DefaultResultSetHandler类publicvoidhandleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler resultHandler, RowBounds rowBounds, ResultMapping parentMapping)throws SQLException {

tcm.putObject(cache, key, list); // issue #578 and #116

jdbcType = configuration.getJdbcTypeForNull();

    if (resultMap.hasNestedResultMaps()) {

}

}

        ensureNoRowBounds();

return list;

try {

        checkResultHandler();

}

typeHandler.setParameter(ps, i 1, value, jdbcType);

        handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);

}

} catch (TypeException e) {

    } else {

return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

throw new TypeException("Could not set parameters for mapping: " parameterMapping ". Cause: " e, e);

        // 封装数据        handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);

}

} catch (SQLException e) {

    }

MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。本文将通过debug的方式来了解其工作原理。

throw new TypeException("Could not set parameters for mapping: " parameterMapping ". Cause: " e, e);

}privatevoidhandleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler resultHandler, RowBounds rowBounds, ResultMapping parentMapping)

Mybatis核心类:

}

        throws SQLException {

SqlSessionFactory:每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为中心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或通过Java的方式构建出 SqlSessionFactory 的实例。SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,建议使用单例模式或者静态单例模式。一个SqlSessionFactory对应配置文件中的一个环境(environment),如果你要使用多个数据库就配置多个环境分别对应一个SqlSessionFactory。

}

    DefaultResultContext resultContext =newDefaultResultContext();

SqlSession:SqlSession是一个接口,它有2个实现类,分别是DefaultSqlSession以及SqlSessionManager。SqlSession通过内部存放的执行器来对数据进行CRUD。此外SqlSession不是线程安全的,因为每一次操作完数据库后都要调用close对其进行关闭,官方建议通过try-finally来保证总是关闭SqlSession。

}

    skipRows(rsw.getResultSet(), rowBounds);

Executor:Executor接口有两个实现类,其中BaseExecutor有三个继承类分别是BatchExecutor(重用语句并执行批量更新),ReuseExecutor(重用预处理语句prepared statements),SimpleExecutor。以上三个就是主要的Executor。通过下图可以看到Mybatis在Executor的设计上面使用了装饰者模式,我们可以用CachingExecutor来装饰前面的三个执行器目的就是用来实现缓存。

}

    while(shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {

图片 5image

}

        ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap,null);

MappedStatement:MappedStatement就是用来存放我们SQL映射文件中的信息包括sql语句,输入参数,输出参数等等。一个SQL节点对应一个MappedStatement对象。

子流程2 SQL查询结果集的封装:

        Object rowValue = getRowValue(rsw, discriminatedResultMap);

Mybatis工作流程:

// SimpleExecutor类

        storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());

图片 6image

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {

    }

下面将通过debug方式对Mybatis进行一步步解析。首先贴出我的mybatis-config.xml文件以及Mapper.xml文件。

PreparedStatement ps = (PreparedStatement) statement;

}privateObject getRowValue(ResultSetWrapper rsw, ResultMap resultMap)throws SQLException {

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

// 执行查询操作

    finalResultLoaderMap lazyLoader =new ResultLoaderMap();

";

ps.execute();

    // createResultObject为新创建的对象,数据表对应的类Object rowValue = createResultObject(rsw, resultMap, lazyLoader,null);

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

// 执行结果集封装

    if(rowValue !=null&& !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {

";

return resultSetHandler.<E> handleResultSets;

        finalMetaObject metaObject = configuration.newMetaObject(rowValue);

select * from user

}

        booleanfoundValues =this.useConstructorMappings;

User where id = #{id}

// DefaultReseltSetHandler类

        if(shouldApplyAutomaticMappings(resultMap,false)) {

insert into User (username,birthday,sex,address)

public List<Object> handleResultSets(Statement stmt) throws SQLException {

            // 这里把数据填充进去,metaObject中包含了resultObject信息foundValues = applyAutomaticMappings(rsw, resultMap, metaObject,null) || foundValues;

values (#{name},#{birthday},#{sex},#{address})

ErrorContext.instance().activity("handling results").object(mappedStatement.getId;

        }

update User set username = #{username},birthday = #{birthday},

final List<Object> multipleResults = new ArrayList<Object>();

        foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader,null) || foundValues;

sex = #{sex},address = #{address} where id = #{id}

int resultSetCount = 0;

        foundValues = lazyLoader.size() > 0 || foundValues;

delete from User where id = #{id}

/**

        rowValue = (foundValues || configuration.isReturnInstanceForEmptyRow()) ? rowValue :null;

select * from User where sex = #{param1}

* 获取第一个ResultSet,同时获取数据库的MetaData数据,包括数据表列名、列的类型、类序号等。

    }

and username like #{param2}

* 这些信息都存储在了ResultSetWrapper中了

    return rowValue;

and address = #{parma3}

*/

}privatebooleanapplyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix)throws SQLException {

select count from user where username like #{username}

ResultSetWrapper rsw = getFirstResultSet;

    List autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);

username like #{pattern}

List<ResultMap> resultMaps = mappedStatement.getResultMaps();

    booleanfoundValues =false;

and sex = #{sex}

int resultMapCount = resultMaps.size();

    if(autoMapping.size() > 0) {

and address = #{address}

validateResultMapsCount(rsw, resultMapCount);

        // 这里进行for循环调用,因为user表中总共有7列,所以也就调用7次for (UnMappedColumnAutoMapping mapping : autoMapping) {

where id in

while (rsw != null && resultMapCount > resultSetCount) {

            // 这里将esultSet中查询结果转换为对应的实际类型finalObject value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);

图片 7image

ResultMap resultMap = resultMaps.get(resultSetCount);

            if(value !=null) {

第一步通过SqlSessionFactoryBuilder创建SqlSessionFactory:

handleResultSet(rsw, resultMap, multipleResults, null);

                foundValues =true;

首先在SqlSessionFactoryBuilder的build()方法中可以看到MyBatis内部定义了一个类XMLConfigBuilder用来解析配置文件mybatis-config.xml。针对配置文件中的每一个节点进行解析并将数据存放到Configuration这个对象中,紧接着使用带有Configuration的构造方法发返回一个DefautSqlSessionFactory。

rsw = getNextResultSet;

            }

public SqlSessionFactory build(InputStream inputStream) {

cleanUpAfterHandlingResultSet();

            if(value !=null|| (configuration.isCallSettersOnNulls() && !mapping.primitive)) {

return build(inputStream, null, null);

resultSetCount ;

                // gcode issue #377, call setter on nulls (value is not 'found')                metaObject.setValue(mapping.property, value);

}

}

            }

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

String[] resultSets = mappedStatement.getResultSets();

        }

try {

if (resultSets != null) {

    }

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

while (rsw != null && resultSetCount < resultSets.length) {

    return foundValues;

//解析mybatis-config.xml

ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);

}

return build(parser.parse;

if (parentMapping != null) {

mapping.typeHandler.getResult会获取查询结果值的实际类型,比如我们user表中id字段为int类型,那么它就对应Java中的Integer类型,然后通过调用statement.getInt("id")来获取其int值,其类型为Integer。metaObject.setValue方法会把获取到的Integer值设置到Java类中的对应字段。

} catch (Exception e) {

String nestedResultMapId = parentMapping.getNestedResultMapId();

// MetaObject类publicvoid setValue(String name, Object value) {

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

ResultMap resultMap = configuration.getResultMap(nestedResultMapId);

    PropertyTokenizer prop =new PropertyTokenizer(name);

} finally {

handleResultSet(rsw, resultMap, null, parentMapping);

    if (prop.hasNext()) {

ErrorContext.instance;

}

        MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());

try {

rsw = getNextResultSet;

        if(metaValue == SystemMetaObject.NULL_META_OBJECT) {

inputStream.close();

cleanUpAfterHandlingResultSet();

            if(value ==null&& prop.getChildren() !=null) {

} catch (IOException e) {

resultSetCount ;

                // don't instantiate child path if value is nullreturn;

// Intentionally ignore. Prefer previous error.

}

            } else {

}

}

                metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);

}

return collapseSingleResultList(multipleResults);

            }

}

}

        }

//返回SqlSessionFactory,默认使用的是实现类DefaultSqlSessionFactory

ResultSetWrapper是ResultSet的包装类,调用getFirstResultSet方法获取第一个ResultSet,同时获取数据库的MetaData数据,包括数据表列名、列的类型、类序号等,这些信息都存储在ResultSetWrapper类中了。然后调用handleResultSet方法来来进行结果集的封装。

        metaValue.setValue(prop.getChildren(), value);

public SqlSessionFactory build(Configuration config) {

// DefaultResultSetHandler类

    } else {

return new DefaultSqlSessionFactory;

private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {

        objectWrapper.set(prop, value);

}

try {

    }

public Configuration parse() {

if (parentMapping != null) {

}

if {

handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);

metaValue.setValue方法最后会调用到Java类中对应数据域的set方法,这样也就完成了SQL查询结果集的Java类封装过程。最后贴一张调用栈到达Java类的set方法中的快照:

throw new BuilderException("Each XMLConfigBuilder can only be used once.");

} else {

图片 8

}

if (resultHandler == null) {

MyBatis缓存

parsed = true;

DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);

MyBatis提供查询缓存,用于减轻数据库压力,提高性能。MyBatis提供了一级缓存和二级缓存。

//获取根节点configuration

handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);

一级缓存是SqlSession级别的缓存,每个SqlSession对象都有一个哈希表用于缓存数据,不同SqlSession对象之间缓存不共享。同一个SqlSession对象对象执行2遍相同的SQL查询,在第一次查询执行完毕后将结果缓存起来,这样第二遍查询就不用向数据库查询了,直接返回缓存结果即可。MyBatis默认是开启一级缓存的。

parseConfiguration(parser.evalNode("/configuration"));

multipleResults.add(defaultResultHandler.getResultList;

二级缓存是mapper级别的缓存,二级缓存是跨SqlSession的,多个SqlSession对象可以共享同一个二级缓存。不同的SqlSession对象执行两次相同的SQL语句,第一次会将查询结果进行缓存,第二次查询直接返回二级缓存中的结果即可。MyBatis默认是不开启二级缓存的,可以在配置文件中使用如下配置来开启二级缓存:

return configuration;

} else {

当SQL语句进行更新操作(删除/添加/更新)时,会清空对应的缓存,保证缓存中存储的都是最新的数据。MyBatis的二级缓存对细粒度的数据级别的缓存实现不友好,比如如下需求:对商品信息进行缓存,由于商品信息查询访问量大,但是要求用户每次都能查询最新的商品信息,此时如果使用mybatis的二级缓存就无法实现当一个商品变化时只刷新该商品的缓存信息而不刷新其它商品的信息,因为mybaits的二级缓存区域以mapper为单位划分,当一个商品信息变化会将所有商品信息的缓存数据全部清空。解决此类问题需要在业务层根据需求对数据有针对性缓存,具体业务具体实现。

}

handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);

//开始解析mybatis-config.xml,并把解析后的数据存放到configuration中

}

private void parseConfiguration(XNode root) {

}

try {

} finally {

//保存mybatis-config.xml中的标签setting,本例中开启全局缓存cacheEnabled,设置默认执行器defaultExecutorType=REUSE

// issue #228 (close resultsets)

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

closeResultSet(rsw.getResultSet;

//issue #117 read properties first

}

//解析是否配置了外部properties,例如本例中配置的jdbc.propertis

}

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

这里调用handleRowValues方法进行结果值的设置。

//查看是否配置了VFS,默认没有,本例也没有使用

// DefaultResultSetHandler类

loadCustomVfs;

public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {

//查看是否用了类型别名,减少完全限定名的冗余,本例中使用了别名User代替了com.ctc.Model.User

if (resultMap.hasNestedResultMaps {

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

ensureNoRowBounds();

//查看是否配置插件来拦截映射语句的执行,例如拦截Executor的Update方法,本例没有使用

checkResultHandler();

pluginElement(root.evalNode("plugins"))

handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);

//查看是否配置了ObjectFactory,默认情况下使用对象的无参构造方法或者是带有参数的构造方法,本例没有使用

} else {

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

// 封装数据

//查看是否配置了objectWrapperFatory,这个用来或者ObjectWapper,可以访问:对象,Collection,Map属性。本例没有使用

handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);

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

}

//查看是否配置了reflectorFactory,mybatis的反射工具,提供了很多反射方法。本例没有使用

}

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

private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)

//放入参数到configuration对象中

throws SQLException {

settingsElement;

DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();

// read it after objectFactory and objectWrapperFactory issue #631

skipRows(rsw.getResultSet(), rowBounds);

//查看数据库环境配置

while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet {

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

ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);

//查看是否使用多种数据库,本例没有使用

Object rowValue = getRowValue(rsw, discriminatedResultMap);

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

storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet;

//查看是否配置了新的类型处理器,如果跟处理的类型跟默认的一致就会覆盖。本例没有使用

}

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

}

//查看是否配置SQL映射文件,有四种配置方式,resource,url,class以及自动扫包package。本例使用package

private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {

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

final ResultLoaderMap lazyLoader = new ResultLoaderMap();

} catch (Exception e) {

// createResultObject为新创建的对象,数据表对应的类

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

Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);

}

if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType {

}

final MetaObject metaObject = configuration.newMetaObject;

第二步通过SqlSessionFactory创建SqlSession:

boolean foundValues = this.useConstructorMappings;

@Override

if (shouldApplyAutomaticMappings(resultMap, false)) {

public SqlSession openSession() {

// 这里把数据填充进去,metaObject中包含了resultObject信息

return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);

foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;

}

}

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

foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;

Transaction tx = null;

foundValues = lazyLoader.size() > 0 || foundValues;

try {

rowValue = (foundValues || configuration.isReturnInstanceForEmptyRow ? rowValue : null;

//拿到前文从mybatis中解析到的数据库环境配置

}

final Environment environment = configuration.getEnvironment();

return rowValue;

final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);

}

//拿到jdbc的事务管理器,有两种一种是jbc,一种的managed。本例使用的是JdbcTransaction

private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {

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

List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);

//从mybatis配置文件可以看到本例使用了REUSE,因此返回的是ReuseExecutor并把事务传入对象中

boolean foundValues = false;

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

if (autoMapping.size {

return new DefaultSqlSession(configuration, executor, autoCommit);

// 这里进行for循环调用,因为user表中总共有7列,所以也就调用7次

} catch (Exception e) {

for (UnMappedColumnAutoMapping mapping : autoMapping) {

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

// 这里将esultSet中查询结果转换为对应的实际类型

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

final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);

} finally {

if (value != null) {

ErrorContext.instance;

foundValues = true;

}

}

}

if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {

// gcode issue #377, call setter on nulls (value is not 'found')

executorType = executorType == null ? defaultExecutorType : executorType;

metaObject.setValue(mapping.property, value);

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

}

Executor executor;

}

if (ExecutorType.BATCH == executorType) {

}

executor = new BatchExecutor(this, transaction);

return foundValues;

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

}

executor = new ReuseExecutor(this, transaction);

mapping.typeHandler.getResult会获取查询结果值的实际类型,比如我们user表中id字段为int类型,那么它就对应Java中的Integer类型,然后通过调用statement.getInt来获取其int值,其类型为Integer。metaObject.setValue方法会把获取到的Integer值设置到Java类中的对应字段。

} else {

// MetaObject类

executor = new SimpleExecutor(this, transaction);

public void setValue(String name, Object value) {

}

PropertyTokenizer prop = new PropertyTokenizer;

if (cacheEnabled) {

if (prop.hasNext {

executor = new CachingExecutor;

MetaObject metaValue = metaObjectForProperty(prop.getIndexedName;

}

if (metaValue == SystemMetaObject.NULL_META_OBJECT) {

executor = interceptorChain.pluginAll;

if (value == null && prop.getChildren() != null) {

return executor;

// don't instantiate child path if value is null

}

return;

//返回一个SqlSession,默认使用DefaultSqlSession

} else {

public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {

metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);

this.configuration = configuration;

}

this.executor = executor;

}

this.dirty = false;

metaValue.setValue(prop.getChildren;

this.autoCommit = autoCommit;

} else {

}

objectWrapper.set(prop, value);

第三步通过SqlSession拿到Mapper对象的代理:

}

@Override

}

public T getMapper(Class type) {

metaValue.setValue方法最后会调用到Java类中对应数据域的set方法,这样也就完成了SQL查询结果集的Java类封装过程。最后贴一张调用栈到达Java类的set方法中的快照:

return configuration.getMapper(type, this);

图片 9

}

MyBatis缓存

public T getMapper(Class type, SqlSession sqlSession) {

MyBatis提供查询缓存,用于减轻数据库压力,提高性能。MyBatis提供了一级缓存和二级缓存。

//前文解析Mybatis-config.xml的时候,在解析标签mapper就是用configuration对象的mapperRegistry存放数据

图片 10

return mapperRegistry.getMapper(type, sqlSession);

一级缓存是SqlSession级别的缓存,每个SqlSession对象都有一个哈希表用于缓存数据,不同SqlSession对象之间缓存不共享。同一个SqlSession对象对象执行2遍相同的SQL查询,在第一次查询执行完毕后将结果缓存起来,这样第二遍查询就不用向数据库查询了,直接返回缓存结果即可。MyBatis默认是开启一级缓存的。

}

二级缓存是mapper级别的缓存,二级缓存是跨SqlSession的,多个SqlSession对象可以共享同一个二级缓存。不同的SqlSession对象执行两次相同的SQL语句,第一次会将查询结果进行缓存,第二次查询直接返回二级缓存中的结果即可。MyBatis默认是不开启二级缓存的,可以在配置文件中使用如下配置来开启二级缓存:

@SuppressWarnings("unchecked")

<settings>

public T getMapper(Class type, SqlSession sqlSession) {

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

//knownMapper是一个HashMap在存放mapperRegistry的过程中,以每个Mapper对象的类型为Key, MapperProxyFactory 为value保存。

</settings>

//例如本例中保存的就是Key:com.ctc.mapper.UserMapper,value就是保存了key的MapperProxyFactory对象

当SQL语句进行更新操作时,会清空对应的缓存,保证缓存中存储的都是最新的数据。MyBatis的二级缓存对细粒度的数据级别的缓存实现不友好。

final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get;

比如如下需求:对商品信息进行缓存,由于商品信息查询访问量大,但是要求用户每次都能查询最新的商品信息,此时如果使用mybatis的二级缓存就无法实现当一个商品变化时只刷新该商品的缓存信息而不刷新其它商品的信息,因为mybaits的二级缓存区域以mapper为单位划分,当一个商品信息变化会将所有商品信息的缓存数据全部清空。

if (mapperProxyFactory == null) {

解决此类问题需要在业务层根据需求对数据有针对性缓存,具体业务具体实现。

throw new BindingException("Type " type " is not known to the MapperRegistry.");

欢迎工作一到八年的Java工程师朋友们加入Java高级交流:787707172

}

本群提供免费的学习指导 架构资料 以及免费的解答

try {

不懂得问题都可以在本群提出来 之后还会有直播平台和讲师直接交流噢

return mapperProxyFactory.newInstance(sqlSession);

} catch (Exception e) {

throw new BindingException("Error getting mapper instance. Cause: " e, e);

}

}

public T newInstance(SqlSession sqlSession) {

//生成一个mapperProxy对象,这个对象实现了InvocationHandler, Serializable。就是JDK动态代理中的方法调用处理器

final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache);

return newInstance(mapperProxy);

}

public MapperProxy(SqlSession sqlSession, Class mapperInterface, Map methodCache) {

this.sqlSession = sqlSession;

this.mapperInterface = mapperInterface;

this.methodCache = methodCache;

}

@SuppressWarnings("unchecked")

protected T newInstance(MapperProxy mapperProxy) {

//通过JDK动态代理生成一个Mapper的代理,在本例中的就是UserMapper的代理类,它实现了UserMapper接口

return Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);

}

第四步通过MapperProxy调用Maper中相应的方法:

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

//判断当前调用的method是不是Object中声明的方法,如果是的话直接执行。

if (Object.class.equals(method.getDeclaringClass {

try {

return method.invoke(this, args);

} catch (Throwable t) {

throw ExceptionUtil.unwrapThrowable;

}

}

final MapperMethod mapperMethod = cachedMapperMethod;

return mapperMethod.execute(sqlSession, args);

}

//把当前请求放入一个HashMap中,一旦下次还是同样的方法进来直接返回。

private MapperMethod cachedMapperMethod(Method method) {

MapperMethod mapperMethod = methodCache.get;

if (mapperMethod == null) {

mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration;

methodCache.put(method, mapperMethod);

}

return mapperMethod;

}

public Object execute(SqlSession sqlSession, Object[] args) {

Object result;

switch (command.getType {

case INSERT: {

Object param = method.convertArgsToSqlCommandParam;

result = rowCountResult(sqlSession.insert(command.getName);

break;

}

case UPDATE: {

Object param = method.convertArgsToSqlCommandParam;

result = rowCountResult(sqlSession.update(command.getName);

break;

}

case DELETE: {

Object param = method.convertArgsToSqlCommandParam;

result = rowCountResult(sqlSession.delete(command.getName);

break;

}

case SELECT:

if (method.returnsVoid() && method.hasResultHandler {

executeWithResultHandler(sqlSession, args);

result = null;

} else if (method.returnsMany {

result = executeForMany(sqlSession, args);

} else if (method.returnsMap {

result = executeForMap(sqlSession, args);

} else if (method.returnsCursor {

result = executeForCursor(sqlSession, args);

} else {

//本次案例会执行selectOne

Object param = method.convertArgsToSqlCommandParam;

result = sqlSession.selectOne(command.getName;

}

break;

case FLUSH:

result = sqlSession.flushStatements();

break;

default:

throw new BindingException("Unknown execution method for: " command.getName;

}

if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid {

throw new BindingException("Mapper method '" command.getName()

  • " attempted to return null from a method with a primitive return type (" method.getReturnType;

}

return result;

}

@Override

public <T> T selectOne(String statement, Object parameter) {

// Popular vote was to return null on 0 results and throw exception on too many.

List<T> list = this.<T>selectList(statement, parameter);

if (list.size {

return list.get;

} else if (list.size {

throw new TooManyResultsException("Expected one result to be returned by selectOne(), but found: " list.size;

} else {

return null;

}

}

@Override

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {

try {

MappedStatement ms = configuration.getMappedStatement(statement);

return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);

} catch (Exception e) {

throw ExceptionFactory.wrapException("Error querying database. Cause: "

  • e, e);

} finally {

ErrorContext.instance;

}

}

//这边调用的是CachingExecutor类的query,还记得前文解析mybatis-config.xml的时候我们指定了REUSE但是因为在配置文件中开启了缓存

//所以ReuseExecutor被CachingExecotur装饰,新增了缓存的判断,最后还是会调用ReuseExecutor

@Override

public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {

BoundSql boundSql = ms.getBoundSql(parameterObject);

CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);

return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

}

@Override

public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)

throws SQLException {

Cache cache = ms.getCache();

if (cache != null) {

flushCacheIfRequired;

if (ms.isUseCache() && resultHandler == null) {

ensureNoOutParams(ms, parameterObject, boundSql);

@SuppressWarnings("unchecked")

List<E> list = (List<E>) tcm.getObject(cache, key);

if (list == null) {

//如果缓存中没有数据则查询数据库

list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

//结果集放入缓存

tcm.putObject(cache, key, list); // issue #578 and #116

}

return list;

}

}

return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

}

本文由pc28.am发布于计算机编程,转载请注明出处:mybatis的准绳总括部份,Mybatis职业流程及其规律与

上一篇:最新AliJava技巧面试题,能力面试复习大纲 下一篇:没有了
猜你喜欢
热门排行
精彩图文