• SSM框架学习(三、MyBatis实践:提高持久层数据处理效率)


    目录

    一、Mybatis简介

    1.简介

    2.持久层框架对比

    3.快速入门(基于Mybatis3方式)

    4.ibatis方式的实现和原理

    5.ibatis与mybatis之间的关系

    二、Mybatis基本使用 

    1.向 sql 语句传参

    (1)mybatis日志输出配置

    (2)#{ key } 和 ${ key } 

     2.数据输入

    (1)单个简单类型参数

    (2)实体类类型参数 

    (3)零散的简单类型数据

    (4)Map类型参数

     3.数据输出

    (1)单个简单类型

    (2)返回实体类对象

    (3)返回 Map 类型

    (4)返回 List 类型

    (5)返回主键值

    Ⅰ. 自增长类型主键

    Ⅱ. 非自增长类型主键 

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

    三、Mybatis 多表映射

    1.概念

    (1)实体类设计方案

     (2)多表映射案例准备

    2.对一映射

    3.对多映射

    4.多表映射优化

    四、MyBatis动态语句

    1. if 和 where标签 

    2. set 标签

    3. trim标签(了解)

    4. choose/when/otherwise 标签

    5. foreach标签

    6. sql 片段提取

    五、Mybatis 高级扩展

    1. Mapper 批量映射优化

    2. 插件和分页插件 PageHelper

    (1)插件机制

    (2)PageHelper 插件使用

     3. 逆向工程和MybatisX插件

    (1)ORM思维介绍

    (2)逆向工程

     (3)逆向工程插件 MyBatisX 使用


    一、Mybatis简介

    1.简介

    MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。

    MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

    MyBatis最初是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation迁移到了Google Code。随着开发团队转投Google Code旗下, iBatis3.x正式更名为MyBatis。代码于2013年11月迁移到Github。

    2.持久层框架对比

    JDBC:

            SQL 夹杂在Java代码中耦合度高,导致硬编码内伤

            维护不易且实际开发需求中 SQL 有变化,频繁修改的情况多见

            代码冗长,开发效率低

    Hibernate 和 JPA:

            操作简便,开发效率高

            程序中的长难复杂 SQL 需要绕过框架

            内部自动生成的 SQL,不容易做特殊优化

            基于全映射的全自动框架,大量字段的 POJO 进行部分映射时比较困难。

            反射操作太多,导致数据库性能下降

    MyBatis:

            轻量级,性能出色

            SQL 和 Java 编码分开,功能边界清晰。Java代码专注业务、SQL语句专注数据

            开发效率稍逊于 Hibernate,但是完全能够接收

    开发效率:Hibernate>Mybatis>JDBC

    运行效率:JDBC>Mybatis>Hibernate

    3.快速入门(基于Mybatis3方式)

    a.准备数据模型

    1. CREATE DATABASE `mybatis-example`;
    2. USE `mybatis-example`;
    3. CREATE TABLE `t_emp`(
    4. emp_id INT AUTO_INCREMENT,
    5. emp_name CHAR(100),
    6. emp_salary DOUBLE(10,5),
    7. PRIMARY KEY(emp_id)
    8. );
    9. INSERT INTO `t_emp`(emp_name,emp_salary) VALUES("tom",200.33);
    10. INSERT INTO `t_emp`(emp_name,emp_salary) VALUES("jerry",666.66);
    11. INSERT INTO `t_emp`(emp_name,emp_salary) VALUES("andy",777.77);

    b.项目搭建和准备

    ① 项目搭建

    ② 依赖导入

    1. <dependencies>
    2. <dependency>
    3. <groupId>org.mybatisgroupId>
    4. <artifactId>mybatisartifactId>
    5. <version>3.5.11version>
    6. dependency>
    7. <dependency>
    8. <groupId>mysqlgroupId>
    9. <artifactId>mysql-connector-javaartifactId>
    10. <version>8.0.25version>
    11. dependency>
    12. <dependency>
    13. <groupId>org.junit.jupitergroupId>
    14. <artifactId>junit-jupiter-apiartifactId>
    15. <version>5.3.1version>
    16. dependency>
    17. dependencies>

    ③ 实体类准备

    1. public class Employee {
    2. private Integer empId;
    3. private String empName;
    4. private Double empSalary;
    5. public Integer getEmpId() {
    6. return empId;
    7. }
    8. public void setEmpId(Integer empId) {
    9. this.empId = empId;
    10. }
    11. public String getEmpName() {
    12. return empName;
    13. }
    14. public void setEmpName(String empName) {
    15. this.empName = empName;
    16. }
    17. public Double getEmpSalary() {
    18. return empSalary;
    19. }
    20. public void setEmpSalary(Double empSalary) {
    21. this.empSalary = empSalary;
    22. }
    23. }

     c.准备Mapper接口和MapperXML文件

    MyBatis 框架下,SQL语句编写位置发生改变,从原来的Java类,改成XML或者注解定义!

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

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

    ① 定义mapper接口

    1. package com.mihoyo.mapper;
    2. import com.mihoyo.pojo.Employee;
    3. public interface EmployMapper {
    4. //根据id查询员工信息
    5. Employee queryById(Integer id);
    6. }

    ② 定义mapper.xml 

    1. "1.0" encoding="UTF-8" ?>
    2. mapper
    3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    4. "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
    5. <mapper namespace="com.mihoyo.mapper.EmployeeMapper">
    6. <select id="queryById" resultType="com.mihoyo.pojo.Employee">
    7. select emp_id empId,emp_name empName, emp_salary empSalary from
    8. t_emp where emp_id = #{id}
    9. select>
    10. mapper>

    注意: 

    ① 四个一致:

            方法名和 SQL 的 id 一致

            方法返回值和 resultType 一致

            方法的参数和 SQL 的参数一致

            接口的全类名和映射配置文件的名称空间一致

    mapper 接口不能方法重载!因为虽然 java 语法不会出现问题,但 mapper.xml 无法识别,它是根据方法名进行识别的。

    ③ mapper.xml 一般写在 resource 目录下,这样编译后 maven 会打包到类路径(target/classes)中。

    d.准备MyBatis配置文件

    mybatis框架配置文件: 数据库连接信息,性能配置,mapper.xml配置等。

    习惯上命名为 mybatis-config.xml,这个文件名仅仅只是建议,并非强制要求。将来整合 Spring 之后,这个配置文件可以省略,所以大家操作时可以直接复制、粘贴。

    1. "1.0" encoding="UTF-8" ?>
    2. configuration
    3. PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    4. "http://mybatis.org/dtd/mybatis-3-config.dtd">
    5. <configuration>
    6. <environments default="development">
    7. <environment id="development">
    8. <transactionManager type="JDBC"/>
    9. <dataSource type="POOLED">
    10. <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
    11. <property name="url" value="jdbc:mysql://localhost:3306/mybatis-example"/>
    12. <property name="username" value="root"/>
    13. <property name="password" value="123456"/>
    14. dataSource>
    15. environment>
    16. environments>
    17. <mappers>
    18. <mapper resource="mappers/EmployeeMapper.xml"/>
    19. mappers>
    20. configuration>

    e.运行和测试

    1. public class MybatisTest {
    2. //使用 mybatis 提供的api进行方法的调用
    3. @Test
    4. public void test_01() throws IOException {
    5. //1.读取外部配置文件(mybatis-config.xml)
    6. InputStream ips = Resources.getResourceAsStream("mybatis-config.xml");
    7. //2.创建sqlSessionFactory
    8. SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ips);
    9. //3.根据创建sqlSessionFactory创建sqlSession(每次业务创建一个,用完就释放)
    10. SqlSession sqlSession = sqlSessionFactory.openSession();
    11. //4.获取接口的代理对象(通过代理技术),调用代理对象的方法,就会查找mapper接口的方法
    12. EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
    13. Employee employee = mapper.queryById(1);
    14. System.out.println("employee = "+ employee);
    15. //5.提交事务(非查询语句)和释放资源
    16. //sqlSession.commit();
    17. sqlSession.close();
    18. }
    19. }

    说明:

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

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

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


    SqlSession 和 HttpSession 区别:

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

                                    代表浏览器和Web服务器之间的会话。

            SqlSession:不依赖Web服务器,属于持久化层。

                                    代表Java程序和数据库之间的会话。

    4.ibatis方式的实现和原理

    a.准备数据模型

    1. use `mybatis-example`;
    2. create table student(
    3. sid int primary key auto_increment,
    4. sname varchar(20)
    5. );
    6. INSERT INTO `student` (sid, sname) VALUES (1, "tom");
    7. INSERT INTO `student` (sid, sname) VALUES (2, "jerry");

     b.准备实体类

    1. public class Student {
    2. private Integer sid;
    3. private String sname;
    4. public Student() {
    5. }
    6. public Student(Integer sid, String sname) {
    7. this.sid = sid;
    8. this.sname = sname;
    9. }
    10. public Integer getSid() {
    11. return sid;
    12. }
    13. public void setSid(Integer sid) {
    14. this.sid = sid;
    15. }
    16. public String getSname() {
    17. return sname;
    18. }
    19. public void setSname(String sname) {
    20. this.sname = sname;
    21. }
    22. public String toString() {
    23. return "Student{sid = " + sid + ", sname = " + sname + "}";
    24. }
    25. }

     c.定义mapper.xml (不用定义mapper接口)

    1. "1.0" encoding="UTF-8" ?>
    2. mapper
    3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    4. "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
    5. <mapper namespace="xx.yy">
    6. <select id="kkk" resultType="com.mihoyo.pojo.Student">
    7. select * from student where sid = #{id}
    8. select>
    9. mapper>

    d.准备MyBatis配置文件

    1. "1.0" encoding="UTF-8" ?>
    2. configuration
    3. PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    4. "http://mybatis.org/dtd/mybatis-3-config.dtd">
    5. <configuration>
    6. <environments default="development">
    7. <environment id="development">
    8. <transactionManager type="JDBC"/>
    9. <dataSource type="POOLED">
    10. <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
    11. <property name="url" value="jdbc:mysql://localhost:3306/mybatis-example"/>
    12. <property name="username" value="root"/>
    13. <property name="password" value="123456"/>
    14. dataSource>
    15. environment>
    16. environments>
    17. <mappers>
    18. <mapper resource="mappers/StudentMapper.xml"/>
    19. mappers>
    20. configuration>

    e.运行和测试

    1. @Test
    2. public void test_02() throws IOException {
    3. //1.读取外部配置文件(mybatis-config.xml)
    4. InputStream ips = Resources.getResourceAsStream("mybatis-config.xml");
    5. //2.创建sqlSessionFactory
    6. SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ips);
    7. //3.根据创建sqlSessionFactory创建sqlSession(每次业务创建一个,用完就释放)
    8. SqlSession sqlSession = sqlSessionFactory.openSession();
    9. //4.使用sqlSession提供的增删改查方法进行数据库操作
    10. //参数1:sql标签对应的标识(id 或者 namespace.id)
    11. //参数2:执行sql语句传入的参数
    12. Student stu = sqlSession.selectOne("xx.yy.kkk", 1);
    13. System.out.println("Student = " + stu);
    14. //5.提交事务(非查询语句)和释放资源
    15. //sqlSession.commit();
    16. sqlSession.close();
    17. }

     注意:

    ibatis 方式的缺点:

            a. sql 语句标签对应的字符串标识太过随意,容易出现错误

            b. 执行 sql 语句传入的参数只能传一个,多个参数需要整合成一个 Map 集合中

            c. 返回值类型需要自己指定,不会提示,默认是 Object 类型

    ② sqlSession 提供了增删改查方法,但该方法并不是帮我们生成 sql 语句,而是帮我们查找对应的 sql 语句标签,再交给 mybatis 执行。

    5.ibatis与mybatis之间的关系

    Mybatis 会先使用 jdk 动态代理技术,生成一个代理对象。

    ① 代理对象 根据 接口的全限定符 和 方法名,内部拼接成 "接口的全限定符.方法名",再去调用 ibatis 对应的方法,去查找对应的 sql 语句标签,进行执行。

    所以 mapper.xml 会严格限定 namespace 和 id,因为拼接后要根据它们去查找对应的 sql 标签。

    这样就确保了 查找 sql 语句标签的过程不会出现错误!

    ② 代理对象也会将传入的参数进行整合,然后给 ibatis 对应的方法传入整合后的参数。

    这样也优化了参数传递!

    所以:mybatis 底层依然调用 ibatis,只不过底层进行了封装,有一套固定的模式!

    二、Mybatis基本使用 

    1.向 sql 语句传参

    (1)mybatis日志输出配置

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

    我们可以在 mybatis 的配置文件使用 settings 标签设置,输出运过程SQL日志!

    通过查看日志,我们可以判定#{} 和 ${}的输出效果!

    settings 设置项:

    logImpl

    指定 MyBatis 所用日志的具体实现,未指定时将自动查找。

    SLF4J

    LOG4J(3.5.9 起废弃)

    LOG4J2

    JDK_LOGGING

    COMMONS_LOGGING

    STDOUT_LOGGING

    NO_LOGGING

    未设置

    日志配置: 

    1. "1.0" encoding="UTF-8" ?>
    2. configuration
    3. PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    4. "http://mybatis.org/dtd/mybatis-3-config.dtd">
    5. <configuration>
    6. <settings>
    7. <setting name="logImpl" value="STDOUT_LOGGING"/>
    8. settings>
    9. ...
    10. configuration>

    (2)#{ key } 和 ${ key } 

    #{ key }占位符 + 赋值
    ${ key }字符串拼接

     #{ key } 形式:

     ${ key } 形式:

    细节:

    ① 推荐使用 #{ key } 的形式,可以防止 SQL注入

    ② 存在即合理!#{ key } 只能替代值的位置,对于列名,容器名,关键字,都无法替代。

         比如:select * from 表 where 动态列名 = 动态值

         动态列名只能使用 ${ columnName },动态值可以使用 #{ columnValue }

    所以:动态值,使用 #{ key }

               动态列名,容器名,关键字,使用 ${ key }

     2.数据输入

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

    简单类型:只包含一个值的数据类型(单值类型) 

            基本数据类型:int、byte、short、double、……

            基本数据类型的包装类型:Integer、Character、Double、……

            字符串类型:String

    复杂类型:包含多个值的数据类型(多值类型)

            实体类类型:Employee、Department、……

            集合类型:List、Set、Map、……

            数组类型:int[]、String[]、……

            复合类型:List、实体类中包含集合……

    (1)单个简单类型参数

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

    1. //根据id删除员工信息
    2. int deleteById(Integer id);
    3. //根据工资查询员工信息
    4. List queryBySalary(Double salary);

     SQL语句:

    1. <delete id="deleteById">
    2. delete from t_emp where emp_id = #{empId}
    3. delete>
    4. <select id="queryBySalary" resultType="com.mihoyo.pojo.Employee">
    5. select emp_id empId,emp_name empName,emp_salary empSalary
    6. from t_emp where emp_salary= #{salary}
    7. select>

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

    (2)实体类类型参数 

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

    1. //插入员工数据(实体对象)
    2. int insertEmp(Employee employee);

    SQL语句:

    1. <insert id="insertEmp">
    2. insert into t_emp(emp_name,emp_salary) values(#{empName},#{empSalary})
    3. insert>

    当传入的是一个实体对象,key = 属性名

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

    (3)零散的简单类型数据

    方案一:使用 mybatis 默认机制,形参从左到右依次对应:arg0 / param1, arg1 / param2 ...

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

    1. //根据员工姓名和工资查询员工信息
    2. List queryByNameAndSalary(String name,Double salary);

     SQL语句:

    1. <select id="queryByNameAndSalary" resultType="com.mihoyo.pojo.Employee">
    2. select emp_id empId,emp_name empName,emp_salary empSalary
    3. from t_emp where emp_name = #{arg0} and emp_salary = ${arg1}
    4. select>

    或者

    1. <select id="queryByNameAndSalary" resultType="com.mihoyo.pojo.Employee">
    2. select emp_id empId,emp_name empName,emp_salary empSalary
    3. from t_emp where emp_name = #{param1} and emp_salary = ${param2}
    4. select>

    方案二:使用 @Param 注解指定(推荐)

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

    1. //根据员工姓名和工资查询员工信息
    2. List queryByNameAndSalary(@Param("eName") String name, @Param("eSalary") Double salary);

     SQL语句:

    1. <select id="queryByNameAndSalary" resultType="com.mihoyo.pojo.Employee">
    2. select emp_id empId,emp_name empName,emp_salary empSalary
    3. from t_emp where emp_name = #{eName} and emp_salary = ${eSalary}
    4. select>

    (4)Map类型参数

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

    1. //插入员工数据,传入一个map(name=员工名,salary=员工薪水)
    2. int insertEmpMap(Map data);

    SQL语句:

    1. "insertEmpMap">
    2. insert into t_emp(emp_name,emp_salary) values(#{name},#{salary})

    当传入的是 map 类型的参数时,key = map 中的 key

    测试:

    1. @Test
    2. public void test_01() throws IOException {
    3. //1.读取外部配置文件
    4. InputStream ips = Resources.getResourceAsStream("mybatis-config.xml");
    5. //2.创建sqlSessionFactory
    6. SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ips);
    7. //3.获取sqlSession
    8. SqlSession sqlSession = sqlSessionFactory.openSession();
    9. //4.获取代理mapper对象
    10. EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
    11. Map paramMap = new HashMap<>();
    12. //存入键值对
    13. paramMap.put("name", "zhangsan");
    14. paramMap.put("salary", 123.45);
    15. int result = mapper.insertEmpMap(paramMap);
    16. System.out.println("result = " + result);
    17. //5.提交事务或释放资源
    18. sqlSession.close();
    19. }

     3.数据输出

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

            增删改操作:默认返回的是受影响行数,直接使用 int 或 long 类型接收即可

            查询操作:查询结果的返回值类型可能不确定

    我们需要做的是,指定查询的输出数据类型,并且插入场景下,实现主键数据回显示。

    (1)单个简单类型

    Mapper 接口中的抽象方法:

    1. //dml语句(插入,修改,删除)-->返回受影响的行数
    2. int deleteById(Integer id);
    3. //根据员工的id查询员工姓名
    4. String queryNameById(Integer id);
    5. //根据员工的id查询员工工资
    6. Double querySalaryById(Integer id);

    SQL语句:

    1. <delete id="deleteById">
    2. delete from t_emp where emp_id = #{id}
    3. delete>
    4. <select id="queryNameById" resultType="java.lang.String">
    5. select emp_name from t_emp where emp_id = #{id}
    6. select>
    7. <select id="querySalaryById" resultType="double">
    8. select emp_salary from t_emp where emp_id = #{id}
    9. select>

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

    resultType 的取值有两种:类的全限定符类的别名

    mybatis 给我们常用的 Java 数据类型,都提供了别名,如下:

    映射的类型别名
    byte_byte
    char_char (since 3.5.10)
    char_character (since 3.5.10)
    long_long
    short_short
    int_int
    int_integer
    double_double
    float_float
    boolean_boolean
    Stringstring
    Bytebyte
    Characterchar (since 3.5.10)
    Charactercharacter (since 3.5.10)
    Longlong
    Shortshort
    Integerint
    Integerinteger
    Doubledouble
    Floatfloat
    Booleanboolean
    Datedate
    BigDecimaldecimal
    BigDecimalbigdecimal
    BigIntegerbiginteger
    Objectobject
    Date[]date[]
    BigDecimal[]decimal[]
    BigDecimal[]bigdecimal[]
    BigInteger[]biginteger[]
    Object[]object[]
    Mapmap
    HashMaphashmap
    Listlist
    ArrayListarraylist
    Collectioncollection
    Iteratoriterator

    不用死记,规则如下:

            基本数据类型:int   double  ->  _int   _double

            包装数据类型:Integer   Double  ->  int / integer  double

            集合容器类型:Map   List   HashMap  ->  map   list   hashmap(小写即可)


    Test:如果返回的类型,java 没有提供相应的别名,怎么办?

    可以自定义别名,或 使用类的全限定符。

    扩展(给自定义类定义别名):

    ① 给单独的类定义别名:

    1. "1.0" encoding="UTF-8" ?>
    2. configuration
    3. PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    4. "http://mybatis.org/dtd/mybatis-3-config.dtd">
    5. <configuration>
    6. <typeAliases>
    7. <typeAlias type="com.mihoyo.pojo.Employee" alias="employee"/>
    8. typeAliases>
    9. ...
    10. configuration>

    ② 批量定义别名

    1. "1.0" encoding="UTF-8" ?>
    2. configuration
    3. PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    4. "http://mybatis.org/dtd/mybatis-3-config.dtd">
    5. <configuration>
    6. <typeAliases>
    7. <package name="com.mihoyo.pojo"/>
    8. typeAliases>
    9. ...
    10. configuration>

    批量将包下的类给于别名,别名就是首字母小写的类名(如 Employee --> employee)

    批量定义别名后(前提条件),不想使用批量的别名,可以使用 @Alias 注解重新定义别名

    1. @Alias("emp")
    2. public class Employee {
    3. ...
    4. }

    (2)返回实体类对象

    Mapper 接口的抽象方法:

    1. //返回单个自定义实体类型
    2. Employee queryById(Integer id);

    SQL语句:

    1. <select id="queryById" resultType="com.mihoyo.pojo.Employee">
    2. select emp_id empId,emp_name empName,emp_salary empSalary
    3. from t_emp where emp_id = ${empId}
    4. select>

    返回实体类对象时,resultType = 实体类型即可

    注意:

    当返回实体类对象时,存在一个默认要求:列名和属性名要一 一映射,因为底层会根据映射对应的属性名存入对象。

    所以,sql 语句中需要给每个数据库表字段起别名

    但这种方式过于麻烦,我们可以增加全局配置自动识别对应关系:

    1. <settings>
    2. <setting name="mapUnderscoreToCamelCase" value="true"/>
    3. settings>

    在 Mybatis 全局配置文件中,进行如上配置,select语句中可以不给字段设置别名。

    可以自动将字段名(XXX_YYY)变为属性名(xxxYyy)。

    (3)返回 Map 类型

    SQL查询返回的各个字段综合起来并不和任何一个现有的实体类对应,没法封装到实体类对象中,就可以用map集合进行存储,key = 查询的列名,value = 查询到的值

    Mapper 接口的抽象方法:

    1. //查询部门的最高工资和平均工资
    2. Map selectEmpNameAndMaxSalary();

    SQL语句:

    1. <select id="selectEmpNameAndMaxSalary" resultType="map">
    2. SELECT
    3. emp_name 员工姓名,
    4. emp_salary 员工工资,
    5. (SELECT AVG(emp_salary) FROM t_emp) 部门平均工资
    6. FROM t_emp WHERE emp_salary=(
    7. SELECT MAX(emp_salary) FROM t_emp
    8. )
    9. select>

    测试:

    1. @Test
    2. public void test_01() throws IOException {
    3. //1.读取外部配置文件
    4. InputStream ips = Resources.getResourceAsStream("mybatis-config.xml");
    5. //2.创建sqlSessionFactory
    6. SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ips);
    7. //3.获取sqlSession
    8. SqlSession sqlSession = sqlSessionFactory.openSession();
    9. //4.获取代理mapper对象
    10. EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
    11. Map resultMap = mapper.selectEmpNameAndMaxSalary();
    12. Set> entrySet = resultMap.entrySet();
    13. for (Map.Entry entry : entrySet) {
    14. String key = entry.getKey();
    15. Object value = entry.getValue();
    16. System.out.println(key + "=" + value);
    17. }
    18. //5.提交事务或释放资源
    19. sqlSession.close();
    20. }

    (4)返回 List 类型

    Mapper 接口中抽象方法:

    1. //查询工资高于传入值的员工姓名
    2. List queryNameBySalary(Double salary);
    3. //查询所有员工信息
    4. List queryAll();

    SQL语句:

    1. <select id="queryNameBySalary" resultType="string">
    2. select emp_name from t_emp where emp_salary > #{ salary }
    3. select>
    4. <select id="queryAll" resultType="employee">
    5. select * from t_emp
    6. select>

    返回的是 List 集合类型,此时不需要任何特殊处理,在 resultType 属性中指定泛型类型即可。

    原理:

    Mybatis 底层调用的是 ibatis,查询共有两个方法:selectOne 和 selectList,而 SelectOne 底层也是调用 selectList,所以底层本质全是按照 List 集合进行查找的

    (5)返回主键值

    Ⅰ. 自增长类型主键

    Mapper 接口中的抽象方法:

    1. //员工插入
    2. int insertEmp(Employee employee);

    SQL语句:

    1. <insert id="insertEmp" useGeneratedKeys="true" keyColumn="emp_id" keyProperty="empId">
    2. insert into t_emp(emp_name,emp_salary) value(#{empName},#{empSalary})
    3. insert>

    测试:

    1. @Test
    2. public void test_02() throws IOException {
    3. //1.读取外部配置文件
    4. InputStream ips = Resources.getResourceAsStream("mybatis-config.xml");
    5. //2.创建sqlSessionFactory
    6. SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ips);
    7. //3.获取sqlSession
    8. SqlSession sqlSession = sqlSessionFactory.openSession();
    9. //4.获取代理mapper对象
    10. EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
    11. //准备实体对象
    12. Employee employee = new Employee();
    13. employee.setEmpName("zhangsan");
    14. employee.setEmpSalary(999.0);
    15. System.out.println("插入前,empId = " + employee.getEmpId());
    16. //插入
    17. mapper.insertEmp(employee);
    18. System.out.println("插入后,empId = " + employee.getEmpId());
    19. //5.提交事务或释放资源
    20. sqlSession.close();
    21. }

    运行结果:

    Ⅱ. 非自增长类型主键 

    a.准备数据库

    1. create table teacher(
    2. t_id varchar(64) primary key ,
    3. t_name varchar(20)
    4. )

    b. 准备实体类

    1. public class Teacher {
    2. private String tId;
    3. private String tName;
    4. public Teacher() {
    5. }
    6. public Teacher(String tId, String tName) {
    7. this.tId = tId;
    8. this.tName = tName;
    9. }
    10. public String getTId() {
    11. return tId;
    12. }
    13. public void setTId(String tId) {
    14. this.tId = tId;
    15. }
    16. public String getTName() {
    17. return tName;
    18. }
    19. public void setTName(String tName) {
    20. this.tName = tName;
    21. }
    22. public String toString() {
    23. return "Teacher{tId = " + tId + ", tName = " + tName + "}";
    24. }
    25. }

    Mapper 接口中的抽象方法:

    1. public interface TeacherMapper {
    2. //插入老师信息
    3. int insertTeacher(Teacher teacher);
    4. }

    SQL语句:

    1. "1.0" encoding="UTF-8" ?>
    2. mapper
    3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    4. "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
    5. <mapper namespace="com.mihoyo.mapper.TeacherMapper">
    6. <insert id="insertTeacher">
    7. insert into teacher(t_id, t_name) values (#{tId},#{tName})
    8. insert>
    9. mapper>

    测试:

    1. @Test
    2. public void test_03() throws IOException {
    3. //1.读取外部配置文件
    4. InputStream ips = Resources.getResourceAsStream("mybatis-config.xml");
    5. //2.创建sqlSessionFactory
    6. SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ips);
    7. //3.获取sqlSession
    8. SqlSession sqlSession = sqlSessionFactory.openSession();
    9. //4.获取代理mapper对象
    10. TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
    11. //准备实体对象
    12. Teacher teacher = new Teacher();
    13. teacher.setTName("zhangsan");
    14. //用UUID随机生产主键id
    15. String id= UUID.randomUUID().toString().replace("-","");
    16. teacher.setTId(id);
    17. //插入
    18. mapper.insertTeacher(teacher);
    19. //5.提交事务或释放资源
    20. sqlSession.close();
    21. }

    上述数据库的主键是一个字符串类型的数据,每次插入新数据都需要书写一段代码来生成主键值,过于麻烦(需要自己维护主键)。

    改进方案:Mybatis 可以帮助我们对主键进行维护,无需自己生成主键值。

    1. <insert id="insertTeacher">
    2. <selectKey order="BEFORE" resultType="string" keyProperty="tId">
    3. select replace(uuid(),'-','')
    4. selectKey>
    5. insert into teacher(t_id, t_name) values (#{tId},#{tName})
    6. insert>

    此时,测试代码中无需再自行维护主键:

    1. @Test
    2. public void test_03() throws IOException {
    3. //1.读取外部配置文件
    4. InputStream ips = Resources.getResourceAsStream("mybatis-config.xml");
    5. //2.创建sqlSessionFactory
    6. SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ips);
    7. //3.获取sqlSession
    8. SqlSession sqlSession = sqlSessionFactory.openSession();
    9. //4.获取代理mapper对象
    10. TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
    11. //准备实体对象
    12. Teacher teacher = new Teacher();
    13. teacher.setTName("zhangsan");
    14. System.out.println("插入前,tId = " + teacher.getTId());
    15. //插入
    16. mapper.insertTeacher(teacher);
    17. System.out.println("插入后,tId = " + teacher.getTId());
    18. //5.提交事务或释放资源
    19. sqlSession.close();
    20. }

     运行结果:

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

    方案一:别名对应

    1. <select id="queryById" resultType="com.mihoyo.pojo.Employee">
    2. select emp_id empId,emp_name empName,emp_salary empSalary
    3. from t_emp where emp_id = ${empId}
    4. select>

    方案二:全局配置自动识别驼峰式命名规则

    1. <settings>
    2. <setting name="mapUnderscoreToCamelCase" value="true"/>
    3. settings>

    SQL语句中可以不使用别名:

    1. <select id="queryById" resultType="com.mihoyo.pojo.Employee">
    2. select emp_id,emp_name,emp_salary
    3. from t_emp where emp_id = ${empId}
    4. select>

    还可以直接使用:select * 

    方案三:使用 resultMap 自定义映射

    1. <resultMap id="eMap" type="employee">
    2. <id column="emp_id" property="empId"/>
    3. <result column="emp_name" property="empName"/>
    4. <result column="emp_salary" property="empSalary"/>
    5. resultMap>
    6. <select id="queryById" resultMap="eMap">
    7. select emp_id,emp_name,emp_salary from t_emp where emp_id = ${empId}
    8. select>

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

    三、Mybatis 多表映射

    1.概念

    开发中更多的是多表查询需求,这种情况我们如何让进行处理?

    我们的学习目标:

            ① 多表查询语句使用

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

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

    (1)实体类设计方案

    数据库中,多表关系是双向查看的关系:一对一,一对多,多对多

    而在 Java 实体类设计时,多表关系是单向查看的关系:

    对一:一个订单对应一个客户

    1. //客户实体
    2. public class Customer {
    3. private Integer customerId;
    4. private String customerName;
    5. }
    6. //订单实体
    7. public class Order {
    8. private Integer orderId;
    9. private String orderName;
    10. private Customer customer;// 体现的是对一的关系
    11. }

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

    对多:一个客户对应多个订单

    1. //订单实体
    2. public class Order {
    3. private Integer orderId;
    4. private String orderName;
    5. }
    6. //客户实体
    7. public class Customer {
    8. private Integer customerId;
    9. private String customerName;
    10. private List orderList;// 体现的是对多的关系
    11. }

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


    注意:

    ① 只有真实发生多表查询时,才需要设计和修改实体类,否则不提前设计和修改实体类。

    ② 无论多少张表联查,实体类设计都是两两考虑。

    ③ 在查询映射的时候,只需要关注本次查询相关的属性!例如:查询订单和对应的客户,就不要关注客户中的订单集合。

     (2)多表映射案例准备

    数据库:

    1. CREATE TABLE `t_customer` (`customer_id` INT NOT NULL AUTO_INCREMENT, `customer_name` CHAR(100), PRIMARY KEY (`customer_id`) );
    2. CREATE TABLE `t_order` ( `order_id` INT NOT NULL AUTO_INCREMENT, `order_name` CHAR(100), `customer_id` INT, PRIMARY KEY (`order_id`) );
    3. INSERT INTO `t_customer` (`customer_name`) VALUES ('c01');
    4. INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o1', '1');
    5. INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o2', '1');
    6. INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o3', '1');

    注意:

    实际开发时,一般在开发过程中,不给数据库表设置外键约束
    原因是避免调试不方便。 一般是功能开发完成,再加外键约束检查是否有bug。


    实体类设计:

    1. @Data // lombok
    2. public class Customer {
    3. private Integer customerId;
    4. private String customerName;
    5. private List orderList;// 体现的是对多的关系
    6. }
    7. @Data
    8. public class Order {
    9. private Integer orderId;
    10. private String orderName;
    11. private Integer customerId;//外键
    12. private Customer customer;// 体现的是对一的关系
    13. }

    2.对一映射

    需求:根据ID查询订单,以及订单关联的用户的信息

    a. OrderMapper接口

    1. public interface OrderMapper {
    2. //根据订单id查询订单信息和订单所对应的客户
    3. Order queryOrderById(Integer id);//订单信息中包含客户
    4. }

    b. OrderMapper.xml配置文件

    1. "1.0" encoding="UTF-8" ?>
    2. mapper
    3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    4. "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
    5. <mapper namespace="com.mihoyo.mapper.OrderMapper">
    6. <resultMap id="orderMap" type="order">
    7. <id column="order_id" property="orderId"/>
    8. <result column="order_name" property="orderName"/>
    9. <result column="customer_id" property="customerId"/>
    10. <association property="customer" javaType="customer">
    11. <id column="customer_id" property="customerId"/>
    12. <result column="customer_name" property="customerName"/>
    13. association>
    14. resultMap>
    15. <select id="queryOrderById" resultMap="orderMap">
    16. select * from t_order tor join t_customer tcr
    17. on tor.customer_id = tcr.customer_id
    18. where tor.order_id = #{id}
    19. select>
    20. mapper>

    对应关系可以参考下图:

    细节:

     resultType 只支持一层映射,而 resultMap 可以支持深层映射(可以多层嵌套 association)。

    c. mybatis-config.xml 配置文件

    1. "1.0" encoding="UTF-8" ?>
    2. configuration
    3. PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    4. "http://mybatis.org/dtd/mybatis-3-config.dtd">
    5. <configuration>
    6. <settings>
    7. <setting name="logImpl" value="STDOUT_LOGGING"/>
    8. settings>
    9. <typeAliases>
    10. <package name="com.mihoyo.pojo"/>
    11. typeAliases>
    12. <environments default="development">
    13. <environment id="development">
    14. <transactionManager type="JDBC"/>
    15. <dataSource type="POOLED">
    16. <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
    17. <property name="url" value="jdbc:mysql://localhost:3306/mybatis-example"/>
    18. <property name="username" value="root"/>
    19. <property name="password" value="123456"/>
    20. dataSource>
    21. environment>
    22. environments>
    23. <mappers>
    24. <mapper resource="mappers/OrderMapper.xml"/>
    25. mappers>
    26. configuration>

    d. 测试

    1. public class MybatisTest {
    2. private SqlSession sqlSession;
    3. @BeforeEach // 每次都测试方法之前,先走的初始化方法
    4. public void init() throws IOException {
    5. //1.读取外部配置文件
    6. InputStream ips = Resources.getResourceAsStream("mybatis-config.xml");
    7. //2.创建sqlSessionFactory
    8. SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ips);
    9. //3.获取sqlSession
    10. sqlSession = sqlSessionFactory.openSession();
    11. }
    12. @AfterEach // 每次都测试方法之后,调用的结束方法
    13. public void clean(){
    14. //5.提交事务或释放资源
    15. sqlSession.close();
    16. }
    17. @Test
    18. public void testToOne() {
    19. //根据id查询订单和对应的客户
    20. OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
    21. Order order = mapper.queryOrderById(1);
    22. System.out.println(order);
    23. System.out.println(order.getCustomer());
    24. }
    25. }

    3.对多映射

    需求:查询所有客户和客户关联的订单信息

    a. CustomerMapper 接口:

    1. public interface CustomerMapper {
    2. //查询所有客户信息以及客户对应的订单信息
    3. List queryCustomerAll();
    4. }

    b. CustomerMapper.xml 文件

    1. "1.0" encoding="UTF-8" ?>
    2. mapper
    3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    4. "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
    5. <mapper namespace="com.mihoyo.mapper.CustomerMapper">
    6. <resultMap id="customerMap" type="customer">
    7. <id column="customer_id" property="customerId"/>
    8. <result column="customer_name" property="customerName"/>
    9. <collection property="orderList" ofType="Order">
    10. <id column="order_id" property="orderId"/>
    11. <result column="order_name" property="orderName"/>
    12. <result column="customer_id" property="customerId"/>
    13. collection>
    14. resultMap>
    15. <select id="queryCustomerAll" resultMap="customerMap">
    16. select * from t_customer tcr
    17. join t_order tor
    18. on tcr.customer_id=tor.customer_id
    19. select>
    20. mapper>

    对应关系可以参考下图:

    c. Mybatis 全局注册Mapper文件 

    1. <mappers>
    2. <mapper resource="mappers/OrderMapper.xml"/>
    3. <mapper resource="mappers/CustomerMapper.xml"/>
    4. mappers>

    d. 测试:

    1. @Test
    2. public void testToMany(){
    3. CustomerMapper mapper = sqlSession.getMapper(CustomerMapper.class);
    4. List customers = mapper.queryCustomerAll();
    5. System.out.println("customers = " + customers);
    6. for (Customer customer : customers) {
    7. List orderList = customer.getOrderList();
    8. System.out.println("orderList = " + orderList);
    9. }
    10. }

    总结:

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

    4.多表映射优化

    setting属性属性含义可选值默认值
    autoMappingBehavior

    指定 MyBatis 应如何自动映射列到字段或属性。

    NONE 表示关闭自动映射

    PARTIAL 只会自动映射没有定义嵌套结果映射的字段(对于只有一层的 result 标签,可以自动映射)

    FULL 会自动映射任何复杂的结果集(无论是否多少层,都自动映射)

    NONE, PARTIAL,

    FULL

    PARTIAL

    将 autoMappingBehavior 设置为 full,进行多表 resultMap 映射的时候,可以省略符合列和属性命名映射规则的 result 标签。

    注意:

    只有 列名=属性名,或者开启驼峰映射(XXX_YYY -> xxxYyy)的 reult 标签才能自动映射。


    修改 mybati-sconfig.xml:

    1. <settings>
    2. <setting name="mapUnderscoreToCamelCase" value="true"/>
    3. <setting name="autoMappingBehavior" value="FULL"/>
    4. settings>

    此时,CustomerMapper.xml 中的自定义映射可省略为:

    1. "1.0" encoding="UTF-8" ?>
    2. mapper
    3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    4. "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
    5. <mapper namespace="com.mihoyo.mapper.CustomerMapper">
    6. <resultMap id="customerMap" type="customer">
    7. <id column="customer_id" property="customerId"/>
    8. <collection property="orderList" ofType="Order">
    9. <id column="order_id" property="orderId"/>
    10. collection>
    11. resultMap>
    12. <select id="queryCustomerAll" resultMap="customerMap">
    13. select * from t_customer tcr
    14. join t_order tor
    15. on tcr.customer_id=tor.customer_id
    16. select>
    17. mapper>

    四、MyBatis动态语句

    经常遇到很多按照很多查询条件进行查询的情况,比如智联招聘的职位搜索等。

    其中经常出现很多条件不取值的情况,在后台应该如何完成最终的SQL语句呢? 

    动态 SQL 是 MyBatis 的强大特性之一!

    1. if 和 where标签 

    需求:根据多个条件查询员工信息,但条件可能为空

     Mapper 接口中的抽象方法:

    1. public interface EmployeeMapper {
    2. //根据员工的姓名和工资查询员工信息
    3. List query(@Param("name") String name, @Param("salary") Double salary);
    4. }

    SQL语句:

    1. <select id="query" resultType="employee">
    2. select * from t_emp
    3. <where>
    4. <if test="name!=null">
    5. emp_name = #{name}
    6. if>
    7. <if test="salary !=null and salary > 0">
    8. and emp_salary = #{salary}
    9. if>
    10. where>
    11. select>

    细节:

    ① if 标签可以通过 test 属性进行判断,如果为 true,就将标签内的 sql 语句进行拼接。

         test 属性中的判断语句:key  比较符号  值(且:and    或:or)

         大于和小于可以直接写符号(< >),但不推荐,因为低版本的 mybatis 可能会识别成标签的开始或者结束符号。推荐使用实体符号(大于:>        小于:< )

    ② where 标签的作用:

            a.只要 where 内部有任何一个 if 满足,自动添加 where 关键字,否则不会添加 where 关键字

            b.自动去掉条件中多余的 and 和 or 关键字


    测试:

    1. @Test
    2. public void test_01() {
    3. //根据id查询订单和对应的客户
    4. EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
    5. List list = mapper.query(null, 100d);
    6. System.out.println("list = " + list);
    7. }

    2. set 标签

     Mapper 接口中的抽象方法:

    1. //根据员工id更新员工数据(要求name和salary不为空时才更新)
    2. int update(Employee employee);

     SQL语句:

    1. <update id="update">
    2. update t_emp
    3. <set>
    4. <if test="empName!=null">
    5. emp_name = #{empName},
    6. if>
    7. <if test="empSalary !=null">
    8. emp_salary = #{empSalary}
    9. if>
    10. set>
    11. where emp_id = #{empId}
    12. update>

    细节:

    set 标签的作用:

            a. 自动添加 set 关键字

            b. 自动去掉多余的逗号(,)

    注意:

    update 方法中,必须保证至少一个 if 满足。因为如果都不满足,不管有没有 set 关键字,语法都是错的!

    3. trim标签(了解)

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

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

    例如,前两个案例中的 SQL 语句可修改为:

    1. <select id="query" resultType="employee">
    2. select * from t_emp
    3. <trim prefix="where" prefixOverrides="and|or">
    4. <if test="name!=null">
    5. emp_name = #{name}
    6. if>
    7. <if test="salary !=null and salary > 0">
    8. and emp_salary = #{salary}
    9. if>
    10. trim>
    11. select>
    12. <update id="update">
    13. update t_emp
    14. <trim prefix="set" suffixOverrides=",">
    15. <if test="empName!=null">
    16. emp_name = #{empName},
    17. if>
    18. <if test="empSalary !=null">
    19. emp_salary = #{empSalary}
    20. if>
    21. trim>
    22. where emp_id = #{empId}
    23. update>

    4. choose/when/otherwise 标签

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

    从上到下依次执行条件判断,遇到的第一个满足条件的分支会被采纳,被采纳分支后面的分支都将不被考虑。

    如果所有的when分支都不满足,那么就执行otherwise分支。(类似于 switch-case-default)。


     Mapper 接口中的抽象方法:

    1. /*
    2. 根据两个条件查询
    3. 如果name不为空,根据name查询
    4. 如果name为空,salary不为空,根据salary查询
    5. 都为空,查询全部
    6. */
    7. List queryChoose(@Param("name") String name, @Param("salary") Double salary);

     SQL语句:

    1. <select id="queryChoose" resultType="employee">
    2. select * from t_emp where
    3. <choose>
    4. <when test="name!=null">
    5. emp_name = #{name}
    6. when>
    7. <when test="salary !=null">
    8. and emp_salary = #{salary}
    9. when>
    10. <otherwise>1=1otherwise>
    11. choose>
    12. select>

    5. foreach标签

     Mapper 接口中的抽象方法:

    1. //根据id进行批量查询
    2. List queryBatch(@Param("ids") List ids);
    3. //批量插入
    4. int insertBatch(@Param("list") List employeeList);
    5. //批量更新
    6. int updateBatch(@Param("list") List employeeList);

     SQL语句:

    1. <select id="queryBatch" resultType="employee">
    2. select * from t_emp where emp_id in
    3. <foreach collection="ids" open="(" separator="," close=")" item="id">
    4. #{id}
    5. foreach>
    6. select>
    7. <insert id="insertBatch">
    8. insert into t_emp(emp_name,emp_salary) values
    9. <foreach collection="list" separator="," item="employee">
    10. (#{employee.empName},#{employee.empSalary})
    11. foreach>
    12. insert>
    13. <update id="updateBatch">
    14. <foreach collection="list" item="employee" separator=";">
    15. update t_emp set emp_name = #{employee.empName},emp_salary = #{employee.empSalary}
    16. where emp_id = #{employee.empId}
    17. foreach>
    18. update>

    注意:

    ① 上述批量查询和批量插入,本质上都是一条 sql 语句执行。

    而批量更新,事实上是一次性执行多条 sql 语句,中间用分号隔开。

    当一次性发送多条SQL语句让数据库执行,此时需要在数据库连接信息的URL地址中设置:

    <property name="url" value="jdbc:mysql://localhost:3306/mybatis-example?allowMultiQueries=true"/>

     ② 关于 foreach 标签的 collection 属性:

    如果没有给接口中 List 类型的参数使用 @Param 注解指定一个具体的名字,那么默认可以使用collection 或 list 或 arg0 来引用要遍历的集合。

    但在实际开发中,为了避免隐晦的表达造成一定的误会,建议使用 @Param 注解明确声明变量的名称,然后 collection 属性使用注解指定的名称。

    6. sql 片段提取

    1. <sql id="selectSql">
    2. select * from t_emp
    3. sql>
    4. <select id="query" resultType="employee">
    5. <include refid="selectSql"/>
    6. <where>
    7. <if test="name!=null">
    8. emp_name = #{name}
    9. if>
    10. <if test="salary !=null and salary > 0">
    11. and emp_salary = #{salary}
    12. if>
    13. where>
    14. select>

    五、Mybatis 高级扩展

    1. Mapper 批量映射优化

    Mapper 配置文件很多时,在全局配置文件中一个一个注册太麻烦,我们希望可以批量注册。

    配置方式:

    1. <mappers>
    2. <package name="com.mihoyo.mapper"/>
    3. mappers>

    注意:

    批量 mapper 配置文件指定,package = “Mapper 接口 和 Mapper.xml 打包后共同所在的包”

    所以,必要满足 2 个要求:

            ① 要求 Mapper 接口 和 Mapper.xml 的命名必须相同

            ② 要求 Mapper 接口 和 Mapper.xml 的所在的包名必须相同(最终打包的位置相同)

                    方案一:xml 文件也加入到接口所在包,并且 pom 文件中在 build 中配置 resources,来防止我们资源导出失败的问题

    1. <build>
    2. <resource>
    3. <directory>src/main/javadirectory>
    4. <includes>
    5. <include>**/*.propertiesinclude>
    6. <include>**/*.xmlinclude>
    7. includes>
    8. <filtering>falsefiltering>
    9. resource>
    10. resources>
    11. build>

                    方案二:resource 文件夹也创建和 mapper 接口一样的文件夹结构

    细节:resources 目录下创建多级目录,用 / 分割,而不是 . (即 com/mihoyo/mapper )。

    2. 插件和分页插件 PageHelper

    (1)插件机制

    MyBatis 对插件进行了标准化的设计,并提供了一套可扩展的插件机制。

    插件可以在用于语句执行过程中进行拦截,并允许通过自定义处理程序来拦截和修改 SQL 语句、映射语句的结果等。

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

    Interceptor(拦截器):定义一个拦截方法 intercept,该方法在执行 SQL 语句、执行查询、查询结果的映射时会被调用。

    Invocation(调用):实际上是对被拦截的方法的封装,封装了Object target、Method method 和 Object[] args 这三个字段。

    InterceptorChain(拦截器链):对所有的拦截器进行管理,包括将所有的链接成一条链,并在执行 SQL 语句时按顺序调用。

    (2)PageHelper 插件使用

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

    a. pom.xml 引入依赖

    1. <dependency>
    2. <groupId>com.github.pagehelpergroupId>
    3. <artifactId>pagehelperartifactId>
    4. <version>5.1.11version>
    5. dependency>

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

    1. <plugins>
    2. <plugin interceptor="com.github.pagehelper.PageInterceptor">
    3. <property name="helperDialect" value="mysql"/>
    4. plugin>
    5. plugins>

    c. mapper接口 和 mapper.xml 

    1. public interface EmployeeMapper {
    2. List queryList();
    3. }
    1. <mapper namespace="com.mihoyo.mapper.EmployeeMapper">
    2. <select id="queryList" resultType="employee">
    3. select * from t_emp where emp_salary > 100
    4. select>
    5. mapper>

    注意:sql 语句末尾不用加 ; 

    d. 分页插件使用

    1. //使用分页插件
    2. @Test
    3. public void test_01() {
    4. EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
    5. //调用方法之前,先设置分页数据(当前是第几页,每页显示多少数据)
    6. PageHelper.startPage(2,2);
    7. List list = mapper.queryList();
    8. //将查询数据封装到一个 PageInfo 的分页实体类中
    9. PageInfo pageInfo = new PageInfo<>(list);
    10. //从 pageInfo 中获取分页的数据
    11. List list1 = pageInfo.getList();//获取当前页的数据
    12. System.out.println("list1 = " + list1);
    13. int pages = pageInfo.getPages();//获取总页数
    14. System.out.println("pages = " + pages);
    15. long total = pageInfo.getTotal();//获取总条数
    16. System.out.println("total = " + total);
    17. int pageNum = pageInfo.getPageNum();//获取当前页数
    18. System.out.println("pageNum = " + pageNum);
    19. int pageSize = pageInfo.getPageSize();//获取每页容量
    20. System.out.println("pageSize = " + pageSize);
    21. //...
    22. }

    注意:不能将两条查询装到一个分页区(PageInfo)。

    运行结果:

     3. 逆向工程和MybatisX插件

    (1)ORM思维介绍

    ORM(Object-Relational Mapping,对象-关系映射)是一种将数据库和面向对象编程语言中的对象之间进行转换的技术。

    它将对象和关系数据库的概念进行映射,最后我们就可以通过方法调用进行数据库操作。

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

    ORM 框架的两种方式:

    半自动 ORM :通常需要程序员手动编写 SQL 语句或者配置文件,将实体类和数据表进行映射,还需要手动将查询的结果集转换成实体对象。

    全自动 ORM :将实体类和数据表进行自动映射,使用 API 进行数据库操作时,ORM 框架会自动执行 SQL 语句并将查询结果转换成实体对象,程序员无需再手动编写 SQL 语句和转换代码。

    (2)逆向工程

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

    简单来说:逆向工程使得半自动 orm 框架,也能实现单表的 crud 自动生成,向全自动 orm 迈进!

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

     (3)逆向工程插件 MyBatisX 使用

    MyBatisX 是一个 MyBatis 的代码生成插件,可以通过简单的配置和操作快速生成 MyBatis Mapper、pojo 类和 Mapper.xml 文件。

    步骤:

    ① 安装插件

    ② 使用 IntelliJ IDEA连接数据库

    a. 连接数据库

     

     b. 填写信息

    c. 展示库表

     

    d. 逆向工程使用

     

    ③ 查看生成结果

     

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

  • 相关阅读:
    【Linux进程篇】进程地址空间
    从lc560“和为 K 的子数组“带你认识“前缀和+哈希表“的解题思路
    论文阅读【3】Efficient Estimation of Word Representations in Vector Space
    rust &String 和 &str 区别
    linux-sed命令使用方法记录
    Cyanine5-N3细胞成像和显微镜CY5-N3/azide星戈瑞
    luogu-P1462 通往奥格瑞玛的道路 && ybt-修建道路【最短路+二分】
    docker 中给命令起别名
    Bubble Fish Park
    C++类与结构体、this指针(二)
  • 原文地址:https://blog.csdn.net/xpy2428507302/article/details/142068272