Mybaits的入门:提高持久层数据处理效率

34 阅读11分钟

一..持久层框架对比 JDBC

  • SQL 夹杂在Java代码中耦合度高,导致硬编码内伤
  • 维护不易且实际开发需求中 SQL 有变化,频繁修改的情况多见
  • 代码冗长,开发效率低

Hibernate 和 JPA

  • 操作简便,开发效率高
  • 程序中的长难复杂 SQL 需要绕过框架
  • 内部自动生成的 SQL,不容易做特殊优化
  • 基于全映射的全自动框架,大量字段的 POJO 进行部分映射时比较困难。
  • 反射操作太多,导致数据库性能下降

Mybaits

  • 轻量级,性能出色
  • SQL 和 Java 编码分开,功能边界清晰。Java代码专注业务、SQL语句专注数据
  • 开发效率稍逊于 Hibernate,但是完全能够接收

开发效率:Hibernate>Mybatis>JDBC

运行效率:JDBC>Mybatis>Hibernate

2.准备Mapper接口和MapperXML文件 MyBatis 框架下,SQL语句编写位置发生改变,从原来的Java类,改成XML或者注解定义!

推荐在XML文件中编写SQL语句,让用户能更专注于 SQL 代码,不用关注其他的JDBC代码。

如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码!!

一般编写SQL语句的文件命名:XxxMapper.xml Xxx一般取表名!!

说明:

  • SqlSession:代表Java程序和数据库之间的会话。(HttpSession是Java程序和浏览器之间的会话)

  • SqlSessionFactory:是“生产”SqlSession的“工厂”。

  • 工厂模式:如果创建某一个对象,使用的过程基本固定,那么我们就可以把创建这个对象的相关代码封装到一个“工厂类”中,以后都使用这个工厂类来“生产”我们需要的对象。

  • HttpSession:工作在Web服务器上,属于表述层。

    • 代表浏览器和Web服务器之间的会话。
  • SqlSession:不依赖Web服务器,属于持久化层。

    • 代表Java程序和数据库之间的会话。可以吗

a0232b543ada0ff5a88d15b02d4f2b54.png

二.MyBatis基本使用

1.向SQL语句传参 mybatis日志输出配置

mybatis配置文件设计标签和顶层结构如下:

  • configuration(配置)
    • [properties(属性)]
    • [settings(设置)]
    • [typeAliases(类型别名)]
    • [typeHandlers(类型处理器)]
    • [objectFactory(对象工厂)]
    • [plugins(插件)]
    • [environments(环境配置)]
      • environment(环境变量)
        • transactionManager(事务管理器)
        • dataSource(数据源)
    • [databaseIdProvider(数据库厂商标识)]
    • [mappers(映射器)]

2.数据输入

数据输出总体上有两种形式:

  • 增删改操作返回的受影响行数:直接使用 int 或 long 类型接收即可
  • 查询操作的查询结果

我们需要做的是,指定查询的输出数据类型即可!

并且插入场景下,实现主键数据回显示!

日志配置:

```

1.2

  Mybatis会将SQL语句中的#{}转换为问号占位符。

1.3

  ${}形式传参,底层Mybatis做的是字符串拼接操作。



  通常不会采用${}的方式传值。一个特定的适用场景是:通过Java程序动态生成数据库表,表名部分需要Java程序通过参数传入;而JDBC对于表名部分是不能使用问号占位符的,此时只能使用

  结论:实际开发中,能用#{}实现的,肯定不用${}。

  特殊情况: 动态的不是值,是列名或者关键字,需要使用${}拼接

2 数据输入

1 Mybatis总体机制概括

  ![](https://secure2.wostatic.cn/static/cVsqhzY9aD6CpCtijq92pu/img005.png)

2 概念说明

  这里数据输入具体是指上层方法(例如Service方法)调用Mapper接口时,数据传入的形式。

  - 简单类型:只包含一个值的数据类型
      - 基本数据类型:int、byte、short、double、……
      - 基本数据类型的包装类型:Integer、Character、Double、……
      - 字符串类型:String
  - 复杂类型:包含多个值的数据类型
      - 实体类类型:Employee、Department、……
      - 集合类型:List、Set、Map、……
      - 数组类型:int[]、String[]、……
      - 复合类型:List<Employee>、实体类中包含集合……
select emp_id empId,emp_name empName,emp_salary empSalary from t_emp where emp_id=#{empId} ```
  > 单个简单类型参数,在#{}中可以随意命名,但是没有必要。通常还是使用和接口方法参数同名。

#### 2.2.4 **实体类类型参数**

  Mapper接口中抽象方法的声明
int insertEmployee(Employee employee);
  SQL语句
<insert id="insertEmployee">
  insert into t_emp(emp_name,emp_salary) values(#{empName},#{empSalary})

SQL语句

```XML
<update id="updateEmployee">
  update t_emp set emp_salary=#{empSalary} where emp_id=#{empId}
</update>

对应关系

  ![](https://secure2.wostatic.cn/static/dgY93pHWwQsVeYvQCmdPoB/img006.png)

#### 2.2.6 **Map类型参数**

  Mapper接口中抽象方法的声明

SQL语句

对应关系

  #{}中写Map中的key

  **使用场景**

  有很多零散的参数需要传递,但是没有对应的实体类类型可以使用。使用@Param注解一个一个传入又太麻烦了。所以都封装到Map中。

2.3数据输出

2.3.1 输出概述

  数据输出总体上有两种形式:

  - 增删改操作返回的受影响行数:直接使用 intlong 类型接收即可
  - 查询操作的查询结果

  我们需要做的是,指定查询的输出数据类型即可!

  并且插入场景下,实现主键数据回显示!

2.3.2 单个简单类型 Mybatis 内部给常用的数据类型设定了很多别名。 以 int 类型为例,可以写的名称有:int、integer、Integer、java.lang.Integer、Int、INT、INTEGER 等等。

  junit测试

  **细节解释:**

    select标签,通过resultType指定查询返回值类型!

    resultType = "全限定符 | 别名 | 如果是返回集合类型,写范型类型即可"

  别名问题:

    [https://mybatis.org/mybatis-3/zh/configuration.html#typeAliases](https://mybatis.org/mybatis-3/zh/configuration.html#typeAliases)
<typeAliases> <package name="domain.blog"/> </typeAliases>
    每一个在包 `domain.blog` 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 `domain.blog.Author` 的别名为 `author`;若有注解,则别名为其注解值。见下面的例子:

      SQL语句

```XML
<!-- 编写具体的SQL语句,使用id属性唯一的标记一条SQL语句 -->
<!-- resultType属性:指定封装查询结果的Java实体类的全类名 -->
<select id="selectEmployee" resultType="com.atguigu.mybatis.entity.Employee">

  <!-- Mybatis负责把SQL语句中的#{}部分替换成“?”占位符 -->
  <!-- 给每一个字段设置一个别名,让别名和Java实体类中属性名一致 -->
  select emp_id empId,emp_name empName,emp_salary empSalary from t_emp where emp_id=#{```

通过给数据库表字段加别名,让查询结果的每一列都和Java实体类中属性对应起来。
 增加全局配置自动识别对应关系

      在 Mybatis 全局配置文件中,做了下面的配置,select语句中可以不给字段设置别名

```XML
<!-- 在全局范围内对Mybatis进行配置 -->
<settings>

  <!-- 具体配置 -->
  <!-- 从org.apache.ibatis.session.Configuration类中可以查看能使用的配置项 -->
  <!-- 将mapUnderscoreToCamelCase属性配置为true,表示开启自动映射驼峰式命名规则 -->
  <!-- 规则要求数据库表字段命名方式:单词_单词 -->
  <!-- 规则要求Java实体类属性名命名方式:首字母小写的驼峰式命名 -->
  <setting name="mapUnderscoreToCamelCase" value="true"/>



    #### 2.3.5 返回List类型

      查询结果返回多个实体类对象,希望把多个实体类对象放在List集合中返回。此时不需要任何特殊处理,在resultType属性中还是设置实体类类型即可。

      Mapper接口中抽象方法

```Java
List<Employee> selectAll();
  SQL语句

          注意

          Mybatis是将自增主键的值设置到实体类对象中,而不是以Mapper接口方法返回值的形式返回。
      2. **非自增长类型主键**

          而对于不支持自增型主键的数据库(例如 Oracle)或者字符串类型主键,则可以使用 selectKey 子元素:selectKey 元素将会首先运行,id 会被设置,然后插入语句会被调用!

          使用 `selectKey` 帮助插入UUID作为字符串类型主键示例:

```XML
<insert id="insertUser" parameterType="User">
    <selectKey keyProperty="id" resultType="java.lang.String"
        order="BEFORE">
        SELECT UUID() as id
    </selectKey>
    INSERT INTO user (id, username, password) 
    VALUES (
        #{id},
        #{username},
        #{password}
    )
</insert>

      在上例中,我们定义了一个 `insertUser` 的插入语句来将 `User` 对象插入到 `user` 表中。我们使用 `selectKey` 来查询 UUID 并设置到 `id` 字段中。

      通过 `keyProperty` 属性来指定查询到的 UUID 赋值给对象中的 `id` 属性,而 `resultType` 属性指定了 UUID 的类型为 `java.lang.String`。

      需要注意的是,我们将 `selectKey` 放在了插入语句的前面,这是因为 MySQL 在 `insert` 语句中只支持一个 `select` 子句,而 `selectKey` 中查询 UUID 的语句就是一个 `select` 子句,因此我们需要将其放在前面。

      最后,在将 `User` 对象插入到 `user` 表中时,我们直接使用对象中的 `id` 属性来插入主键值。

      使用这种方式,我们可以方便地插入 UUID 作为字符串类型主键。当然,还有其他插入方式可以使用,如使用Java代码生成UUID并在类中显式设置值等。需要根据具体应用场景和需求选择合适的插入方式。

#### 2.3.7 实体类属性和数据库字段对应关系
  1. 别名对应

      将字段的别名设置成和实体类属性一致。
<!-- 编写具体的SQL语句,使用id属性唯一的标记一条SQL语句 -->
<!-- resultType属性:指定封装查询结果的Java实体类的全类名 -->
<select id="selectEmployee" resultType="com.atguigu.mybatis.entity.Employee">

  <!-- Mybatis负责把SQL语句中的#{}部分替换成“?”占位符 -->
  <!-- 给每一个字段设置一个别名,让别名和Java实体类中属性名一致 -->
  select emp_id empId,emp_name empName,emp_salary empSalary from t_emp where emp_id=#{maomi}

</select>
      > 关于实体类属性的约定:

getXxx()方法、setXxx()方法把方法名中的get或set去掉,首字母小写。 2. 全局配置自动识别驼峰式命名规则

      在Mybatis全局配置文件加入如下配置:
<!-- 使用settings对Mybatis全局进行设置 -->
<settings>

  <!-- 将xxx_xxx这样的列名自动映射到xxXxx这样驼峰式命名的属性名 -->
  <setting name="mapUnderscoreToCamelCase" value="true"/>

</settings>
      SQL语句中可以不使用别名
<!-- Employee selectEmployee(Integer empId); -->
<select id="selectEmployee" resultType="com.atguigu.mybatis.entity.Employee">

  select emp_id,emp_name,emp_salary from t_emp where emp_id=#{empId}

</select>
  3. 使用resultMap

      使用resultMap标签定义对应关系,再在后面的SQL语句中引用这个对应关系
<!-- 专门声明一个resultMap设定column到property之间的对应关系 -->
<resultMap id="selectEmployeeByRMResultMap" type="com.atguigu.mybatis.entity.Employee">

  <!-- 使用id标签设置主键列和主键属性之间的对应关系 -->
  <!-- column属性用于指定字段名;property属性用于指定Java实体类属性名 -->
  <id column="emp_id" property="empId"/>

  <!-- 使用result标签设置普通字段和Java实体类属性之间的关系 -->


  ### 2.4 CRUD强化练习
    1. 准备数据库数据

        首先,我们需要准备一张名为 `user` 的表。该表包含字段 id(主键)、username、password。创建SQL如下:

```SQL
CREATE TABLE `user` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `username` VARCHAR(50) NOT NULL,
  `password` VARCHAR(50) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

2. 实体类准备

    接下来,我们需要定义一个实体类 `User`,来对应 user 表的一行数据。


3. Mapper接口定义

    定义一个 Mapper 接口 `UserMapper`,并在其中添加 user 表的增、删、改、查方法。


4. MapperXML编写

    在 resources /mappers目录下创建一个名为 `UserMapper.xml` 的 XML 文件,包含与 Mapper 接口中相同的五个 SQL 语句,并在其中,将查询结果映射到 `User` 实体中。

5. MyBatis配置文件

    位置:resources: mybatis-config.xml 

<mappers>
    <!-- Mapper注册:指定Mybatis映射文件的具体位置 -->
    <!-- mapper标签:配置一个具体的Mapper映射文件 -->
    <!-- resource属性:指定Mapper映射文件的实际存储位置,这里需要使用一个以类路径根目录为基准的相对路径 -->
    <!--    对Maven工程的目录结构来说,resources目录下的内容会直接放入类路径,所以这里我们可以以resources目录为基准 -->
    <mapper resource="mappers/UserMapper.xml"/>
</mappers>
```

  ### 2.5 mapperXML标签总结

    MyBatis 的真正强大在于它的语句映射,这是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 致力于减少使用成本,让用户能更专注于 SQL 代码。

    SQL 映射文件只有很少的几个顶级元素(按照应被定义的顺序列出):

    - `insert` – 映射插入语句。
    - `update` – 映射更新语句。
    - `delete` – 映射删除语句。
    - `select` – 映射查询语句。

    **select标签:**

      MyBatis 在查询和结果映射做了相当多的改进。一个简单查询的 select 元素是非常简单:

```XML
<select id="selectPerson" 
resultType="hashmap" resultMap="自定义结构"> SELECT * FROM PERSON WHERE ID = #{id} </select>
  这个语句名为 selectPerson,接受一个 int(或 Integer)类型的参数,并返回一个 HashMap 类型的对象,其中的键是列名,值便是结果行中的对应值。

  注意参数符号:#{id}  ${key}

  MyBatis 创建一个预处理语句(PreparedStatement)参数,在 JDBC 中,这样的一个参数在 SQL 中会由一个“?”来标识,并被传递到一个新的预处理语句中,就像这样:
// 近似的 JDBC 代码,非 MyBatis 代码...
String selectPerson = "SELECT * FROM PERSON WHERE ID=?";
PreparedStatement ps = conn.prepareStatement(selectPerson);
ps.setInt(1,id);
  select 元素允许你配置很多属性来配置每条语句的行为细节:
属性描述
id在命名空间中唯一的标识符,可以被用来引用这条语句。
resultType期望从这条语句中返回结果的类全限定名或别名。 注意,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和 resultMap 之间只能同时使用一个。
resultMap对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultType 和 resultMap 之间只能同时使用一个。
timeout这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。
statementType可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
属性描述
id在命名空间中唯一的标识符,可以被用来引用这条语句。
timeout这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。
statementType可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
useGeneratedKeys(仅适用于 insert 和 update)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false。
keyProperty(仅适用于 insert 和 update)指定能够唯一识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,默认值:未设置(unset)。如果生成列不止一个,可以用逗号分隔多个属性名称。
keyColumn(仅适用于 insert 和 update)设置生成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第一列的时候,是必须设置的。如果生成列不止一个,可以用逗号分隔多个属性名称。

三、MyBatis多表映射

3.1 多表映射概念

  1. 多表查询结果映射思路

    上面课程中,我全面讲解了单表的mybatis操作!但是开发中更多的是**多表查询**需求,这种情况我们如何让进行处理?
    
    MyBatis 思想是:数据库不可能永远是你所想或所需的那个样子。 我们希望每个数据库都具备良好的第三范式或 BCNF 范式,可惜它们并不都是那样。 如果能有一种数据库映射模式,完美适配所有的应用程序查询需求,那就太好了,而 ResultMap 就是 MyBatis 就是完美答案。 
    
    官方例子:我们如何映射下面这个语句? 
    

    6. 关键词

    在“对多”关联关系中,同样有很多配置,但是提炼出来最关键的就是:“collection”和“ofType”
    

3.4 多表映射总结

#### 3.4.1 多表映射优化
setting属性属性含义可选值默认值
autoMappingBehavior指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射;PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集(无论是否嵌套)。NONE, PARTIAL, FULLPARTIAL
#### 3.4.2 多表映射总结
关联关系配置项关键词所在配置文件和具体位置
对一association标签/javaType属性/property属性Mapper配置文件中的resultMap标签内
对多collection标签/ofType属性/property属性Mapper配置文件中的resultMap标签内

四、MyBatis动态语句

4.1 动态语句需求和简介

经常遇到很多按照很多查询条件进行查询的情况,比如智联招聘的职位搜索等。其中经常出现很多条件不取值的情况,在后台应该如何完成最终的SQL语句呢?

![](https://secure2.wostatic.cn/static/aFpU3YtV2MDrwZof2q4gaK/image.png)

动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。

使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。

如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。

4.2 if和where标签

使用动态 SQL 最常见情景是根据条件包含 where  / if 子句的一部分。比如:


<where>
    <!-- 使用if标签,让我们可以有选择的加入SQL语句的片段。这个SQL语句片段是否要加入整个SQL语句,就看if标签判断的结果是否为true -->
    <!-- 在if标签的test属性中,可以访问实体类的属性,不可以访问数据库表的字段 -->
    <if test="empName != null">
        <!-- 在if标签内部,需要访问接口的参数时还是正常写#{} -->
        or emp_name=#{empName}
    </if>
    <if test="empSalary &gt; 2000">
        or emp_salary>#{empSalary}
    </if>
    <!--
     第一种情况:所有条件都满足 WHERE emp_name=? or emp_salary>?
     第二种情况:部分条件满足 WHERE emp_salary>?
     第三种情况:所有条件都不满足 没有where子句
     -->
</where>
```

4.3 set标签

where emp_id=#{empId}
<!--
     第一种情况:所有条件都满足 SET emp_name=?, emp_salary=?
     第二种情况:部分条件满足 SET emp_salary=?
     第三种情况:所有条件都不满足 update t_emp where emp_id=?
        没有set子句的update语句会导致SQL语法错误
 -->
```

4.4 trim标签(了解)

使用trim标签控制条件部分两端是否包含某些字符

- prefix属性:指定要动态添加的前缀
- suffix属性:指定要动态添加的后缀
- prefixOverrides属性:指定要动态去掉的前缀,使用“|”分隔有可能的多个值
- suffixOverrides属性:指定要动态去掉的后缀,使用“|”分隔有可能的多个值


<!-- prefix属性指定要动态添加的前缀 -->
<!-- suffix属性指定要动态添加的后缀 -->
<!-- prefixOverrides属性指定要动态去掉的前缀,使用“|”分隔有可能的多个值 -->
<!-- suffixOverrides属性指定要动态去掉的后缀,使用“|”分隔有可能的多个值 -->
<!-- 当前例子用where标签实现更简洁,但是trim标签更灵活,可以用在任何有需要的地方 -->
<trim prefix="where" suffixOverrides="and|or">
    <if test="empName != null">
        emp_name=#{empName} and
    </if>
    <if test="empSalary &gt; 3000">
        emp_salary>#{empSalary} and
    </if>
    <if test="empAge &lt;= 20">
        emp_age=#{empAge} or
    </if>
    <if test="empGender=='male'">
        emp_gender=#{empGender}
    </if>
</trim>
```

4.5 choose/when/otherwise标签

在多个分支条件中,仅执行一个。

- 从上到下依次执行条件判断
- 遇到的第一个满足条件的分支会被采纳
- 被采纳分支后面的分支都将不被考虑
- 如果所有的when分支都不满足,那么就执行otherwise分支

4.6 foreach标签

**基本用法**

用批量插入举例
<!--
    collection属性:要遍历的集合
    item属性:遍历集合的过程中能得到每一个具体对象,在item属性中设置一个名字,将来通过这个名字引用遍历出来的对象
    separator属性:指定当foreach标签的标签体重复拼接字符串时,各个标签体字符串之间的分隔符
    open属性:指定整个循环把字符串拼好后,字符串整体的前面要添加的字符串
    close属性:指定整个循环把字符串拼好后,字符串整体的后面要添加的字符串
    index属性:这里起一个名字,便于后面引用
        遍历List集合,这里能够得到List集合的索引值
        遍历Map集合,这里能够得到Map集合的key
 -->
<foreach collection="empList" item="emp" separator="," open="values" index="myIndex">
    <!-- 在foreach标签内部如果需要引用遍历得到的具体的一个对象,需要使用item属性声明的名称 -->
    (#{emp.empName},#{myIndex},#{emp.empSalary},#{emp.empGender})
</foreach>
**批量更新时需要注意**

上面批量插入的例子本质上是一条SQL语句,而实现批量更新则需要多条SQL语句拼起来,用分号分开。也就是一次性发送多条SQL语句让数据库执行。此时需要在数据库连接信息的URL地址中设置:
atguigu.dev.url=jdbc:mysql:///mybatis-example?allowMultiQueries=true
对应的foreach标签如下:
<!-- int updateEmployeeBatch(@Param("empList") List<Employee> empList) -->
<update id="updateEmployeeBatch">
    <foreach collection="empList" item="emp" separator=";">
        update t_emp set emp_name=#{emp.empName} where emp_id=#{emp.empId}
    </foreach>
</update>
**关于foreach标签的collection属性**

如果没有给接口中List类型的参数使用@Param注解指定一个具体的名字,那么在collection属性中默认可以使用collection或list来引用这个list集合。这一点可以通过异常信息看出来:
Parameter 'empList' not found. Available parameters are [arg0, collection, list]
在实际开发中,为了避免隐晦的表达造成一定的误会,建议使用@Param注解明确声明变量的名称,然后在foreach标签的collection属性中按照@Param注解指定的名称来引用传入的参数。

4.7 sql片段

**抽取重复的SQL片段**
<!-- 使用sql标签抽取重复出现的SQL片段 -->
<sql id="mySelectSql">
    select emp_id,emp_name,emp_age,emp_salary,emp_gender from t_emp
</sql>
引用已抽取的SQL片段
<!-- 使用include标签引用声明的SQL片段 -->
<include refid="mySelectSql"/>

五、MyBatis高级扩展

5.1 Mapper批量映射优化

1. 需求

    Mapper 配置文件很多时,在全局配置文件中一个一个注册太麻烦,希望有一个办法能够一劳永逸。
2. 配置方式

    Mybatis 允许在指定 Mapper 映射文件时,只指定其所在的包:
<mappers>
    <package name="com.atguigu.mapper"/>
</mappers>
    此时这个包下的所有 Mapper 配置文件将被自动加载、注册,比较方便。
3. 资源创建要求
- Mapper 接口和 Mapper 配置文件名称一致
    - Mapper 接口:EmployeeMapper.java
    - Mapper 配置文件:EmployeeMapper.xml
- Mapper 配置文件放在 Mapper 接口所在的包内
    - 可以将mapperxml文件放在mapper接口所在的包!
    - 可以在sources下创建mapper接口包一致的文件夹结构存放mapperxml文件

        ![](https://secure2.wostatic.cn/static/rxqwWnS66k6f3ZXGBU3Keb/image.png)

        ![](https://secure2.wostatic.cn/static/sAqWwce8qsUKcoddYAqMws/image.png)

5.2 插件和分页插件PageHelper

#### 5.2.1 插件机制和PageHelper插件介绍

  MyBatis 对插件进行了标准化的设计,并提供了一套可扩展的插件机制。插件可以在用于语句执行过程中进行拦截,并允许通过自定义处理程序来拦截和修改 SQL 语句、映射语句的结果等。

  具体来说,MyBatis 的插件机制包括以下三个组件:

  1. `Interceptor`(拦截器):定义一个拦截方法 `intercept`,该方法在执行 SQL 语句、执行查询、查询结果的映射时会被调用。
  2. `Invocation`(调用):实际上是对被拦截的方法的封装,封装了 `Object target``Method method``Object[] args` 这三个字段。
  3. `InterceptorChain`(拦截器链):对所有的拦截器进行管理,包括将所有的 Interceptor 链接成一条链,并在执行 SQL 语句时按顺序调用。

  插件的开发非常简单,只需要实现 Interceptor 接口,并使用注解 `@Intercepts` 来标注需要拦截的对象和方法,然后在 MyBatis 的配置文件中添加插件即可。

  PageHelperMyBatis 中比较著名的分页插件,它提供了多种分页方式(例如 MySQL 和 Oracle 分页方式),支持多种数据库,并且使用非常简单。下面就介绍一下 PageHelper 的使用方式。

  https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md#如何配置数据库方言

#### 5.2.2 PageHelper插件使用
  1. pom.xml引入依赖
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.1.11</version>
</dependency>

  2. mybatis-config.xml配置分页插件

      在 MyBatis 的配置文件中添加 PageHelper 的插件:
<plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
        <property name="helperDialect" value="mysql"/>
    </plugin>
</plugins>

      其中,`com.github.pagehelper.PageInterceptor` 是 PageHelper 插件的名称,`dialect` 属性用于指定数据库类型(支持多种数据库)
  3. 页插件使用

      在查询方法中使用分页:

5.3 逆向工程和MybatisX插件

#### 5.3.1 ORM思维介绍

  ORM(Object-Relational Mapping,对象-关系映射)是一种将数据库和面向对象编程语言中的对象之间进行转换的技术。它将对象和关系数据库的概念进行映射,最后我们就可以通过方法调用进行数据库操作!!

  最终: **让我们可以使用面向对象思维进行数据库操作!!!**

  **ORM 框架通常有半自动和全自动两种方式。**

  - 半自动 ORM 通常需要程序员手动编写 SQL 语句或者配置文件,将实体类和数据表进行映射,还需要手动将查询的结果集转换成实体对象。
  - 全自动 ORM 则是将实体类和数据表进行自动映射,使用 API 进行数据库操作时,ORM 框架会自动执行 SQL 语句并将查询结果转换成实体对象,程序员无需再手动编写 SQL 语句和转换代码。

下面是半自动和全自动 ORM 框架的区别

  1. 映射方式:半自动 ORM 框架需要程序员手动指定实体类和数据表之间的映射关系,通常使用 XML 文件或注解方式来指定;全自动 ORM 框架则可以自动进行实体类和数据表的映射,无需手动干预。
  2. 查询方式:半自动 ORM 框架通常需要程序员手动编写 SQL 语句并将查询结果集转换成实体对象;全自动 ORM 框架可以自动组装 SQL 语句、执行查询操作,并将查询结果转换成实体对象。
  3. 性能:由于半自动 ORM 框架需要手动编写 SQL 语句,因此程序员必须对 SQL 语句和数据库的底层知识有一定的了解,才能编写高效的 SQL 语句;而全自动 ORM 框架通过自动优化生成的 SQL 语句来提高性能,程序员无需进行优化。
  4. 学习成本:半自动 ORM 框架需要程序员手动编写 SQL 语句和映射配置,要求程序员具备较高的数据库和 SQL 知识;全自动 ORM 框架可以自动生成 SQL 语句和映射配置,程序员无需了解过多的数据库和 SQL 知识。

  常见的半自动 ORM 框架包括 MyBatis 等;常见的全自动 ORM 框架包括 Hibernate、Spring Data JPA、MyBatis-Plus 等。

3.2 逆向工程

      MyBatis 的逆向工程是一种自动化生成持久层代码和映射文件的工具,它可以根据数据库表结构和设置的参数生成对应的实体类、Mapper.xml 文件、Mapper 接口等代码文件,简化了开发者手动生成的过程。逆向工程使开发者可以快速地构建起 DAO 层,并快速上手进行业务开发。

MyBatis 的逆向工程有两种方式:通过 MyBatis Generator 插件实现和通过 Maven 插件实现。无论是哪种方式,逆向工程一般需要指定一些配置参数,例如数据库连接 URL、用户名、密码、要生成的表名、生成的文件路径等等。 总的来说,MyBatis 的逆向工程为程序员提供了一种方便快捷的方式,能够快速地生成持久层代码和映射文件,是半自动 ORM 思维像全自动发展的过程,提高程序员的开发效率。

  **注意:逆向工程只能生成单表crud的操作,多表查询依然需要我们自己编写!**

#### 5.3.3 逆向工程插件MyBatisX使用

     MyBatisX 是一个 MyBatis 的代码生成插件,可以通过简单的配置和操作快速生成 MyBatis Mapper、pojo 类和 Mapper.xml 文件。下面是使用 MyBatisX 插件实现逆向工程的步骤:
  1. 安装插件:

     在 IntelliJ IDEA 中打开插件市场,搜索 MyBatisX 并安装。
    
  2. 使用 IntelliJ IDEA连接数据库 - 连接数据库

          ![](https://secure2.wostatic.cn/static/suwzNwsHMPdWwco8fMRTai/image.png)
      - 填写信息
    
          ![](https://secure2.wostatic.cn/static/qEua8K6S4s7wFd9YZ4Xbcm/image.png)
      - 展示库表
    
          ![](https://secure2.wostatic.cn/static/kUoGo2UYu5JNLf3BnxZWXL/image.png)
      - 逆向工程使用
    
          ![](https://secure2.wostatic.cn/static/s6LMYQtxCLAQ86ScJS5Kam/image.png)
    
          ![](https://secure2.wostatic.cn/static/nt2dnyzG5K9NkQBpb1BJ2E/image.png)
    
          ![](https://secure2.wostatic.cn/static/5x6sMk4hWDScuAifL2HuJX/image.png)
    
  3. 查看生成结果

       ![](https://secure2.wostatic.cn/static/59Kv1MuiUawH6qVWQSV1Z1/image.png)
    
  4. 逆向工程案例使用

       正常使用即可,自动生成单表的crud方法!
    

六、MyBatis总结

核心点掌握目标
mybatis基础使用流程, 参数输入,#{} ${},参数输出
mybatis多表实体类设计,resultMap多表结果映射
mybatis动态语句Mybatis动态语句概念, where , if , foreach标签
mybatis扩展Mapper批量处理,分页插件,逆向工程