12.mybatis核心架构原理

课程标题《2022超详细mybatis框架源码解读》
课程内容:
1.mybatis环境搭建
2.mybatis使用哪些设计模式
3.mybatis大致运行原理
4.mybatis建造者模式源码解读
5.mybatis代理模式源码解读
6.mybatis一级、二级缓存源码解读

mybatis 2018年

Mybatis环境搭建

Mybatis介绍

https://mybatis.net.cn/index.html

mybatis是一个用Java编写的持久层框架,它使用ORM实现了结果集的封装。

ORM是Object Relational Mapping 对象关系映射。简单来说,就是把数据库表和实体类及实体类的属性对应起来,让开发者操作实体类就实现操作数据库表,它封装了jdbc操作的很多细节,使开发者只需要关注sql语句本身,而无需关注注册驱动,创建连接等复杂过程。

ORM:Object-Relation-Mapping,也就是对象关系映射,是一种程序设计思想,mybatis就是ORM的一种实现方式,简单来说就是将数据库中查询出的数据映射到对应的实体中。

数据库表结构

CREATE TABLE `mayikt_users` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=33 DEFAULT CHARSET=utf8mb3;

1.引入mybatis相关依赖 已经完成了

2.mybatis-config.xml(该配置文件名称是可以改) 存放就是我们数据库相关连接信息

3.定义mapper ----编写我们mybatis 相关 sql语句 每个表 对应一个mapper

4.定义java对象–需要注意下 类中的 成员属性与数据库表中字段 映射 默认 类中的 成员属性数据库表中字段名称对应的。

5.使用 mybatis api开始执行该 sql语句即可 得到结果

maven依赖

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.5</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.18</version>
        </dependency>

定义xml配置文件

存放数据库连接信息mybatis-config.xml

<?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>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/mayikt?serverTimezone=GMT%2B8"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mybatis/userMaaper.xml"/>
    </mappers>
</configuration>

Mapper文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="userMapper">
    <select id="getByUsers" resultType="com.mayikt.entity.UserEntity">
        select * from mayikt_users
  </select>
</mapper>

测试代码

package com.mayikt.test;

import com.mayikt.entity.UserEntity;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
 * @author 余胜军
 * @ClassName Test01
 * @qq 644064779
 * @addres www.mayikt.com
 * 微信:yushengjun644
 */
public class Test01 {
    public static void main(String[] args) throws IOException {
        // 1.读取加载mybatis-config.xml
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        // 2.获取到获取到
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 3.根据 mapper id=getByUsers 执行该s  ql 语句 通过  sql语句得到我们的对象 orm
        List<UserEntity> userEntitys = sqlSession.selectList("getByUsers", UserEntity.class);
        System.out.println(userEntitys);
        sqlSession.close();
    }
}

mapper代理开发模式

1.mapper接口方式开发整合就必须是对应的mapper接口的全限定类名

2.接口中的方法与映射文件中的SQL语句的ID

3.需要在mybatis-config.xml 新增 加载该userMaaper

4.定义mapper 接口 需要考虑方法的名称与userMapper.xml的 sql id名称保持一致。

package com.mayikt.mapper;

import com.mayikt.entity.UserEntity;

import java.util.List;

/**
 * @author 余胜军
 * @ClassName UserMapper
 * @qq 644064779
 * @addres www.mayikt.com
 * 微信:yushengjun644
 */
public interface UserMapper {
    List<UserEntity> getByUsers();
}

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mayikt.mapper.UserMapper">
    <select id="getByUsers" resultType="com.mayikt.entity.UserEntity">
        select * from mayikt_users
  </select>
</mapper>

5.相关代码

package com.mayikt.test;

import com.mayikt.entity.UserEntity;
import com.mayikt.mapper.UserMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
 * @author 余胜军
 * @ClassName Test01
 * @qq 644064779
 * @addres www.mayikt.com
 * 微信:yushengjun644
 */
public class Test01 {
    public static void main(String[] args) throws IOException {
        // 1.读取加载mybatis-config.xml
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        // 2.获取到获取到
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 3.根据 mapper id=getByUsers 执行该s  ql 语句 通过  sql语句得到我们的对象 orm
//        List<UserEntity> userEntitys = sqlSession.selectList("getByUsers", UserEntity.class);
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        System.out.println(mapper.getByUsers());
//        System.out.println(userEntitys);
        sqlSession.close();
    }
}

mybatis使用设计模式

1、Builder模式,例如SqlSessionFactoryBuilder、XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuilder、CacheBuilder;(解析配置文件)

2、工厂模式,例如SqlSessionFactory、ObjectFactory、MapperProxyFactory;

3、单例模式,例如ErrorContext和LogFactory;

4、代理模式,Mybatis实现的核心,比如MapperProxy、ConnectionLogger,用的jdk的动态代理;还有executor.loader包使用了cglib或者javassist达到延迟加载的效果;调动我们的Mapper接口

5、组合模式,例如SqlNode和各个子类ChooseSqlNode等;

6、模板方法模式,例如BaseExecutor和SimpleExecutor,还有BaseTypeHandler和所有的子类例如IntegerTypeHandler;

7、适配器模式,例如Log的Mybatis接口和它对jdbc、log4j等各种日志框架的适配实现;

8、装饰者模式,例如Cache包中的cache.decorators子包中等各个装饰者的实现;(多级缓存)

9、迭代器模式,例如迭代器模式PropertyTokenizer;

简单手写mybatis大致原理

底层基于jdk动态代理实现

mybatis mapper接口分析

在mybatis mapper 是一个接口为何可以调用的呢?底层其实就是基于JDK动态代理实现。

相关代码:

package com.mayikt.mybatis.ext;

import org.springframework.stereotype.Indexed;

import java.lang.annotation.*;

/**
 * @author 余胜军
 * @ClassName MayiktInsert
 * @qq 644064779
 * @addres www.mayikt.com
 * 微信:yushengjun644
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface MayiktInsert {
    String value();
}

package com.mayikt.mybatis;

import com.mayikt.mybatis.ext.MayiktInsert;
import com.mayikt.utils.MayiktJdbcUtils;
import org.apache.commons.lang.StringUtils;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.PreparedStatement;

/**
 * @author 余胜军
 * @ClassName MybatisJdkInvocationHandler
 * @qq 644064779
 * @addres www.mayikt.com
 * 微信:yushengjun644
 */
public class MybatisJdkInvocationHandler implements InvocationHandler {
    private Class mapperClass;

    public MybatisJdkInvocationHandler(Class mapperClass) {
        this.mapperClass = mapperClass;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 使用java反射技术获取该方法上的注解
        MayiktInsert declaredAnnotation = method.getDeclaredAnnotation(MayiktInsert.class);
        String insertSql = declaredAnnotation.value();
        if (StringUtils.isEmpty(insertSql)) {
            return null;
        }
        // 执行该sql语句
        Connection connection = MayiktJdbcUtils.getConnection();
        PreparedStatement preparedStatement = connection.prepareStatement(insertSql);
        int result = preparedStatement.executeUpdate();
        return result;
    }

    public <T> T getProxy() {
        return (T) Proxy.newProxyInstance(mapperClass.getClassLoader(), new Class[]{mapperClass}, this);
    }
}

package com.mayikt.mybatis;

import com.mayikt.mybatis.ext.MayiktInsert;

/**
 * @author 余胜军
 * @ClassName UserMapper
 * @qq 644064779
 * @addres www.mayikt.com
 * 微信:yushengjun644
 */
public interface UserMapper {
    @MayiktInsert("INSERT INTO `mayikt`.`mayikt_users` (`id`, `name`, `age`) VALUES (null, 'wangmazi', NULL);")
    int addUser();
}

package com.mayikt.mybatis;

/**
 * @author 余胜军
 * @ClassName Test01
 * @qq 644064779
 * @addres www.mayikt.com
 * 微信:yushengjun644
 */
public class Test01 {
    public static void main(String[] args) {
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        UserMapper userMapper = MapperProxy.getUserMapper(UserMapper.class);
        int result = userMapper.addUser();
        System.out.println(result);
    }
}

package com.mayikt.mybatis;

/**
 * @author 余胜军
 * @ClassName MapperProxy
 * @qq 644064779
 * @addres www.mayikt.com
 * 微信:yushengjun644
 */
public class MapperProxy {
    public static UserMapper getUserMapper(Class mapperClass) {
        return new MybatisInvocationHandler(mapperClass).getProxy();
    }

    public static void main(String[] args) {
        // 将jdk动态生成好的 class 存放本地
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        UserMapper userMapper = MapperProxy.getUserMapper(UserMapper.class);
        int i = userMapper.addUser();
        System.out.println(i);
    }
}

mybatis 底层 mapper 接口会执行到MapperProxy

会执行到:MapperProxy

mybatis mapperjdk动态代理源码解读

jdk动态代理 底层 生成 mapper 代理类

public final class $Proxy4 extends Proxy implements UserMapper {

}

当我们在去调用$Proxy4.方法的时候 会执行 mybatis jdk 动态代理回调类

public class MapperProxy<T> implements InvocationHandler, Serializable {
    
    private static final long serialVersionUID = -6424540398559729838L;
    private final SqlSession sqlSession;
    private final Class<T> mapperInterface;
    private final Map<Method, MapperMethod> methodCache;
    
    public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
        this.sqlSession = sqlSession;
        this.mapperInterface = mapperInterface;
        this.methodCache = methodCache;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            } else if (isDefaultMethod(method)) {
                return invokeDefaultMethod(proxy, method, args);
            }
        } catch (Throwable t) {
            throw ExceptionUtil.unwrapThrowable(t);
        }
        // 执行到我们 mapperMethod.execute(sqlSession, args);
        final MapperMethod mapperMethod = cachedMapperMethod(method);
        return mapperMethod.execute(sqlSession, args);
    }
    
    private MapperMethod cachedMapperMethod(Method method) {
        MapperMethod mapperMethod = methodCache.get(method);
        if (mapperMethod == null) {
            mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
            methodCache.put(method, mapperMethod);
        }
        return mapperMethod;
  }
    
    return mapperMethod.execute(sqlSession, args);
    判断sql语句类型 select、insert、delete、update
        
   public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
      case INSERT: {
    	Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        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 {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);
        }
        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;
  }
    
  name:Mapper类完整路径地址+方法名称组合
  通过源码分析可以得出 底层
result = sqlSession.<E>selectList(command.getName(), param);
    

mybatis运行的原理

1.初始化阶段:读取 解析XML 配置文件和注解中的配置信息,创建配置对象,并完成各个模块的初始化的工作,底层采用建造模式

2.代理封装阶段:封装 mybatis 的编程模型,使用 mapper 接口开发的初始化工作,底层爱用jdk动态代理模式

3.数据访问阶段:通过 SqlSession 完成 SQL 的解析,参数的映射、SQL 的执行、结果的解析过程;

谈谈建造模式模式设计原理

什么是建造者模式

建造者模式是设计模式的一种,将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

以下情况建议使用建造者模式:

1、某个对象有复杂的内部结构,通常包含多个成员属性,而且属性是可选的。

2、相同的方法,不同的执行顺序,产生不同的对象或不同的运行结果。

3、当创建一个对象需要很多步骤时,适合使用建造者模式。

建造者模式应用场景

1.mybatis 解析xml

2.StringBuilder

3.OkHttpClient

Request request = new Request.Builder().url("http://www.mayikt.com").build();.build()😉

建造者模式小案例

例如我们现在封装HttpClient框架,里面有很多配置属性

例如 接口url、请求方法、超时时间、body等

方式1new MayiktHttpClient("http://www.mayikt.com", "POST");
new MayiktHttpClient("http://www.mayikt.com", "POST", 10000l);
方式2MayiktHttpClient post = new MayiktBuilder().setUrl("http://www.mayikt.com")
                .setMethod("POST").setBody("mayikt").setTimeout(1000l).build();
System.out.println(post);
package com.mayikt.test;

/**
 * @author 余胜军
 * @ClassName MayiktHttpClient
 * @qq 644064779
 * @addres www.mayikt.com
 * 微信:yushengjun644
 */
public class MayiktHttpClient {
    /**
     * 请求url
     */
    private String url;
    /**
     * 请求方法
     */
    private String method;

    /**
     * 超时时间
     */
    private Long timeout;
    /**
     * 内容
     */
    private String body;

    public MayiktHttpClient(String url, String method) {
        this.url = url;
        this.method = method;
    }

    public MayiktHttpClient() {

    }
    //retry


    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getMethod() {
        return method;
    }

    public void setMethod(String method) {
        this.method = method;
    }

    public Long getTimeout() {
        return timeout;
    }

    public void setTimeout(Long timeout) {
        this.timeout = timeout;
    }

    public String getBody() {
        return body;
    }

    public void setBody(String body) {
        this.body = body;
    }
}

我们使用

new MayiktHttpClient("http://www.mayikt.com", "POST")

如果需要传递三个参数 看起来逼格不是很高

        new MayiktHttpClient("http://www.mayikt.com", "POST",10000l);
package com.mayikt.test;

/**
 * @author 余胜军
 * @ClassName MayiktBuilder
 * @qq 644064779
 * @addres www.mayikt.com
 * 微信:yushengjun644
 */
public class MayiktBuilder {
    /**
     * 请求url
     */
    private String url;
    /**
     * 请求方法
     */
    private String method;

    /**
     * 超时时间
     */
    private Long timeout;
    /**
     * 内容
     */
    private String body;

    public MayiktHttpClient build() {
        return new MayiktHttpClient(this);
    }

    public MayiktBuilder setUrl(String url) {
        this.url = url;
        return this;
    }

    public MayiktBuilder setMethod(String method) {
        this.method = method;
        return this;
    }

    public MayiktBuilder setTimeout(Long timeout) {
        this.timeout = timeout;
        return this;
    }

    public MayiktBuilder setBody(String body) {
        this.body = body;
        return this;
    }

    public String getMethod() {
        return method;
    }


    public String getUrl() {
        return url;
    }

    public Long getTimeout() {
        return timeout;
    }


    public String getBody() {
        return body;
    }

}

package com.mayikt.test;

/**
 * @author 余胜军
 * @ClassName MayiktHttpClient
 * @qq 644064779
 * @addres www.mayikt.com
 * 微信:yushengjun644
 */
public class MayiktHttpClient {
    /**
     * 请求url
     */
    private String url;
    /**
     * 请求方法
     */
    private String method;

    /**
     * 超时时间
     */
    private Long timeout;
    /**
     * 内容
     */
    private String body;

    public MayiktHttpClient(String url, String method) {
        this.url = url;
        this.method = method;
    }

    public MayiktHttpClient(String url, String method, Long timeout) {
        this.url = url;
        this.method = method;
        this.timeout = timeout;
    }

    public MayiktHttpClient(MayiktBuilder mayiktBuilder) {
        this.url = mayiktBuilder.getUrl();
        this.body = mayiktBuilder.getBody();
        this.method = mayiktBuilder.getMethod();
        this.timeout = mayiktBuilder.getTimeout();
    }

    public MayiktHttpClient() {

    }
    //retry


    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getMethod() {
        return method;
    }

    public void setMethod(String method) {
        this.method = method;
    }

    public Long getTimeout() {
        return timeout;
    }

    public void setTimeout(Long timeout) {
        this.timeout = timeout;
    }

    public String getBody() {
        return body;
    }

    public void setBody(String body) {
        this.body = body;
    }

    @Override
    public String toString() {
        return "MayiktHttpClient{" +
                "url='" + url + '\'' +
                ", method='" + method + '\'' +
                ", timeout=" + timeout +
                ", body='" + body + '\'' +
                '}';
    }
}

用法:

        new MayiktHttpClient("http://www.mayikt.com", "POST");
        new MayiktHttpClient("http://www.mayikt.com", "POST", 10000l);
        MayiktHttpClient post = new MayiktBuilder().setUrl("http://www.mayikt.com")
                .setMethod("POST").setBody("mayikt").setTimeout(1000l).build();
        System.out.println(post);

相关代码

📎mybatis源码解读01.rar

mybatis 建造模式源码解读

configuration

configuration 对象的关键属性解析如下:

MapperRegistry:mapper 接口动态代理工厂类的注册中心。在 MyBatis 中,通过mapperProxy 实现 InvocationHandler 接口,MapperProxyFactory 用于生成动态代理的实例对象;

ResultMap:用于解析 mapper.xml 文件中的 resultMap 节点,使用 ResultMapping 来封装id,result 等子元素;

MappedStatement:用于存储 mapper.xml 文件中的 select、insert、update 和 delete 节点,同时还包含了这些节点的很多重要属性;

SqlSource:用于创建 BoundSql,mapper.xml 文件中的 sql 语句会被解析成 BoundSql 对象,经过解析 BoundSql 包含的语句最终仅仅包含?占位符,可以直接提交给数据库执行;

mybatis解析xml配置文件源码解读

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

1.BaseBuilder:所有解析器的父类,包含配置文件实例,为解析文件提供的一些通用的方法;

2.XMLConfigBuilder: 主要负责解析 mybatis-config.xml;

3.XMLMapperBuilder: 主要负责解析映射配置 Mapper.xml 文件;

4.XMLStatementBuilder: 主要负责解析映射配置文件中的 SQL 节点;

mybatis-config.xml读取配置源码解读

1.mybatis 底层采用 dom解析配置xml配置

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
	try {
	//读取xml相关配置
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
		
 public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
    this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
  }
		

  private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
	  //标记 如果parsed该是为false没有解析 如果是为true的情况下
	  //则是已经解析过 parsed=true
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
  }

  public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
    commonConstructor(validation, variables, entityResolver);
     //读取xml相关配置(dom)
    this.document = createDocument(new InputSource(inputStream));
  }	

  private Document createDocument(InputSource inputSource) {
    // important: this must only be called AFTER common constructor
    try {
      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      factory.setValidating(validation);

      factory.setNamespaceAware(false);
      factory.setIgnoringComments(true);
      factory.setIgnoringElementContentWhitespace(false);
      factory.setCoalescing(false);
      factory.setExpandEntityReferences(true);

      DocumentBuilder builder = factory.newDocumentBuilder();
      builder.setEntityResolver(entityResolver);
      builder.setErrorHandler(new ErrorHandler() {
        @Override
        public void error(SAXParseException exception) throws SAXException {
          throw exception;
        }

        @Override
        public void fatalError(SAXParseException exception) throws SAXException {
          throw exception;
        }

        @Override
        public void warning(SAXParseException exception) throws SAXException {
        }
      });
      // 解析xml配置
      return builder.parse(inputSource);
    } catch (Exception e) {
      throw new BuilderException("Error creating document instance.  Cause: " + e, e);
    }
  }

断点debug 查看document 解析xml配置成功

img

mybatis解析mapper相关配置

new new XMLConfigBuilder(inputStream, environment, properties);private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    this.parsed = false;  //已经解析是为false
    this.environment = environment;
    this.parser = parser;
  }
		

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
	  //读取xml成功之后开始解析xml       
      return build(parser.parse()); 
   //解析xml成功之后会得到一个 Configuration 
  public Configuration parse() {
	 //因为在构造函数设置了parsed 为fasle,每个XMLConfigBuilder只能使用一次。
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
	// 标识为:true
    parsed = true;
	//开始从根节点解析
	 
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }
!-- 数据库相关的配置-->
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name=	
//解析mybatis-config.xml相关配置 最后赋值给我们的 configuration
private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      propertiesElement(root.evalNode("properties"));
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      environmentsElement(root.evalNode("environments"));
      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);
    }
  }
		
重点:      mapperElement(root.evalNode("mappers")); 解析mappers标签
mapperElement(root.evalNode("mappers"));	
private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
		  // 循环遍历
        if ("package".equals(child.getName())) {
          String mapperPackage = child.getStringAttribute("name");
          configuration.addMappers(mapperPackage);
        } else {
		  // 如果是为引入的resource 则循环 加载每个 resource XMLMapperBuilder
          String resource = child.getStringAttribute("resource");
          String url = child.getStringAttribute("url");
          String mapperClass = child.getStringAttribute("class");
          if (resource != null && url == null && mapperClass == null) {
            ErrorContext.instance().resource(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url != null && mapperClass == null) {
            ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url == null && mapperClass != null) {
            Class<?> mapperInterface = Resources.classForName(mapperClass);
            configuration.addMapper(mapperInterface);
          } else {
            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
          }
        }
      }
    }
  }
		
		

解析我们的resource相关配置

  public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
    commonConstructor(validation, variables, entityResolver);
    this.document = createDocument(new InputSource(inputStream));
  }
  private Document createDocument(InputSource inputSource) {
    // important: this must only be called AFTER common constructor
    try {
      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      factory.setValidating(validation);

      factory.setNamespaceAware(false);
      factory.setIgnoringComments(true);
      factory.setIgnoringElementContentWhitespace(false);
      factory.setCoalescing(false);
      factory.setExpandEntityReferences(true);

      DocumentBuilder builder = factory.newDocumentBuilder();
      builder.setEntityResolver(entityResolver);
      builder.setErrorHandler(new ErrorHandler() {
        @Override
        public void error(SAXParseException exception) throws SAXException {
          throw exception;
        }

        @Override
        public void fatalError(SAXParseException exception) throws SAXException {
          throw exception;
        }

        @Override
        public void warning(SAXParseException exception) throws SAXException {
        }
      });
      return builder.parse(inputSource);
    } catch (Exception e) {
      throw new BuilderException("Error creating document instance.  Cause: " + e, e);
    }
  }

解析完成配置文件之后:将我们的Configuration交给DefaultSqlSessionFactory

openSession源码解读

@Override
public SqlSession openSession() {
    // 默认传递一个执行 ----简单的执行器
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }
  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
	  // 获取环境配置
      final Environment environment = configuration.getEnvironment();
	  // 获取TransactionFactory
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
	  // 获取Transaction事务
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
	  // 根据事务 execType 获取 Executor 执行器
      final Executor executor = configuration.newExecutor(tx, execType);
	  // new DefaultSqlSession 最后封装我们的configuration、executor、autoCommit
	  // 封装到DefaultSqlSession返回。
		
      return new 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();
    }
  }
//获取 Executor 执行器  
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);
    }
	// 如果cacheEnabled是为true 返回CachingExecutor 判断是否开启了二级缓存
	// 走我们的CachingExecutor 执行器
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

SimpleExecutor:默认的Executor,每个SQL执行时候都会创建新的Statement

ReuseExecutor:相同的SQL会重复使用Statement

BatchExecutor:用于批处理的Executor

CachingExecutor:可缓存数据的Executor,用代理模式包装了其他类型的Executor

// 如果cacheEnabled是为true 返回CachingExecutor 则开启mybatis 二级缓存

<?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>
    <settings>
        <!-- 打印sql日志 -->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
        <!--开启二级缓存-->
        <setting name="cacheEnabled" value="true"/>
    </settings>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/mayikt?serverTimezone=GMT%2B8"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/userMapper.xml"/>
    </mappers>
</configuration>

*//3. 创建SqlSessionFactoryBuilder.build(inputStream) 建造者 键盘快捷键ctrl+点击方法
*SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
*//4.获取到session //传递true 或者false 是否自动提交 true
*SqlSession sqlSession = sqlSessionFactory.openSession(false);

1.解析mybatis-configxml 得到Configuration 在将该对象赋值给我们SqlSessionFactory

new SqlSessionFactoryBuilder().build(inputStream);

2.使用sqlSessionFactory 得到SqlSession 对象时 判断当前使用的执行器类型

SimpleExecutor:默认的Executor,每个SQL执行时候都会创建新的Statement

ReuseExecutor:相同的SQL会重复使用Statement

BatchExecutor:用于批处理的Executor

CachingExecutor:可缓存数据的Executor,用代理模式包装了其他类型的Executor

// 如果cacheEnabled是为true 返回CachingExecutor 则开启mybatis 二级缓存

默认采用SimpleExecutor 执行器 如果开启了二级缓存则使用CachingExecutor

SqlSession sqlSession = sqlSessionFactory.openSession(false);

最后返回了我们的默认的SqlSession

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值