【Mybatis源码解析】- Mybatis入门操作

Mybatis入门

MyBatisHibernate一样,是一个优秀的持久层框架。已经说过很多次了,原生的jdbc操作存在大量的重复性代码(如注册驱动,创建连接,创建statement,结果集检测等)。框架的作用就是把这些繁琐的代码封装,这样可以让程序员专注于sql语句本身。

MyBatis通过XML或者注解的方式将要执行的sql语句配置起来,并通过java对象和sql语句映射成最终执行的sql语句。最终由MyBatis框架执行sql,并将结果映射成java对象并返回。

Mybatis的执行流程

我们大致了解一下Mybatis的执行流程,结合案例工程来了解一下他是怎么工作的,后面将通过一步步的分析源码的方式进行深一步的了解

1、读取Mybatis配置信息

包含全局配置文件和映射文件。全局配置文件包含来数据源、日志、事务等信息,映射文件包含来SQL执行相关的信息

2、获取SqlSessionFactory工厂

  • 通过XmlConfigBuilder加载配置信息,例如properties外部文件,alias别名,plugins插件等信息
  • 通过XmlMapperBuilder加载Mapper配置文件信息
  • 处理每一个mapper内的信息。
  • 通过XmlStatementBuilder处理标签为select|insert|update|delete的语句,最终调用addMappedStatement方法,将mapper配置文件中的每一条SQL语句封装成mappedStatement对象,作为value保存在HashMap集合中;
  • 下一步执行addLoadedResource使用HashSet集合存放mybatismapper.xml 映射文件路径地址;
  • 进入bindMapperForNamespace()方法,通过namespace使用Java反射机制找到mapper接口,再调用addMapper()方法,判断是否是接口类型,是否注册过(注册过则抛出异常)其中mapperRegistry通过HashMap保存mapper接口,【key:接口;value:MapperProxyFactory】

3、获取SqlSession

  • 进入openSession()方法,执行newExecutor()方法创建执行器;
  • 先创建 SimpleExecutor简单执行器,再判断是否开启了二级缓存,默认是开启的,就会去创建CacheExecutor缓存执行器
  • 执行interceptorChain.pluginAll()方法,责任链设计模式,底层使用动态代理技术,使开发者可以自定义插件开发,只需要实现Interceptor接口,并指定想要拦截的方法签名即可,最后返回执行器;

4、操作Mapper接口

  • 调用getMapper()方法,最终执行mapperProxyFactory.newInstance(sqlSession)方法创建代理类MapperProxy
  • 执行MapperProxy代理类的invoke()方法;
  • 判断mapper接口是否有实现类,显然我们没有实现类,则调用cacheMapperMethod()方法去缓存中获取要代理的方法method
  • 进入cacheMapperMethod()方法先去查找缓存中有没有,没有的话将mapper配置文件中配置的SQL语句和对应的mapper接口方法进行关联并放入map缓存中,后期直接走缓存了,最后执行execute()方法;
  • 执行execute()方法,最终调用select*()方法;
  • 进入selectList()方法,调用getMapperStatement()方法获取对应的SQL语句;
  • 执行query()方法进行查询,判断如果开启了二级缓存并且配置了二级缓存存储介质(Redis,EhCache..)则先走二级缓存中查询数据,第一次查询是没有缓存数据的,则刷新缓存配置,清除缓存。
  • 二级缓存(sessionFactory)中没有查询到数据,就回去执行BaseExecutor去查询 HashMap一级缓存中(sqlSession)是否有缓存数据,一级缓存(PerpetualCache)存放在内存中的,同理也是没有的,最后查询数据库DB

5、封装结果

案例

1、数据及Jar包准备

mybatis组件及mysql驱动

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

原始表数据

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for blog
-- ----------------------------
DROP TABLE IF EXISTS `person`;
CREATE TABLE `person` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `nickname` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
    `mobile` varchar(11) COLLATE utf8mb4_general_ci DEFAULT NULL,
    `age` int(2) DEFAULT NULL,
    `email` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

-- ----------------------------
-- Records of blog
-- ----------------------------
BEGIN;
INSERT INTO `person` VALUES (1, 'wujiwen',  '13011111111', 20, 'jiwenwu@outlook.com');
INSERT INTO `person` VALUES (2, 'mybatis', '13100000000', 10, 'service@mybatis.com');
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

2、配置文件信息

全局配置文件信息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>
    <properties resource="jdbc.properties"/>
    <typeAliases>
        <typeAlias type="cn.wujiwen.bean.Person" alias="person"/>
    </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>

    <mappers>
        <mapper resource="PersonMapper.xml"/>
    </mappers>
</configuration>

数据源信息

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/mybatis
username=root
password=passw0rd

映射文件Mapper.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="cn.wujiwen.mapper.PersonMapper">

    <insert id="insert" parameterType="person" keyColumn="id" keyProperty="id">
        insert into person (nickname,mobile,age,email) values (#{nickname},#{mobile},#{age},#{email});
    </insert>

    <update id="update" parameterType="person">
        update person
        <set>
            <if test="nickname != null">
                nickname = #{nickname},
            </if>
            <if test="mobile != null">
                mobile = #{mobile},
            </if>
            <if test="age != null">
                age = #{age},
            </if>
            <if test="email != null">
                email = #{email},
            </if>
        </set>
        where id = #{id}
    </update>

    <delete id="delete">
        delete from person where id = #{id}
    </delete>
    <select id="listPerson" resultType="person">
        select * from person;
    </select>

    <select id="getPerson" resultType="person">
        select * from person where id = #{id}
    </select>
</mapper>

Mapper接口


public interface PersonMapper {
    int insert(Person person);
    int update(Person person);
    int delete(Integer id);
    List<Person> listPerson();
    Person getPerson(Integer id);
}

测试

/**
 * Desc:
 *
 * @author wujw
 * @email jiwenwu@outlook.com
 * @date 2021/4/2
 */
public class MybatisStarted {
		@Test
    public void test() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession session = sqlSessionFactory.openSession();
        // 指定key获取sql
        Person person = ((Person) session.selectOne("cn.wujiwen.mapper.PersonMapper.getPerson", 1));
        System.out.println(person);
        
        // 通过代理方式
        PersonMapper mapper = session.getMapper(PersonMapper.class);
        List<Person> personList = mapper.listPerson();
        System.out.println(personList);
    }
}
posted @ 2021-04-28 15:37  黑米面包派  阅读(193)  评论(0编辑  收藏  举报