Mybatis相关知识点【详细版】

文章目录

Mybatis 简介

简介

MyBatis 是一款优秀的持久层框架。

快速入门(基于 Mybatis3 方式)

  1. 准备数据模型

    mysql 复制代码
    CREATE DATABASE `mybatis-example`;
    
    USE `mybatis-example`;
    
    CREATE TABLE `t_emp`(
      emp_id INT AUTO_INCREMENT,
      emp_name CHAR(100),
      emp_salary DOUBLE(10,5),
      PRIMARY KEY(emp_id)
    );
    
    INSERT INTO `t_emp`(emp_name,emp_salary) VALUES("tom",200.33);
    INSERT INTO `t_emp`(emp_name,emp_salary) VALUES("jerry",666.66);
    INSERT INTO `t_emp`(emp_name,emp_salary) VALUES("andy",777.77);
  2. 项目搭建和准备

    • 依赖导入

      pom.xml

      maven 复制代码
      <dependencies>
        <!-- mybatis依赖 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.11</version>
        </dependency>
      
        <!-- MySQL驱动 mybatis底层依赖jdbc驱动实现,本次不需要导入连接池,mybatis自带! -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.25</version>
        </dependency>
      
        <!--junit5测试-->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.3.1</version>
        </dependency>
      </dependencies>
    • 实体类准备

      java 复制代码
      public class Employee {
      
          private Integer empId;
      
          private String empName;
      
          private Double empSalary;
          
          //getter | setter
      }
  3. 准备 Mapper 接口和 MapperXML 文件

    MyBatis 框架下,SQL 语句编写位置发生改变,从原来的 java类,改为 XML 或者注解定义!推荐在 XML 文件中编写 SQL 语句,让用户更专注于 SQL 代码,不用关注其他的 JDBC 代码

    MyBatis 中的 Mapper 接口相当于以前的 Dao。但是区别在于,Mapper 仅仅只是建接口就可以了。不需要提供实现类。具体的SQL 写到对应的 MapperXML文件中。

    • 定义 mapper 接口

      包:com.jxnu.mapper

      java 复制代码
      package com.jxnu.mapper
      
      public interface EmployeeMapper{
      	Employee selectEmployee(Integer empId);
      }
    • 定义 mapper.xml

      位置:resources/mappers/EmployeeMapper.xml

      maven 复制代码
      <?xml version="1.0" encoding="UTF-8" ?>
      <!DOCTYPE mapper
              PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
              "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
      
      <!--namespace 等于mapper 接口类的全限定名-->
      <mapper namespace="com.jxnu.mapper.EmployeeMapper">
      	<!-- 查询使用 select 标签
      		id = 方法名
      		resultType = 返回值类型
      		标签内编写SQL语句
      	-->
      	<select id="selectEmployee" resultType="com.jxnu.Enity.Employee">
      		select emp_id empId,emp_name empName,emp_salary empSalary from t_emp where emp_id=#{empId}
      	</select>
      </mapper>

      在mapper 标签中 可以用crud: select delete insert update 等标签

  4. 准备 MyBatis 配置文件

    mybatis 框架配置文件:数据库连接,mapper.xml配置等等

    习惯上命名 mybatis-config.xml,在整合Spring 之后,这个配置文件可以省略

    java 复制代码
    <?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表示配置Mybatis的开发环境,可以配置多个环境,在众多具体环境中,使用default属性指定实际运行时使用的环境。default属性的取值是environment标签的id属性的值。 -->
      <environments default="development">
        <!-- environment表示配置Mybatis的一个具体的环境 -->
        <environment id="development">
          <!-- Mybatis的内置的事务管理器 -->
          <transactionManager type="JDBC"/>
          <!-- 配置数据源 -->
          <dataSource type="POOLED">
            <!-- 建立数据库连接的具体信息 -->
            <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/mybatis-example"/>
            <property name="username" value="root"/>
            <property name="password" value="root"/>
          </dataSource>
        </environment>
      </environments>
    
      <mappers>
        <!-- Mapper注册:指定Mybatis映射文件的具体位置 -->
        <!-- mapper标签:配置一个具体的Mapper映射文件 -->
        <!-- resource属性:指定Mapper映射文件的实际存储位置,这里需要使用一个以类路径根目录为基准的相对路径 -->
        <!--    对Maven工程的目录结构来说,resources目录下的内容会直接放入类路径,所以这里我们可以以resources目录为基准 -->
        <mapper resource="mappers/EmployeeMapper.xml"/>
      </mappers>
    
    </configuration>
  5. 运行和测试

    java 复制代码
    @Test
    public void test throws Expection{
    	//以输入流的形式加载MyBatis配置文件
    	InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
    	//创建SqlSessionFactory对象
    	SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    	//使用SqlSessionFactory对象开启一个会话
    	SqlSession sqlSession = sqlSessionFactory.openSession();
    	//JDK动态代理。
    	EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
    	//可以调用代理类的方法了
    	Employee employee = employeeMapper.selectEmployee(1);
    	
    	//关闭SqlSession
    	sqlSession.commit();	//提交事务
    	sqlSession.close();		//关闭会话
    }

MyBatis 基本使用

向 SQL 语句传参

  1. #{} 形式

    Mybatis 会将SQL 语句中的#{} 转换为问号点位符。即" ?"

  2. ${} 形式

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

通常情况下不会采用 KaTeX parse error: Expected 'EOF', got '#' at position 18: ... 的方式传值,实际开发中,能用#̲{} 实现的,不会用{},只有在特殊情况下,动态的不是值,是列名 或者是关键字,需要要 ${} 进行拼接。

java 复制代码
//注解方式传入参数
@Select("select * from user where ${column} = #{value}")
User findByColumn(@Param("column") String column,@Param("value") String value);

数据输入

Mybatis 总体机制概括

单个简单类型参数

在 Mapper 接口中抽象方法的声明

java 复制代码
Employee selectEmployee(Integer empId);

SQL 语句

java 复制代码
<select id ="selectEmployee" resultType="com.jxnu.enity.Employee">
	select emp_id empId,emp_name empName,emp_salary empSalary from t_emp where emp_id = #{empId}
</select>

单个简单类型参数,在#{} 中可以随意命名。但是通常使用和接口方法参数同样的命名方式。

实体类类型参数

Mapper 接口中抽象方法的声明

java 复制代码
int insertEmployee(Employee employee);

SQL 语句

java 复制代码
<insert id="insertEmployee">
	insert into t_emp(emp_name,emp_salary) values(#{empName},#{empSalary})
</insert>

对应关系

结论

Mybatis 会根据#{} 中传入的数据,加工成 getXxx() 方法,通过反射在实体类对象中调用这个方法,从而获取到对应的数据,填充到#{}解析后的占位符这个位置。

零散的简单类型数据

零散的多个简单类型参数,如果没有特殊处理,那么 Mybatis 无法识别自定义名称:

Mapper接口中抽象方法的声明:

java 复制代码
int updateEmployee(@Param("empId") Integer empId,@Param("empSalary") Double empSalary);

SQL 语句

java 复制代码
<update id="updateEmployee">
	update t_emp set emp_salary=#{empSalary} where emp_id=#{empId};
</update>

结论

用@Param标签来规定#{}中的值。

Map 类型参数

Map 类型指的是类似于 python 中的字典类型 ,可以用 put(key,value) get(key) remove(key)等方法来传值,获取值,删除值。

Mapper 接口中抽象方法的声明

java 复制代码
int updateEmployeeByMap(Map<String,Object> paramMap);

SQL 语句

java 复制代码
<update id="updateEmployeeByMap">
	update t_emp set emp_salary=#{empSalaryKey} where emp_id=#{empIdKey}
</update>

结论

在#{}中填写Map对应的key值即可。

数据输出

单个简单类型

Mapper 接口中的抽象方法

java 复制代码
int selectEmpCount();

SQL 语句

java 复制代码
<select id="selectEmpCount" resultType="int">
	select count(*) from t_emp
</select>

Mybatis 内部给常用的数据类型设定了很多别名。例如:double,string,int等等

别名:

可以在mybatis-config.xml中通过配置给实体类起别名,这个别名默认为实体类名字首字母小写,例如Employee改为employee

java 复制代码
<typeAliases> <package name="com.jxnu.enity" /> </typeAliases>

那么在mapper.xml 中resultType="com.jxnu.enity.Employee"就可以改写为别名形式resultType="employee"

总结:

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

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

返回实体类对象

Mapper 接口的抽象方法

java 复制代码
Employee selectEmployee(Integer empId);

SQL 语句

java 复制代码
<select id="selectEmployee" resultType="com.jxnu.enity.Employee">
	select emp_id empId,emp_name empName,emp_salary empSalary from t_emp where emp_id=#{empId}
</select>

通过给数据库表字段加别名,让查询结果的每一列都和 java 实体类中属性对应起来。

增加全局配置自动识别对应关系

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

java 复制代码
<settings>
	<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

可以查看官网查看name和value。

那么SQL 语句可以这么写了

java 复制代码
<select id="selectEmployee" resultType="int">
	select * from t_emp where emp_id=#{empId}
</select>

返回Map 类型

Mapper 接口的抽象方法

java 复制代码
Map<String,Object> selectEmpNameAndMaxSalary();

SQL 语句

java 复制代码
<select id="selectEmpNameAndMaxSalary" resultType="map">
	SELECT 
        emp_name empName, 
        emp_salary empSalary,
        (SELECT AVG(emp_salary) FROM t_emp) avgSalary
    FROM t_emp
    WHERE emp_salary = (SELECT MAX(emp_salary) FROM t_emp)
</select>

返回的map中,key是列名,value是select根据列名得到的值。

返回List 类型

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

Mapper 接口中抽象方法

java 复制代码
List<Employee> selectAll();

SQL 语句

java 复制代码
<select id="selectAll" resultType="com.jxnu.enity.Employee">
	select * from t_emp
</select>

返回主键值

  1. 自增长类型主键

    Mapper 接口中的抽象方法

    java 复制代码
    int insertEmployee(Employee employee);

    SQL 语句

    java 复制代码
    <insert id="insertEmployee" useGeneratedKeys="true" keyProperty="empId" keyColumn="emp_id">
    	insert into t_emp(emp_name,emp_salary) values(#{empName},#{empSalary})
    </insert>

    keyProperty 代表实体属性名,keyColumn 代表数据库表属性名,useGeneratedKeys代表开启:自动将数据库中自增长的主键值放在实体类对应的属性中。

    注意

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

  2. 非自增长类型主键

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

    java 复制代码
    <insert id="insertEmployee">
    	<selectKey order="before" keyProperty="empId" resultType="string" >
    		select replace( uuid() as empId,"-"."")
    	</selectKey>
    	insert into t_emp(emp_id,emp_name,emp_salary) values(#{empId},#{empName},#{empSalary})
    </insert>

    keyProperty 指的是在#{}中的名字取值,resultType指的是名字的类型是什么,order 指的是在插入之前,还是之后做。

实体类属性和数据库字段对应关系

  1. 别名对应

    将字段的别名设置成和实体类属性一致

    java 复制代码
    <select id="selectEmployee" resultType="employee">
    	select emp_id empId,emp_name empName,emp_salary empSalary from t_emp where emp_id=#{empId}
    </select>

    关于实体类属性的约定:

    getXxx()方法、setXxx()方法把方法名中的get 或 set 去掉,首字母小写。

  2. 全局配置自动识别驼峰式命名规则

    在 Mybatis 全局配置文件加入如下配置:

    java 复制代码
    <setting>
    	<setting name="mapUnderscoreToCamelCase" value="true"/>
    </setting>

    SQL 语句中就可以不使用别名了

    java 复制代码
    <select id="selectEmployee" resultType="employee">
    	select * from t_emp where emp_id=#{empId}
    </select>
  3. 使用 resultMap

    使用 resultMap 标签定义对应关系,再在后面的 SQL 语句中引用这个对应关系。

    resultMap 和 resultType 两者二选一。

    java 复制代码
    <resultMap id="selectEmployeeByRMResultMap" type="employee">
    	//id 标签设置主键列和主键属性之间的对应关系
    	<id column="emp_id" property="empId"/>
        //普通列
    	<result column="emp_name" property="empName"/>
    	<result column="emp_salary" property="empSalary"/>
    </resultMap>
    
    <select id="selectEmployeeByRM" resultMap="selectEmployeeByRMResultMap">
    	select emp_id,emp_name,emp_salary from emp_id=#{empId}
    </select>

mapperXML 标签总结

MyBatis 的真正强大在于它的语句映射,映射器的 XML 文件就显得相对简单,节省代码,MyBaits 致力于减少使用成本,让用户能更专注于 SQL 代码

MyBatis 多表映射

多表映射概念

我们的学习目标:

​ 多表查询语句使用

​ 多表结果承接实体类设计

​ 使用 ResultMap 完成多表结果映射

实体类设计方案

实体类设计关系(查询):(单向查看)

  • 对一:

    一个订单和订单对应的客户信息

    实体类设计:对一关系下,类中只要包含单个对方对象类型属性即可!

    java 复制代码
    public class Customer {
    
      private Integer customerId;
      private String customerName;
    
    }
    
    public class Order {
    
      private Integer orderId;
      private String orderName;
      private Customer customer;// 体现的是对一的关系
    
    }  
  • 对多:

    一个客户和客户对应的一系列订单信息。

    实体类设计:对多关系下,类中只要包含对方类型集合属性即可!

    java 复制代码
    public class Customer {
    
      private Integer customerId;
      private String customerName;
      private List<Order> orderList;// 体现的是对多的关系
    }
    
    public class Order {
    
      private Integer orderId;
      private String orderName;
      private Customer customer;// 体现的是对一的关系
      
    }
  • 多表结果实体类设计小技巧

    • 在查询映射的时候只需要关注本次查询相关的属性!例如:查询订单和订单对应的客户时,就不需要关注客户中的订单集合!
    • 无论多少张表联查,实体类设计都是两两考虑!
    • 只有真实发生多表查询时,才需要设计和修改实体类,否则不需要提前设计和修改实体类!

对一映射

数据库:

mysql 复制代码
CREATE TABLE `t_customer` (`customer_id` INT NOT NULL AUTO_INCREMENT, `customer_name` CHAR(100), PRIMARY KEY (`customer_id`) );

CREATE TABLE `t_order` ( `order_id` INT NOT NULL AUTO_INCREMENT, `order_name` CHAR(100), `customer_id` INT, PRIMARY KEY (`order_id`) ); 

INSERT INTO `t_customer` (`customer_name`) VALUES ('c01');

INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o1', '1');
INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o2', '1');
INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o3', '1'); 

实体类设计:

java 复制代码
@Data
public class Customer {

  private Integer customerId;
  private String customerName;
  private List<Order> orderList;// 体现的是对多的关系
  
}  

@Data
public class Order {
  private Integer orderId;
  private String orderName;
  private Customer customer;// 体现的是对一的关系
  
}  

OrderMapper 接口

java 复制代码
public interface OrderMapper {
  Order selectOrderWithCustomer(Integer orderId);
}

OrderMapper.xml 配置文件

java 复制代码
<!-- 创建resultMap实现"对一"关联关系映射 -->
<!-- id属性:通常设置为这个resultMap所服务的那条SQL语句的id加上"ResultMap" -->
<!-- type属性:要设置为这个resultMap所服务的那条SQL语句最终要返回的类型 -->
<resultMap id="selectOrderWithCustomerResultMap" type="order">

  <!-- 先设置Order自身属性和字段的对应关系 -->
  <id column="order_id" property="orderId"/>

  <result column="order_name" property="orderName"/>

  <!-- 使用association标签配置"对一"关联关系 -->
  <!-- property属性:在Order类中对一的一端进行引用时使用的属性名 -->
  <!-- javaType属性:一的一端类的全类名 -->
  <association property="customer" javaType="customer">

    <!-- 配置Customer类的属性和字段名之间的对应关系 -->
    <id column="customer_id" property="customerId"/>
    <result column="customer_name" property="customerName"/>

  </association>

</resultMap>

<!-- Order selectOrderWithCustomer(Integer orderId); -->
<select id="selectOrderWithCustomer" resultMap="selectOrderWithCustomerResultMap">

  SELECT order_id,order_name,c.customer_id,customer_name
  FROM t_order o
  LEFT JOIN t_customer c
  ON o.customer_id=c.customer_id
  WHERE o.order_id=#{orderId}

</select>

对应关系可以参考下图:

关键词

​ 在"对一"关联关系中,我们的配置比较多,但是关键词就只有:association 和 javaType

对多映射

CustomerMapper 接口

java 复制代码
public interface CustomerMapper {

  Customer selectCustomerWithOrderList(Integer customerId);

}

CustomerMapper.xml 文件

java 复制代码
<!-- 配置resultMap实现从Customer到OrderList的"对多"关联关系 -->
<resultMap id="selectCustomerWithOrderListResultMap"

  type="customer">

  <!-- 映射Customer本身的属性 -->
  <id column="customer_id" property="customerId"/>

  <result column="customer_name" property="customerName"/>

  <!-- collection标签:映射"对多"的关联关系 -->
  <!-- property属性:在Customer类中,关联"多"的一端的属性名 -->
  <!-- ofType属性:集合属性中元素的类型 -->
  <collection property="orderList" ofType="order">

    <!-- 映射Order的属性 -->
    <id column="order_id" property="orderId"/>

    <result column="order_name" property="orderName"/>

  </collection>

</resultMap>

<!-- Customer selectCustomerWithOrderList(Integer customerId); -->
<select id="selectCustomerWithOrderList" resultMap="selectCustomerWithOrderListResultMap">
  SELECT c.customer_id,c.customer_name,o.order_id,o.order_name
  FROM t_customer c
  LEFT JOIN t_order o
  ON c.customer_id=o.customer_id
  WHERE c.customer_id=#{customerId}
</select>

对应关系可以参考下图:

关键词

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

多表映射总结

多表映射优化

setting属性 属性含义 可选值 默认值
autoMappingBehavior 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射;PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集(无论是否嵌套)。 NONE, PARTIAL, FULL PARTIAL

我们可以将autoMappingBehavior设置为full,进行多表resultMap映射的时候,可以省略符合列和属性命名映射规则(列名=属性名,或者开启驼峰映射也可以自定映射)的result标签!

修改 teacher-config.xml:

java 复制代码
<!--开启resultMap自动映射 -->
<setting name="autoMappingBehavior" value="FULL"/>

修改 teacherMapper.xml

java 复制代码
<resultMap id="teacherMap" type="teacher">
    <id property="tId" column="t_id" />
    <!-- 开启自动映射,并且开启驼峰式支持!可以省略 result!-->
<!--        <result property="tName" column="t_name" />-->
    <collection property="students" ofType="student" >
        <id property="sId" column="s_id" />
<!--            <result property="sName" column="s_name" />-->
    </collection>
</resultMap>

多表映射总结

关联关系 配置项关键词 所在配置文件和具体位置
对一 association标签中有javaType属性和property属性 Mapper配置文件中的resultMap标签内
对多 collection标签中有ofType属性和property属性 Mapper配置文件中的resultMap标签内

MyBatis 动态语句

if 和 where 标签

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

java 复制代码
<!-- List<Employee> selectEmployeeByCondition(Employee employee); -->
<select id="selectEmployeeByCondition" resultType="employee">
    select emp_id,emp_name,emp_salary from t_emp
    <!-- where标签会自动去掉"标签体内前面多余的and/or" -->
    <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>
</select>

set 标签

复制代码
<!-- void updateEmployeeDynamic(Employee employee) -->
<update id="updateEmployeeDynamic">
    update t_emp
    <!-- set emp_name=#{empName},emp_salary=#{empSalary} -->
    <!-- 使用set标签动态管理set子句,并且动态去掉两端多余的逗号 -->
    <set>
        <if test="empName != null">
            emp_name=#{empName},
        </if>
        <if test="empSalary &lt; 3000">
            emp_salary=#{empSalary},
        </if>
    </set>
    where emp_id=#{empId}
    <!--
         第一种情况:所有条件都满足 SET emp_name=?, emp_salary=?
         第二种情况:部分条件满足 SET emp_salary=?
         第三种情况:所有条件都不满足 update t_emp where emp_id=?
            没有set子句的update语句会导致SQL语法错误
     -->
</update>

trim 标签(了解)

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

  • prefix属性:指定要动态添加的前缀

  • suffix属性:指定要动态添加的后缀

  • prefixOverrides属性:指定要动态去掉的前缀,使用"|"分隔有可能的多个值

  • suffixOverrides属性:指定要动态去掉的后缀,使用"|"分隔有可能的多个值

    <select id="selectEmployeeByConditionByTrim" resultType="com.atguigu.mybatis.entity.Employee"> select emp_id,emp_name,emp_age,emp_salary,emp_gender from t_emp
    复制代码
      <!-- 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>
    </select>

choose/when/otherwise

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

  • 从上到下依次执行条件判断

  • 遇到的第一个满足条件的分支会被采纳

  • 被采纳分支后面的分支都将不被考虑

  • 如果所有的when分支都不满足,那么就执行otherwise分支

    复制代码
    <!-- List<Employee> selectEmployeeByConditionByChoose(Employee employee) -->
    <select id="selectEmployeeByConditionByChoose" resultType="com.atguigu.mybatis.entity.Employee">
        select emp_id,emp_name,emp_salary from t_emp
        where
        <choose>
            <when test="empName != null">emp_name=#{empName}</when>
            <when test="empSalary &lt; 3000">emp_salary &lt; 3000</when>
            <otherwise>1=1</otherwise>
        </choose>
        
        <!--
         第一种情况:第一个when满足条件 where emp_name=?
         第二种情况:第二个when满足条件 where emp_salary < 3000
         第三种情况:两个when都不满足 where 1=1 执行了otherwise
         -->
    </select>

froeach 标签

基本用法

用批量插入举例

复制代码
<!--
    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注解指定的名称来引用传入的参数

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 高级扩展

Mapper 批量映射优化

  1. 需求

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

  2. 配置方式

    Mybatis 允许在指定 Mapper 映射文件时,只指定其所在的包:

XML 复制代码
<mappers>
    <package name="com.atguigu.mapper"/>
</mappers>
复制代码
此时这个包下的所有 Mapper 配置文件将被自动加载、注册,比较方便。
  1. 资源创建要求
  • Mapper 接口和 Mapper 配置文件名称一致
    • Mapper 接口:EmployeeMapper.java
    • Mapper 配置文件:EmployeeMapper.xml
  • Mapper 配置文件放在 Mapper 接口所在的包内
    • 可以将mapperxml文件放在mapper接口所在的包!

    • 可以在sources下创建mapper接口包一致的文件夹结构存放mapperxml文件


插件和分布插件 PageHelper

插件机制和 PageHelper 插件介绍

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

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

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

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

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

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. 面插件使用

    在查询方法中使用分布:

    复制代码
    @Test
    public void testTeacherRelationshipToMulti() {
    
        TeacherMapper teacherMapper = session.getMapper(TeacherMapper.class);
    
        PageHelper.startPage(1,2);
        // 查询Customer对象同时将关联的Order集合查询出来
        List<Teacher> allTeachers = teacherMapper.findAllTeachers();
    //
        PageInfo<Teacher> pageInfo = new PageInfo<>(allTeachers);
    
        System.out.println("pageInfo = " + pageInfo);
        long total = pageInfo.getTotal(); // 获取总记录数
        System.out.println("total = " + total);
        int pages = pageInfo.getPages();  // 获取总页数
        System.out.println("pages = " + pages);
        int pageNum = pageInfo.getPageNum(); // 获取当前页码
        System.out.println("pageNum = " + pageNum);
        int pageSize = pageInfo.getPageSize(); // 获取每页显示记录数
        System.out.println("pageSize = " + pageSize);
        List<Teacher> teachers = pageInfo.getList(); //获取查询页的数据集合
        System.out.println("teachers = " + teachers);
        teachers.forEach(System.out::println);
    
    }

逆向工程和 MybatisX 插件

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 等。

逆向工程

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

MyBatis 的逆向工程有两种方式:通过 MyBatis Generator 插件实现和通过 Maven 插件实现。无论是哪种方式,逆向工程一般需要指定一些配置参数,例如数据库连接 URL、用户名、密码、要生成的表名、生成的文件路径等等。

总的来说,MyBatis 的逆向工程为程序员提供了一种方便快捷的方式,能够快速地生成持久层代码和映射文件,是半自动 ORM 思维像全自动发展的过程,提高程序员的开发效率。

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

MyBatis 总结

mybatis基础 使用流程, 参数输入,#{} ${},参数输出
mybatis多表 实体类设计,resultMap多表结果映射
mybatis动态语句 Mybatis动态语句概念, where , if , foreach标签
mybatis扩展 Mapper批量处理,分页插件,逆向工程
相关推荐
李宥小哥1 小时前
Redis14-实践-查询缓存
spring·缓存·mybatis
P***84396 小时前
idea、mybatis报错Property ‘sqlSessionFactory‘ or ‘sqlSessionTemplate‘ are required
tomcat·intellij-idea·mybatis
p***95009 小时前
Springboot3 Mybatis-plus 3.5.9
数据库·oracle·mybatis
倚肆9 小时前
MyBatis-Plus Mapper 接口方法详解
java·mybatis
老神在在00110 小时前
Mybatis01
后端·学习·spring·java-ee·mybatis
q***235710 小时前
配置MyBatis-Plus打印执行的 SQL 语句到控制台或日志文件中
数据库·sql·mybatis
不光头强10 小时前
mybatis中的延迟加载和一二级缓存
java·tomcat·mybatis
好好研究10 小时前
MyBatis框架 - 延迟加载+一/二级缓存
java·数据库·mybatis
0***863310 小时前
【SpringBoot3】Spring Boot 3.0 集成 Mybatis Plus
spring boot·后端·mybatis