侧边栏壁纸
  • 累计撰写 98 篇文章
  • 累计创建 85 个标签
  • 累计收到 9 条评论

Mybatis源码简单分析一

bearjun
2021-08-16 / 0 评论 / 0 点赞 / 757 阅读 / 11,765 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2021-08-16,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

mybatis是什么

MyBatis 是一流的持久化框架,支持自定义 SQL、存储过程和高级映射。 MyBatis 消除了几乎所有的 JDBC 代码和手动设置参数和检索结果。 MyBatis 可以使用简单的 XML 或注解进行配置,并将原语、Map 接口和 Java POJO(Plain Old Java Objects)映射到数据库记录。

Mybatis是一个ORM类型的框架,解决的数据库访问和操作的问题,对现有的JDBC技术的封装。

官网地址:https://mybatis.org/mybatis-3/zh/index.html
mybatis和hibernate的对比看这个链接:https://blog.csdn.net/eff666/article/details/71332386

mybatis开发回顾

1、准备jar包

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

2、配置配置文件mybatis-config.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>
  <!--别名的设置-->
  <typeAliases>
    <package name="domain.blog"/>
  </typeAliases>
  <!--环境的设置-->
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
      </dataSource>
    </environment>
  </environments>
  <!--mapper的设置-->
  <mappers>
    <mapper resource="org/mybatis/example/BlogMapper.xml"/>
  </mappers>
</configuration>

XXMapper的配置:

<?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="org.mybatis.example.BlogMapper">
  <select id="selectBlog" resultType="Blog">
    select * from Blog where id = #{id}
  </select>
</mapper>

3、初始化配置
设置好应对的mybatis-config.xml中的数据源。

driver=com.mysql.cj.jdbc.Driver / com.mysql.jdbc.Driver
url=jdbc:mysql://localhost/bearweb?serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=convertToNull&autoReconnect=true&useUnicode=true&characterEncoding=utf-8
username=root
password=123456

注意:注意mysql5.7以及mysql8版本的区别,一个不带cj,一个带cj。

4、开发步骤(7步编程)

  • 创建表
  • 创建entity
  • 取类型别名
  • DAO接口
  • Mapper文件
  • Mapper文件的注册
  • API编程

mybatis数据存储类对象

在Java中或者JVM中对Mybatis相关的配置信息进行封装。

大家都知道,我们对文件的操作,是越少越好,因为文件的io操作很费内存,所以我们都是一次性读取,进而封装到配置对象中。
那Mybatis的配置文件有几种呢?
mybatis-config.xml ---> Configuration
xxxMapper.xml ---> MappedStatement

Configuration

public class Configuration {

    // 对应environments标签的内容
    protected Environment environment;

    // 对应settings标签的内容
    protected boolean safeRowBoundsEnabled;
    protected boolean safeResultHandlerEnabled = true;
    protected boolean mapUnderscoreToCamelCase;
    protected boolean aggressiveLazyLoading;
    protected boolean multipleResultSetsEnabled = true;
    protected boolean useGeneratedKeys;
    protected boolean useColumnLabel = true;
    protected boolean cacheEnabled = true;
    protected boolean callSettersOnNulls;
    protected boolean useActualParamName = true;
    protected boolean returnInstanceForEmptyRow;

    // 对应settings标签的内容
    protected String logPrefix;
    protected Class<? extends Log> logImpl;
    protected Class<? extends VFS> vfsImpl;
    protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
    protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
    protected Set<String> lazyLoadTriggerMethods = new HashSet<>(Arrays.asList("equals", "clone", "hashCode", "toString"));
    protected Integer defaultStatementTimeout;
    protected Integer defaultFetchSize;
    protected ResultSetType defaultResultSetType;
    protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
    protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
    protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;

    protected Properties variables = new Properties();
    protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
    protected ObjectFactory objectFactory = new DefaultObjectFactory();
    protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();

    protected boolean lazyLoadingEnabled = false;
    protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL

    protected String databaseId;

    protected Class<?> configurationFactory;

    protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
    protected final InterceptorChain interceptorChain = new InterceptorChain();
    protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry(this);
    protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
    protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();

    protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection")
            .conflictMessageProducer((savedValue, targetValue) ->
                    ". please check " + savedValue.getResource() + " and " + targetValue.getResource());
    protected final Map<String, Cache> caches = new StrictMap<>("Caches collection");
    protected final Map<String, ResultMap> resultMaps = new StrictMap<>("Result Maps collection");
    protected final Map<String, ParameterMap> parameterMaps = new StrictMap<>("Parameter Maps collection");
    protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<>("Key Generators collection");

    protected final Set<String> loadedResources = new HashSet<>();
    protected final Map<String, XNode> sqlFragments = new StrictMap<>("XML fragments parsed from previous mappers");

    protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<>();
    protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<>();
    protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<>();
    protected final Collection<MethodResolver> incompleteMethods = new LinkedList<>();


    protected final Map<String, String> cacheRefMap = new HashMap<>();

    //.......

    // 创建mybatis其他相关的对象
    public MetaObject newMetaObject(Object object) {
        return MetaObject.forObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
    }

    public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
        ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
        parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
        return parameterHandler;
    }

    public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
                                                ResultHandler resultHandler, BoundSql boundSql) {
        ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
        resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
        return resultSetHandler;
    }

    public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
        statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
        return statementHandler;
    }

    public Executor newExecutor(Transaction transaction) {
        return newExecutor(transaction, defaultExecutorType);
    }

    public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
        executorType = executorType == null ? defaultExecutorType : executorType;
        executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
        Executor executor;
        if (ExecutorType.BATCH == executorType) {
            executor = new BatchExecutor(this, transaction);
        } else if (ExecutorType.REUSE == executorType) {
            executor = new ReuseExecutor(this, transaction);
        } else {
            executor = new SimpleExecutor(this, transaction);
        }
        if (cacheEnabled) {
            executor = new CachingExecutor(executor);
        }
        executor = (Executor) interceptorChain.pluginAll(executor);
        return executor;
    }

    //.......

}

有上面的源码我们可以看出,Mybatis主要在configuration中封装了如下内容:

  • 封装了mybatis-config.xml相关的配置信息
  • 封装了mapper文件,MappedStatement对象
  • 创建了Mybatis其他相关的对象,像其他的一些Handler和executor之类对象

MappedStatement

public final class MappedStatement {

  private String resource;
  private Configuration configuration;
  private String id;
  private Integer fetchSize;
  private Integer timeout;
  private StatementType statementType;
  private ResultSetType resultSetType;
  private SqlSource sqlSource;
  private Cache cache;
  private ParameterMap parameterMap;
  private List<ResultMap> resultMaps;
  private boolean flushCacheRequired;
  private boolean useCache;
  private boolean resultOrdered;
  private SqlCommandType sqlCommandType;
  private KeyGenerator keyGenerator;
  private String[] keyProperties;
  private String[] keyColumns;
  private boolean hasNestedResultMaps;
  private String databaseId;
  private Log statementLog;
  private LanguageDriver lang;
  private String[] resultSets;

  MappedStatement() {
    // constructor disabled
  }

  public static class Builder {
    private MappedStatement mappedStatement = new MappedStatement();

    public Builder(Configuration configuration, String id, SqlSource sqlSource, SqlCommandType sqlCommandType) {
      mappedStatement.configuration = configuration;
      mappedStatement.id = id;
      mappedStatement.sqlSource = sqlSource;
      mappedStatement.statementType = StatementType.PREPARED;
      mappedStatement.resultSetType = ResultSetType.DEFAULT;
      mappedStatement.parameterMap = new ParameterMap.Builder(configuration, "defaultParameterMap", null, new ArrayList<>()).build();
      mappedStatement.resultMaps = new ArrayList<>();
      mappedStatement.sqlCommandType = sqlCommandType;
      mappedStatement.keyGenerator = configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
      String logId = id;
      if (configuration.getLogPrefix() != null) {
        logId = configuration.getLogPrefix() + id;
      }
      mappedStatement.statementLog = LogFactory.getLog(logId);
      mappedStatement.lang = configuration.getDefaultScriptingLanguageInstance();
    }
   // .....
   // 封装SQL  mapper中的sql被封装成一个BoundSql对象
   public BoundSql getBoundSql(Object parameterObject) {
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings == null || parameterMappings.isEmpty()) {
      boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
    }

    // check for nested result maps in parameter mappings (issue #30)
    for (ParameterMapping pm : boundSql.getParameterMappings()) {
      String rmId = pm.getResultMapId();
      if (rmId != null) {
        ResultMap rm = configuration.getResultMap(rmId);
        if (rm != null) {
          hasNestedResultMaps |= rm.hasNestedResultMaps();
        }
      }
    }

    return boundSql;
  }
}

对应的就是一个个的mapper文件中的配置。
例如:
-->MappedStatement
-->MappedStatement

故一个mybatis中会有n个MappedStatement对象。

mybatis操作类对象

除了上述的存储类对象对配置文件进行存储,这还远远不够。我们在进行数据库操作的时候,还应该有操作类对象,就是操作数据库来完成功能。
站在数据库操作层面来看,我们的各种DAO + entity就是操作各种entity来进行数据存储。
而站在Mybatis层面来看,SqlSession会调用各种executor和handler来进行操作处理。

Executor

Executor是Mybatis中处理功能的核心,主要负责增删改和查的操作。

22073-tkmutb8evcf.png
由Executor的基本结构可以看出:

  • 完成增删改update操作以及查query的操作
  • 关于事务之间的操作,包括事务的提交已经回滚
  • 还有关于缓存之间的操作

那么问题来呢?Executor为什么要设计成接口呢?
一般的,和操作相关的都会设计成接口,这样的话。方便扩展。
Executor接口主要有三个实现,这三个实现可以在BaseExecutor的子类中找到:

  • BatchExecutor:批量操作,一次连接,多次处理
  • ReuseExecutor:复用的Executor,复用statement对象,当参数都一致时
  • SimpleExecutor:常用的Executor

StatmentHandler

是Mybatis封装了JDBC Statment,真正Mybatis进行数据库访问操作的核心
功能:增删改查

30069-x1e703sqep.png
也是用的适配器模式,BaseStatementHandler实现StatementHandler,而子类也有三个:

  • CallableStatementHandler
  • PreparedStatementHandler
  • SimpleStatementHandler 进行JDBC操作

如果SimpleStatementHandler是解决数据库访问操作的,那么参数?又怎么处理呢?

ParameterHandler

目的:把Mybatis中的参数换成JDBC相关的参数
其实就是把@param注解中的#
或者$换成JDBC中需要的'?'

ResultSetHandler

对JDBC中查询的结果集ResultSet进行封装

TypeHandler

对java中的数据类型和数据库中的数据类型进行转换

0

评论区