[闹着玩-1] mybatis源码查看

news/2024/7/3 10:21:50

MyBatis

配置

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.huifer</groupId>
    <artifactId>mybatisBook</artifactId>
    <version>1.0-SNAPSHOT</version>


    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.0</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.13</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.4</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <inherited>true</inherited>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

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://localhost:3306/dy_java?serverTimezone=UTC&amp;rewriteBatchedStatements=true&amp;useUnicode=true&amp;characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="DeptMapper.xml"/>
    </mappers>
</configuration>

测试

DeptMapper.xml

<?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="Dept">
    <insert id="insertDept" >
        INSERT INTO dept (dname, loc)
        VALUES (#{dname} , #{loc});
  </insert>
</mapper>

Dept.java

package com.huifer.mybatis;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 描述:
 *
 * @author huifer
 * @date 2019-02-21
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dept {
    private Long id;
    private String dname;
    private String loc;
}

测试类

public class Demo {

    public static void main(String[] args) throws Exception {

        Dept dept = new Dept();
        dept.setDname("技术部");
        dept.setLoc("oc");
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
        SqlSession session = factory.openSession();

        session.insert("insertDept", dept);
        session.commit();

        session.close();
    }

}

查询表

![](/ch-2/pic/mybatis/0001.png)

至此初步配置完成


执行过程

graph TD
    start[获取mybatis.config.xml] -->   conditionA[SqlSessionFactory]
    conditionA --> |xml 解析成 org.apache.ibatis.session.Configuration | conditionB[接收SqlSessionFactory] 
    conditionB --> |org.apache.ibatis.session.defaults.DefaultSqlSessionFactory.openSessionFromDataSource| conditionC[ SqlSession]
    conditionC --> |org.apache.ibatis.session.defaults.DefaultSqlSession insert方法 update方法| conditionD[insert方法]
    conditionD --> |  org.apache.ibatis.mapping.MappedStatement sqlSource属性 | conditionE[mapper 定位]
    conditionE --> | org.apache.ibatis.executor.SimpleExecutor doUpdate方法 stmt|conditionF[数据绑定到具体sql]
    conditionF --> |org.apache.ibatis.executor.statement.PreparedStatementHandler update方法 提交| conditionG[传输sql]

    conditionG -->|commit| stop

图片描述

session

获取mybatils-config.xml 配置 解析xml标签

    • org.apache.ibatis.session.defaults.DefaultSqlSessionFactory
      private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
            Transaction tx = null;
    
            DefaultSqlSession var8;
            try {
                Environment environment = this.configuration.getEnvironment();
                TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
                tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
                Executor executor = this.configuration.newExecutor(tx, execType);
                var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
            } catch (Exception var12) {
                this.closeTransaction(tx);
                throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var12, var12);
            } finally {
                ErrorContext.instance().reset();
            }
    
            return var8;
        }

    ![](/ch-2/pic/mybatis/0002.png)

insert

org.apache.ibatis.session.defaults.DefaultSqlSession

 public int insert(String statement, Object parameter) {
        return this.update(statement, parameter);
    }

    public int update(String statement, Object parameter) {
        int var4;
        try {
            this.dirty = true;
            MappedStatement ms = this.configuration.getMappedStatement(statement);
            var4 = this.executor.update(ms, this.wrapCollection(parameter));
        } catch (Exception var8) {
            throw ExceptionFactory.wrapException("Error updating database.  Cause: " + var8, var8);
        } finally {
            ErrorContext.instance().reset();
        }

        return var4;
    }

ms 对象

![](/ch-2/pic/mybatis/0003.png)

doUpdate

org.apache.ibatis.executor.SimpleExecutor

public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
        Statement stmt = null;

        int var6;
        try {
            Configuration configuration = ms.getConfiguration();
            StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, (ResultHandler)null, (BoundSql)null);
            stmt = this.prepareStatement(handler, ms.getStatementLog());
            var6 = handler.update(stmt);
        } finally {
            this.closeStatement(stmt);
        }

        return var6;
    }

![](/ch-2/pic/mybatis/0004.png)

update

org.apache.ibatis.executor.statement.PreparedStatementHandler
public int update(Statement statement) throws SQLException {
        PreparedStatement ps = (PreparedStatement)statement;
        ps.execute();
        int rows = ps.getUpdateCount();
        Object parameterObject = this.boundSql.getParameterObject();
        KeyGenerator keyGenerator = this.mappedStatement.getKeyGenerator();
        keyGenerator.processAfter(this.executor, this.mappedStatement, ps, parameterObject);
        return rows;
    }


Mapper

  <mappers>
        <!--<mapper resource="DeptMapper.xml"/>-->
        <package name="com.huifer.mybatis.dao"/>
    </mappers>

获取过程源码

  • org.apache.ibatis.builder.xml.XMLConfigBuilder 使用parseConfiguration 方法将 mybatis-config.xml 中的mappers 标签内容获取到 mapperElement具体执行获取内容 ,作用将mappers添加到configuration 中

    private void parseConfiguration(XNode root) {
            try {
                this.propertiesElement(root.evalNode("properties"));
                Properties settings = this.settingsAsProperties(root.evalNode("settings"));
                this.loadCustomVfs(settings);
                this.loadCustomLogImpl(settings);
                this.typeAliasesElement(root.evalNode("typeAliases"));
                this.pluginElement(root.evalNode("plugins"));
                this.objectFactoryElement(root.evalNode("objectFactory"));
                this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
                this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
                this.settingsElement(settings);
                this.environmentsElement(root.evalNode("environments"));
                this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
                this.typeHandlerElement(root.evalNode("typeHandlers"));
                this.mapperElement(root.evalNode("mappers"));
            } catch (Exception var3) {
                throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
            }
        }
    
    
    private void mapperElement(XNode parent) throws Exception {
            if (parent != null) {
                Iterator var2 = parent.getChildren().iterator();
    
                while(true) {
                    while(var2.hasNext()) {
                        XNode child = (XNode)var2.next();
                        String resource;
                        if ("package".equals(child.getName())) {
                            resource = child.getStringAttribute("name");
                            this.configuration.addMappers(resource);
                        } else {
                            resource = child.getStringAttribute("resource");
                            String url = child.getStringAttribute("url");
                            String mapperClass = child.getStringAttribute("class");
                            XMLMapperBuilder mapperParser;
                            InputStream inputStream;
                            if (resource != null && url == null && mapperClass == null) {
                                ErrorContext.instance().resource(resource);
                                inputStream = Resources.getResourceAsStream(resource);
                                mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());
                                mapperParser.parse();
                            } else if (resource == null && url != null && mapperClass == null) {
                                ErrorContext.instance().resource(url);
                                inputStream = Resources.getUrlAsStream(url);
                                mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments());
                                mapperParser.parse();
                            } else {
                                if (resource != null || url != null || mapperClass == null) {
                                    throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
                                }
    
                                Class<?> mapperInterface = Resources.classForName(mapperClass);
                                this.configuration.addMapper(mapperInterface);
                            }
                        }
                    }
    
                    return;
                }
            }
        }

    ![](/ch-2/pic/mybatis/0005.png)

    当前Configuration 下的mappedStatements 属性

    ![](/ch-2/pic/mybatis/0006.png)

  • org.apache.ibatis.binding.MapperRegistry addMapper 方法,作用将解析到的接口对象放到configuration 中 ,一个接口只能注册一次

    
        public <T> void addMapper(Class<T> type) {
            if (type.isInterface()) {
                if (this.hasMapper(type)) {
                    throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
                }
    
                boolean loadCompleted = false;
    
                try {
                    this.knownMappers.put(type, new MapperProxyFactory(type));
                    MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type);
                    parser.parse();
                    loadCompleted = true;
                } finally {
                    if (!loadCompleted) {
                        this.knownMappers.remove(type);
                    }
    
                }
            }
    
        }
  • org.apache.ibatis.builder.annotation.MapperAnnotationBuilder parse方法

    loadXmlResource 来确认加载具体的xml文件

    public void parse() {
            String resource = this.type.toString();
            if (!this.configuration.isResourceLoaded(resource)) {
                this.loadXmlResource();
                this.configuration.addLoadedResource(resource);
                this.assistant.setCurrentNamespace(this.type.getName());
                this.parseCache();
                this.parseCacheRef();
                Method[] methods = this.type.getMethods();
                Method[] var3 = methods;
                int var4 = methods.length;
    
                for(int var5 = 0; var5 < var4; ++var5) {
                    Method method = var3[var5];
    
                    try {
                        if (!method.isBridge()) {
                            this.parseStatement(method);
                        }
                    } catch (IncompleteElementException var8) {
                        this.configuration.addIncompleteMethod(new MethodResolver(this, method));
                    }
                }
            }
    
            this.parsePendingMethods();
        }
    
    
        private void loadXmlResource() {
            if (!this.configuration.isResourceLoaded("namespace:" + this.type.getName())) {
                String xmlResource = this.type.getName().replace('.', '/') + ".xml";
                InputStream inputStream = this.type.getResourceAsStream("/" + xmlResource);
                if (inputStream == null) {
                    try {
                        inputStream = Resources.getResourceAsStream(this.type.getClassLoader(), xmlResource);
                    } catch (IOException var4) {
                    }
                }
    
                if (inputStream != null) {
                    XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, this.assistant.getConfiguration(), xmlResource, this.configuration.getSqlFragments(), this.type.getName());
                    xmlParser.parse();
                }
            }
    
        }
    

    根据 loadXmlResource 中下面这行得知 , PojoMapper.xml 要和 PojoMapperInterface 放在一个路径下

    String xmlResource = this.type.getName().replace('.', '/') + ".xml";

    ![](/ch-2/pic/mybatis/0007.png)

具体sql

  • org.apache.ibatis.builder.xml.XMLStatementBuilder parseStatementNode 方法解析sql语句

      <select id="deptFindById" resultType="com.huifer.mybatis.pojo.Dept" parameterType="java.lang.Long">
            select * from dept where dept.id=#{deptno};
        </select>
    
    public void parseStatementNode() {
            String id = this.context.getStringAttribute("id");
            String databaseId = this.context.getStringAttribute("databaseId");
            if (this.databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
                Integer fetchSize = this.context.getIntAttribute("fetchSize");
                Integer timeout = this.context.getIntAttribute("timeout");
                String parameterMap = this.context.getStringAttribute("parameterMap");
                String parameterType = this.context.getStringAttribute("parameterType");
                Class<?> parameterTypeClass = this.resolveClass(parameterType);
                String resultMap = this.context.getStringAttribute("resultMap");
                String resultType = this.context.getStringAttribute("resultType");
                String lang = this.context.getStringAttribute("lang");
                LanguageDriver langDriver = this.getLanguageDriver(lang);
                Class<?> resultTypeClass = this.resolveClass(resultType);
                String resultSetType = this.context.getStringAttribute("resultSetType");
                StatementType statementType = StatementType.valueOf(this.context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
                ResultSetType resultSetTypeEnum = this.resolveResultSetType(resultSetType);
                String nodeName = this.context.getNode().getNodeName();
                SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
                boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
                boolean flushCache = this.context.getBooleanAttribute("flushCache", !isSelect);
                boolean useCache = this.context.getBooleanAttribute("useCache", isSelect);
                boolean resultOrdered = this.context.getBooleanAttribute("resultOrdered", false);
                XMLIncludeTransformer includeParser = new XMLIncludeTransformer(this.configuration, this.builderAssistant);
                includeParser.applyIncludes(this.context.getNode());
                this.processSelectKeyNodes(id, parameterTypeClass, langDriver);
                SqlSource sqlSource = langDriver.createSqlSource(this.configuration, this.context, parameterTypeClass);
                String resultSets = this.context.getStringAttribute("resultSets");
                String keyProperty = this.context.getStringAttribute("keyProperty");
                String keyColumn = this.context.getStringAttribute("keyColumn");
                String keyStatementId = id + "!selectKey";
                keyStatementId = this.builderAssistant.applyCurrentNamespace(keyStatementId, true);
                Object keyGenerator;
                if (this.configuration.hasKeyGenerator(keyStatementId)) {
                    keyGenerator = this.configuration.getKeyGenerator(keyStatementId);
                } else {
                    keyGenerator = this.context.getBooleanAttribute("useGeneratedKeys", this.configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType)) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
                }
    
                this.builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, (KeyGenerator)keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
            }
        }

    ![](/ch-2/pic/mybatis/0008.png)

  • 还原sql方法

    SqlSource sqlSource = langDriver.createSqlSource(this.configuration, this.context, parameterTypeClass);
  • org.apache.ibatis.scripting.defaults.RawSqlSource RawSqlSource方法

    public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) {
            SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
            Class<?> clazz = parameterType == null ? Object.class : parameterType;
            this.sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap());
        }
  • org.apache.ibatis.builder.SqlSourceBuilder parse方法还原成sql语句

        public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
            SqlSourceBuilder.ParameterMappingTokenHandler handler = new SqlSourceBuilder.ParameterMappingTokenHandler(this.configuration, parameterType, additionalParameters);
            GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
            String sql = parser.parse(originalSql);
            return new StaticSqlSource(this.configuration, sql, handler.getParameterMappings());
        }

    ![](/ch-2/pic/mybatis/0009.png)

  • 最后看一下 sqlSource

    ![](/ch-2/pic/mybatis/0010.png)



http://www.niftyadmin.cn/n/3141275.html

相关文章

[2017.4.14] 随笔一 ------头文件中用宏定义调试语句(引用C语言写爬虫的初学项目)...

C语言刚学完的随笔。 1.学会在头文件中用宏定义调试语句 #define SPIDER_LOG(level, format, ...) do{ \ if (level > g_conf->log_level) {\ time_t now time(NULL); \ char msg[MAX_MESG_LEN]; \ char buf[32]; \ sprintf(msg, format, ##__VA_ARGS__); \ strftime(bu…

JQuery对数组的一些操作总结

$.each(array, [callback]) 遍历 不同于例遍 jQuery 对象的 $.each() 方法&#xff0c;此方法可用于例遍任何对象(不仅仅是数组哦~). 回调函数拥有两个参数&#xff1a;第一个为对象的成员或数组的索引&#xff0c; 第二个为对应变量或内容. 如果需要退出 each 循环可使回调函数…

python萌新:从零基础入门到放弃

不管是在什么领域&#xff0c;自学者都占绝大多数&#xff0c;你说自学可以吗&#xff1f;可以&#xff0c;没问题的&#xff0c;只需要你具备以下几点最基础的能力&#xff1a;  第一点&#xff1a;天赋。对于python而言其实是非常需要天赋的&#xff0c;很多人觉得只需要拿…

CHAPTER 1 ----- a tour of computer sysytems(1)

1.1Information is bits context. All information in a system——including disk files,programs stored in memory ,user data stored in memory ,and data transferred across a network——is represented as a bunch of bits.The only thing that distinguished differe…

关于Linux磁盘分区与双系统

Linux是一个很大的东西&#xff0c;而且稍微按照鸟哥的书做点网络上的扩展就会有很多很多的东西&#xff0c;常常会使得自己迷失&#xff0c;所以我认为在学习的时候&#xff0c;还是应该有自己的目标&#xff0c;写出关于目标的博客&#xff0c;不要什么都讲&#xff0c;却什么…

手把手教你如何新建scrapy爬虫框架的第一个项目(下)

前几天小编带大家学会了如何在Scrapy框架下创建属于自己的第一个爬虫项目&#xff08;上&#xff09;&#xff0c;今天我们进一步深入的了解Scrapy爬虫项目创建&#xff0c;这里以伯乐在线网站的所有文章页为例进行说明。在我们创建好Scrapy爬虫项目之后&#xff0c;会得到上图…

01. 机器学习基础

2019独角兽企业重金招聘Python工程师标准>>> # 机器学习基础 # 线性回归模型import numpy as np import matplotlib.pyplot as plt import tensorflow as tf# 从0到10取30个值 data_x np.linspace(0, 10, 30)# np.random.normal(0, 1, 30): 噪声&#xff0c;从0到1…

bash1---基本0

最近在看《高级BASH脚本编程》&#xff0c;边学习脚本编程&#xff0c;边学习Linux的命令。 另外还解决了笔记本电脑上关于Ubuntu无法找到wifi适配器的问题。具体看我另外一篇博客。 另外推荐一个学习linux命令的网站。 今天主要想讲一下如何备份最后一天所有修改的文件 #!/bin…