代码编织梦想


一、SpringBoot 集成MyBatis

1. pom.xml添加依赖

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-jdbc</artifactId>
 </dependency>
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
 <dependency>
     <groupId>org.mybatis.spring.boot</groupId>
     <artifactId>mybatis-spring-boot-starter</artifactId>
     <version>2.3.0</version>
 </dependency>
 <dependency>
     <groupId>com.mysql</groupId>
     <artifactId>mysql-connector-j</artifactId>
     <scope>runtime</scope>
 </dependency>

2.yml文件配置

server:
  port: 9994  #服务端口号

logging:
  level:
    root: INFO

spring:
  application:
    name: mybatis-test #提供者的服务名称--调用的时候根据改名称来调用对应服务的方法
  datasource:
    url: jdbc:mysql://127.0.0.1:3326/db?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: root


mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.johar.mybatis.mybatistest.mapper

3.定义PO

public class Company {
    private Integer id;

    private String name;

    private String address;
}
public class User {
    private Integer id;

    private String name;

    private Integer age;

    private Integer sex;

    private Company company;
}

4. 定义mapper

public interface CompanyMapper {
    Company selectCompanyById(Integer id);
}
public interface UserMapper {

    User selectUserById(Integer id);
}
<?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.johar.mybatis.mybatistest.mapper.CompanyMapper">
    <select id="selectCompanyById" resultType="com.johar.mybatis.mybatistest.po.Company">
        select * from t_company where id = #{id}
    </select>
</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="com.johar.mybatis.mybatistest.mapper.UserMapper">
    <resultMap id="BaseResultMap" type="com.johar.mybatis.mybatistest.po.User">
        <id property="id" javaType="java.lang.Integer" column="id" jdbcType="INTEGER"/>
        <result property="name" javaType="java.lang.String" column="name" jdbcType="VARCHAR"/>
        <result property="age" javaType="java.lang.Integer" column="age" jdbcType="TINYINT" />
        <result property="sex" javaType="java.lang.Integer" column="sex" jdbcType="TINYINT"/>
        <association property="company" javaType="com.johar.mybatis.mybatistest.po.Company" column="company_id" select="com.johar.mybatis.mybatistest.mapper.CompanyMapper.selectCompanyById">
            <id property="id" javaType="java.lang.Integer" column="id" jdbcType="INTEGER"/>
            <result property="name" javaType="java.lang.String" column="name" jdbcType="VARCHAR"/>
            <result property="address" javaType="java.lang.String" column="address" jdbcType="VARCHAR"/>
        </association>
    </resultMap>

    <select id="selectUserById" parameterType="java.lang.Integer" resultMap="BaseResultMap">
        select * from t_user where id = #{id}
    </select>
</mapper>

5.启动类

@SpringBootApplication
@MapperScan(basePackages = {"com.johar.mybatis.mybatistest.mapper"})
public class MybatisTestApplication {

    public static void main(String[] args) {
        SpringApplication.run(MybatisTestApplication.class, args);
    }

}
@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    public User getById(Integer id){
        return userMapper.selectUserById(id);
    }

}

二、sql执行过程

MyBatis作为常用的ORM框架之一,了解MyBatis中sql执行过程是应用开发必备的技能。如下图是MyBatis 的整体架构:
在这里插入图片描述
MyBatis的整体流程如下所示:
在这里插入图片描述
MyBatis的详细执行流程如下所示:
在这里插入图片描述

1.加载mapper

@MapperScan注解中默认的factoryBean是MapperFactoryBean,负责将CompanyMapper,UserMapper注入到Spring IOC。

@Override
  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }

最终调用的是MapperProxyFactory的newInstance,最终生成的是一个MapperProxy。

protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

2.将UserMapper.selectUserById方法映射为Mapper.xml的sql

前面说到,Mapper实际上时一个MapperProxy的实例,因此调用selectUserById实际调用的是MapperProxy的invoke方法。

@Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
      	// Object的方法直接调用
        return method.invoke(this, args);
      } else {
        // 将mapper中方法映射为Mapper.xml的sql
        return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }

  private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
    try {
      return MapUtil.computeIfAbsent(methodCache, method, m -> {
        if (m.isDefault()) {
          // 默认方法执行逻辑
          try {
            if (privateLookupInMethod == null) {
              return new DefaultMethodInvoker(getMethodHandleJava8(method));
            } else {
              return new DefaultMethodInvoker(getMethodHandleJava9(method));
            }
          } catch (IllegalAccessException | InstantiationException | InvocationTargetException
              | NoSuchMethodException e) {
            throw new RuntimeException(e);
          }
        } else {
          // 非默认方法执行逻辑
          return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
        }
      });
    } catch (RuntimeException re) {
      Throwable cause = re.getCause();
      throw cause == null ? re : cause;
    }
  }

接下来就是实际映射逻辑:

public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    // 映射逻辑见其构造函数中调用的resolveMappedStatement
    this.command = new SqlCommand(config, mapperInterface, method);
    this.method = new MethodSignature(config, mapperInterface, method);
  }

 // 实际执行的逻辑,将方法转换成sql:insert,update,delete,select
  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);
          if (method.returnsOptional()
              && (result == null || !method.getReturnType().equals(result.getClass()))) {
            result = Optional.ofNullable(result);
          }
        }
        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;
  }
private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
        Class<?> declaringClass, Configuration configuration) {
      // 接口类名.方法名 作为唯一值,查询对应的sql
      String statementId = mapperInterface.getName() + "." + methodName;
      if (configuration.hasStatement(statementId)) {
        return configuration.getMappedStatement(statementId);
      } else if (mapperInterface.equals(declaringClass)) {
        return null;
      }
      for (Class<?> superInterface : mapperInterface.getInterfaces()) {
        if (declaringClass.isAssignableFrom(superInterface)) {
          MappedStatement ms = resolveMappedStatement(superInterface, methodName,
              declaringClass, configuration);
          if (ms != null) {
            return ms;
          }
        }
      }
      return null;
    }
  }

3. SqlSessionTemplate拦截器SqlSessionInterceptor

4. Mybatis的插件机制

5. DefaultSelSession

6. BaseExecutor

7. SimpleExecutor

8. newStatementHandler

9.InterceptorChain

10.PreparedStatementHandler

11.PreparedStatement

12.BaseExecutor#closeStatement

13.SqlSesionUtils#closeSqlSession

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/ruoshui77/article/details/129679053

Mybatis执行流程浅析(附深度文章推荐&面试题集锦)-爱代码爱编程

首先推荐一个简单的Mybatis原理视频教程,可以作为入门教程进行学习:点我 (该教程讲解的是如何手写简易版Mybatis) 执行流程的理解 理解Mybatis的简单流程后自己手写一个,可以解决百分之70的面试问题和开发中遇到的困惑,此乃重中之重 假如我们要自己设计一个半自动的仿Mybatis框架,有哪些环节是必不可少的呢?思考再

Mybatis的执行流程-爱代码爱编程

Mybatis的具体执行流程如下:   1. 首先mybatis中xml解析是通过SqlSessionFactoryBuilder.build()方法。 2. 初始化mybatis(解析xml文件构建成Configuration对象)并初始化SqlSessionFactory对象 在解析xml时会同时根据其中节点做相应的初始化操作   关键节点: se

【Mybatis】Mybatis执行流程简述-爱代码爱编程

【Mybatis】Mybatis执行流程简述 第一步: 通过Resource获取加载全局配置文件,调用getResourceAsStream方法加载xml文件,并将之解析为一个流。 //1.读取配置文件; InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml"); 第二步:

mybatis的执行流程-爱代码爱编程

mybatis的执行流程 public void selectUserById(){ try { // 1.获取InputStream流读取mybatis-conf.xml配置文件 InputStream is = Resources.getResourceAsStream("mybatis

Mybatis执行流程、缓存原理以及相关面试题-爱代码爱编程

一、Mybatis执行流程 具体分析Mybatis是如何操作数据库的! 、 1、定义我们的核心配置文件的路径,这个路径是从target/classes下开始找的! String config = "mybatis-config.xml" ; 2、读取这个config表示的文件 InputStream inputStrea

MyBatis 的执行流程-爱代码爱编程

前言 MyBatis可能很多人都一直在用,但是MyBatis的SQL执行流程可能并不是所有人都清楚了,那么既然进来了,通读本文你将收获如下: 1、Mapper接口和映射文件是如何进行绑定的 2、MyBatis中SQL语句的执行流程 3、自定义MyBatis中的参数设置处理器typeHandler 4、自定义MyBatis中结果集处理器typeH

mybatis 执行流程_学习笔记_滨海之君的博客-爱代码爱编程

采用正规的方式,写一个完整版的MyBatis程序。 package com.powernode.mybatis.test; import org.apache.ibatis.io.Resources; import or

mybatis执行过程详解_亨德萨姆的博客-爱代码爱编程

mybatis内部核心流程,详细说明如下文所示: (1)读取mybatis的全局配置文件mybatis-config.xml,包括environment,typealiases,setting,mappers等关键节点;

mybatis-执行流程简介_undefinedexception的博客-爱代码爱编程

目录  一、获取SqlSessionFactory 二、获取SqlSession 三、生成代理对象 四、执行Excutor 五、匹配执行SQL语句  一、获取SqlSessionFactory         SqlSessionFactory 有两个实现类:                 一个是 SqlSessionManag

mybatis基础知识复习笔记_使用jdk动态代理+mapperproxy。本质上调用的是mapperproxy的invoke方法。-爱代码爱编程

1. 什么是MyBatis Mybatis是一个半ORM(对象关系映射)框架,它内部封装了JDBC,开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。程序员直接编写