你还是只会用Mybatis,并不知其原理么

       一生猿,一世猿。
                    —— 猿医生·yys 

                                    

 

目录

 

一、前言

二、原生 mybatis

三、核心对象介绍

四、核心配置介绍

 

 

一、前言

     你还是只会用 Mybatis,并不知其原理么?

     相信很多开发小伙伴们,大都停留在会使用mybatis的基础上,至于原理及内部实现,并没有过多关注。

     注意,这里我说的会使用,也仅仅是被我们伟大的 spring集成之后的 mybatis,而对于原生态的 mybatis而言,相信更多的小伙伴的表情此时已渐渐凝固 ~

                                    

  

       信不信由你,原谅配图时的我不厚道的被这个老外哥逗笑了 ~ (有同感的,点个赞吧 ~)

       话题转移成功,言归正传,最近呢 ,笔者也正在研究 mybatis源码,总结了一些学习心得以及重点知识。

       本篇重点讲解 mybatis的核心对象以及核心配置,后续博文将持续更新 mybatis源码剖析,缓存机制,执行流程....

       在此,记录一下,分享给大家。

       文章最后有彩蛋哦,不看会后悔哈 ~

二、原生 mybatis


        // 1.加载注册JDB驱动
        Class.forName("com.mysql.jdbc.Driver");

        // 2.创建数据库连接,连接参数:地址/用户名/密码
        conn = DriverManager.getConnection(URL, USER, PASS);

        // 3.执行查询
        String sql = " SELECT * FROM yys_user ";
        stmt = conn.createStatement();
        rs = stmt.executeQuery(sql);

        // 4.遍历结果集
        while (rs.next()) {

            // 参数获取
            Long uid = rs.getLong("uid");
            String name = rs.getString("name");
            Integer age = rs.getInt("age");

            // 对象封装
            user.setUid(uid);
            user.setName(name);
            user.setAge(age);
        }
        System.out.println(user);

        // 5.释放资源 - 自下而上的关闭

                                                                                                    

初入职场A猿,略带疑惑的目光,轻言道:

这?....擦了擦双眼,又仔细看了看,这...是原生JDBC呐,这怎么能难倒我,工具类都是我抽取的  ~

有经验的开发B猿,摸了摸略带胡渣的下巴,沉吟道:

不错..不错,笔者思维缜密,逻辑严谨,先从原生JDBC入手,即视的代入感,不慌不忙的点了个赞 ~

沉默的笔者:

                                                                                                           

言归正传,其实对于市面上已经非常成熟的ORM框架或者工具类包,如 Apache的 DBUtils,Spring的 JdbcTemplate,Hibernate, Mybatis,其实它们底层实现都是对 JDBC的封装,我们去查看源码,最后一定会看到上面代码中的核心对象。

而现在大多数公司,大都再使用 Mybatis来作为持久层框架,笔者也是 mybatis爱好者之一,至于原因么,很简单... 就是,公司用的是mybatis, 没办法 ~

                                                                                                          

一般玩笑过后,不是总结,就是总结 ~ 我们来总结下 mybatis的一些特性及优点:

     1、 使用连接池对连接进行管理

     2、 SQL 和代码分离,集中管理

     3、 结果集映射

     4、 参数映射和动态 SQL

     5、 重复 SQL 的提取

     6、 缓存管理

     7、 插件机制

接下来,回到此篇文章的重点,原生Mybatis闪亮登场 ~

1.先引入 mybatis jar包

2.创建 mybatis核心配置文件,定义了对 mybatis核心行为的控制, mybatis-config.xml

3.定义 mapper文件,一般一张表对应一个mapper.xml,其中配置 curd的 sql,以及参数和返回结果的映射关系

准备工作完成,终于到本章重点,关门...上...案例  ~

方式一:通过 SqlSession调用API方式 


        // 读取 mybatis配置文件
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");

        // 通过 SqlSessionFactoryBuilder 创建 SqlSessionFactory工厂
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        // 创建 SqlSession连接
        SqlSession session = sqlSessionFactory.openSession();

        try {
            // 通过 SqlSession调用API方式,执行查询
            User user = (User) session.selectOne("com.yys.mapper.UserMapper.selectById", 1);
            System.out.println(user);

        } finally {
            // 释放资源
            session.close();
        }

缺点:硬编码,编译时不能进行类型检查,小众不推荐 ~

方式二:Mapper接口方式


        // 读取 mybatis配置文件
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");

        // 通过 SqlSessionFactoryBuilder 创建 SqlSessionFactory工厂
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        // 创建 SqlSession连接
        SqlSession session = sqlSessionFactory.openSession();

        try {
            // 通过 Mapper接口方式,执行查询
            UserMapper mapper = session.getMapper(UserMapper.class);
            User user = mapper.selectUserById(1);
            System.out.println(user);

        } finally {
            // 释放资源
            session.close();
        }

看到这里,建议大家,带着四个对象(sqlSessionFactoryBuiler、sqlSessionFactory、sqlSession、mapper)再看一遍案例代码

思考下,这四个对象分别的生命周期以及它们之间关系 ~

三、核心对象介绍

重点介绍核心对象的 1、生命周期   2、作用

(1)、SqlSessionFactoryBuiler

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

显而易见,SqlSessionFactoryBuiler是用来创建 SqlSessionFactory对象的,而在创建SqlSessionFactory工厂对象完成后,它的使命也就完成了,所以它的生命周期只存在于方法的局部。

(2)、SqlSessionFactory​​​​​​

   SqlSession session = sqlSessionFactory.openSession();

可以看出,SqlSessionFactory工厂对象是用来创建 SqlSession对象的,再说说其生命周期,每次应用程序访问数据库,都需要创建一个会话,所以SqlSessionFactory工厂对象应该存在于应用的的整个生命周期中,而且一次访问数据库中,一个SqlSession实例来做这件事就可以,这里采用单例模式。

(3)、SqlSession

可以看出,SqlSession是一个会话,非线程安全的,故不能再线程中共享,所以在方法开始或请求开始时创建一个 SqlSession对象,在方法结束或请求结束时及时关闭它。

(4)、Mapper
   UserMapper mapper = session.getMapper(UserMapper.class);

 

从SqlSession对象中获取的 Mapper对象实际上是一个代理对象,它的作用是发送 sql来操作数据库的,它的生命周期是在一个SqlSession事务方法内。

总结:

核心对象

生命周期

SqlSessionFactoryBuiler

方法局部(Method)

SqlSessionFactory (单例)

应用级别(Application)

SqlSession

请求/方法(Request/Method)

Mapper

方法(Method)

四、核心配置介绍

之前在配置 mybatis的准备工作中,提到mybatis的核心配置文件(mybatis-config.xml),接下来,介绍下重点配置标签下:

(1)、configuration

是 mybatis核心配置文件的根标签,也对应着 mybatis中最重要的配置类Configuration;

注意:MyBatis 全局配置文件顺序是固定的,否则启动的时候会报错

(2)、properties

   <properties resource="db.properties"></properties>

用来配置参数相关信息,eg:最常见的数据库连接信息。

如代码看到的,db.properties中配置得是数据库的连接信息,通过properties标签引入即可,其优势如下:

1、 配置提取,有利于多处引用,便于维护;

2、 项目发布时,配置文件放在外部,避免修改后重新编译打包,此种做法只需要重启应用;

3、 程序和配置分离,提升数据的安全性,比如生产环境的密码只有管理人员/运维人员知道。

(3)、settings

settings标签很重要,Mybatis中非常关键的配置都在这个标签中

属性名

含义

简介

枚举值

默认值

useGeneratedKeys

是否支持 JDBC 自动生成主键

 

设置之后,将会强制使用自动生成主键的策略

 

true / false

 

false

cacheEnabled

是否使用缓存

 

是整个工程中所有映射器配置缓存的

开关,即是一个全局缓存开关

 

true / false

 

true

lazyLoadingEnabled

是否开启延迟加载

 

控制全局是否使用延迟加载

(association、collection)。当有

特殊关联关系需要单独配置时,可以使

用 fetchType 属性来覆盖此配置

 

true / false

 

false

aggressiveLazyLoading

是否需要侵入式延迟加载

开启时,无论调用什么方法加载某个对

象,都会加载该对象的所有属性,关闭

之后只会按需加载

 

true / false

 

false

defaultExecutorType

设置默认的执行器

 

有三种执行器:SIMPLE 为普通执行器;

REUSE 执行器会重用与处理语句;

BATCH 执行器将重用语句并执行批量

更新

 

SIMPLE

REUSE

BATCH

 

 

SIMPLE

lazyLoadTriggerMethods

指定哪个对象的方法触发一次延迟加载

配置需要触发延迟加载的方法的名字,

该方法就会触发一次延迟加载

 

一个逗号分隔的

方法名称列表

 

Equals

clone hashCode toString

 

localCacheScope

MyBatis 利用本地缓存机制(LocalCache)防止循环引用(circularreferences)和加

速重复嵌套查询

 

默认值为 SESSION,这种情况下会缓存

一个会话中执行的所有查询。若设置值

为 STATEMENT,本地会话仅用在语句执

行上,对相同 SqlSession 的不同调用

将不会共享数据

 

SESSION

STATEMENT

 

 

SESSION

logImpl

日志实现

 

指定 MyBatis 所用日志的具体实现,未

指定时将自动查找

 

SLF4J / LOG4J LOG4J2 JDK_LOGGING COMMONS_LOGGING STDOUT_LOGGING NO_LOGGING

 

(4)、typeAliases

typeAliases标签顾名思义,用来给类全路径配置别名的,具体案例如下:

I、用户自定义别名

1.mybatis的 核心配置文件(mybatis-config.xml)配置如下:

   <typeAliases>
       <typeAlias alias="user" type="com.yys.entity.User" />
   </typeAliases>

2.mapper.xml配置

   <!-- 结果集映射 -->
   <resultMap id="BaseResultMap" type="user">
       <id column="uid" property="uid" jdbcType="Long"/>
       <result column="name" property="name" jdbcType="VARCHAR"/>
       <result column="age" property="age" jdbcType="INTEGER"/>
   </resultMap>
   <!-- 通过ID查询用户 -->
   <select id="selectUserById"  parameterType="Long" resultType="user" >
       select uid, name, age from user where uid = #{uid}
   </select>

II、MyBatis 中系统预先定义好的类型别名

     mybatis配置类Configuration.java 

(5)、typeHandlers

typeHandlers标签有点东西了哈,再讲之前,提问个问题:

为什么 java对象里的一个String类型,可以保存成数据库中的varchar、char字段呢?

其实这个问题,仔细想下,肯定是有一个配置可以让String类型转换为varchar,很显然,mybatis的作者已经提前定义很多TypeHandler,都继承了抽象类 BaseTypeHandler,泛型就是要处理的 Java 数据类型,当我们做数据类型转换的时候,就会自动调用对应的 TypeHandler 的方法,如下图:

                                                                                                   

正如所图,我们需要冷静一下,大量知识拥入,容易堵塞...  深呼吸一口,我们继续前行 ~

接下来,我们不去看TypeHandler的源码实现,我们来自定义一个TypeHandler :

                                                                                  

老办法,我们先来深呼吸,俗话说,照猫画虎,对于程序员来说,我们可是攻城“狮” ~

1.定义YysTypeHandler类,继承抽象类BaseTypeHandler<T>类,并实现其方法:

/**
 * Mybatis
 * 	自定义TypeHandler
 * 		
 * @author zhaojun
 */
public class YysTypeHandler extends BaseTypeHandler<String> {

    /**
     * Java类型 -> JDBC类型
     *      设置String类型参数时调用
     */
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i,
                    String parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, parameter);
    }

    /**
     * JDBC类型 -> Java类型
     *      根据列名 获取String类型参数时调用
     */
    @Override
    public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return rs.getString(columnName);
    }

    /**
     * JDBC类型 -> Java类型
     *      根据下标 获取String类型参数时调用
     */
    @Override
    public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return rs.getString(columnIndex);
    }

    /**
     * JDBC类型 -> Java类型
     *      存储过程方式时调用
     */
    @Override
    public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return cs.getString(columnIndex);
    }

}

2.核心配置文件(mybatis-config.xml)增加配置:

    <typeHandlers>
        <typeHandler handler="com.yys.type.YysTypeHandler"></typeHandler>
    </typeHandlers>

3.在我们需要使用的字段上指定,比如:

    <!-- Java类型 到 JDBC类型 -->

    <insert id="insertUser" parameterType="user">
        insert into user (uid, name, age)
        values (
            #{uid,jdbcType=Long}, 
            #{name,jdbcType=VARCHAR,typeHandler=com.yys.type.YysTypeHandler}, 
            #{age,jdbcType=Integer})
    </insert>
    <!-- JDBC类型 到 Java类型 -->

    <result column="name" property="name" jdbcType="VARCHAR" typeHandler="com.yys.type.YysTypeHandler"/>

这时候,看完的小伙伴心想:“也就那么回事么”,哈哈,的确这么回事 ~

(6)、environments、environment

environments、environment标签,常用来管理数据库环境,

在日常工作中,我们一般分为开发环境、测试环境、生产环境,每个环境都有不同的数据库配置,

这时我们就就可以利用 environment标签来进行管理

    <environments default="dev">

        <!-- 开发环境 -->
        <environment id="dev">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>

        <!-- 测试环境 -->
        <environment id="test">
            <transactionManager type="MANAGED"/>
            <dataSource type="POOLED">
                <!-- 此处省略 -->
            </dataSource>
        </environment>

        <!-- 线上环境 -->
        <environment id="prd">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!-- 此处省略 -->
            </dataSource>
        </environment>

    </environments>

(7)、transactionManager

配置事务管理器

1. JDBC -> 使用 Connection 对象的 commit()、rollback()、close() 管理事务

2.MANAGED -> 交给容器管理,现在为原生Mybatis,故无事务

3.spirng+mybatis -> 一般在applicationContext.xml 里面配置数据源,覆盖 MyBatis 的配置,交给 spring管理

(8)、dataSource

数据源

spirng+mybatis,事务与数据源交给 spring管理

(9)、mappers

<mappers>标签配置的是我们的映射器,也就是 Mapper.xml 的路径。

这里配置的 目的是让 MyBatis 在启动的时候去扫描这些映射器,创建映射关系。

我们有四种指定 Mapper 文件的方式:

1、使用相对路径的资源引用(resource)

2、使用完全限定资源定位符(绝对路径)(URL)

3、使用映射器接口实现类的完全限定类名

4、将包内的映射器接口实现全部注册为映射器(最常用)

总结:

配置名称

配置含义

配置简介

configuration

包裹所有配置标签

整个配置文件的顶级标签

properties

 

属性

 

该标签可以引入外部配置的属性,也可以自己配置。该配置标签所在的同一个配置文件的其他配置均可以引用此配置中的属性

setting

 

全局配置参数

 

用来配置一些改变运行时行为的信息,例如是否使用缓存机制,是否使用延迟加载,是否使用错误处理机制等。

typeAliases

 

类型别名

 

用来设置一些别名来代替 Java 的长类型声明(如java.lang.int变为 int),减少配置编码的冗余

typeHandlers

 

类型处理器

 

将数据库获取的值以合适的方式转换为 Java 类型,或者将 Java类型的参数转换为数据库对应的类型

objectFactory

对象工厂

实例化目标类的工厂类配置

 

plugins

 

插件

 

可以通过插件修改 MyBatis 的核心行为,例如对语句执行的某一点进行拦截调用

 

environments

 

环境集合属性对象

 

数据库环境信息的集合。在一个配置文件中,可以有多种数据库环境集合,这样可以使 MyBatis 将 SQL 同时映射至多个数据库

 

environment

 

环境子属性对象

 

数据库环境配置的详细配置

 

transactionManager

 

事务管理

 

指定 MyBatis 的事务管理器

 

dataSource

 

数据源

 

使用其中的 type 指定数据源的连接类型,在标签对中可以使用property 属性指定数据库连接池的其他信息

mappers

 

映射器

 

配置 SQL 映射文件的位置,告知 MyBatis 去哪里加载 SQL映射文件

 

      此篇文章到这里就暂时告一段落,相信大家都能满载而归 ...

      ps: mybatis源码分析还在学习的路上,后续博文将持续更新 mybatis源码剖析,缓存机制,执行流程....

      最后,都看到这里了,顺手点个赞,加个收藏吧 ,加个关注那就更好了 ~  

                   — 猿医生.yys

      差点忘记件比写博客还要重要的事情 ~

      这里有 博哥(梁博)的粉丝么, 听说今年有博哥的演唱会, 私信我,约起哈 ~  ❤

猜你喜欢

转载自blog.csdn.net/qq_42175986/article/details/107669618