Mybatis笔记
Published on 2022-05-12 11:27 in 暂未分类 with 有空就一起吃饭吧

Mybatis笔记

    Mybatis框架

    第一章 初识Mybatis

    1.1 框架概述

    • 生活中“框架”
      • 买房子【毛坯房】
      • 手抓饼【半成品】
    • 程序中框架【代码半成品】
      • Mybatis:持久化层框架【dao层书写JDBC代码(BaseDao&JDBCUtils)】
      • SpringMVC:控制层【表述层、表示层】
      • Spring:全能【大管家】

    1.2 Mybatis简介

    • MyBatis是Apache的一个开源项目iBatis【Mybatis前身IBatis】
    • Mybatis是一个半自动持久层ORM框架
    • ORM:Object Relational Mapping【对象关系映射】
      • 将Java中对象与数据库中表建立映射关系,优势:操作Java中对象,就可以影响表中数据。
    • Mybatis与JDBC对比
      • JDBC中的SQL与Java代码相耦合
      • Mybatis将SQL与Java代码解耦
    • Mybatis与Hibernate对比
      • Mybatis是一个半自动化ORM持久化层框架【需要手写SQL】
      • Hibernate是一个全自动化ORM持久化层框架【无需手写SQL】
    • Java POJO(Plain Old Java Objects,普通老式 Java 对象)
      • JavaWeb:JavaBean
      • SSM:POJO

    1.3 Mybatis官方网址

    第二章 Mybatis框架搭建

    2.1 搭建框架步骤

    1. 导入jar包
    2. 编写配置文件
    3. 使用框架核心类库

    2.2 搭建Mybatis框架步骤

    • 准备

      • 建库建表
      • 准备maven工程
    • 搭建环境

      • 导入jar包

        <!--导入MyBatis的jar包-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.6</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.26</version>
        </dependency>
        <!--junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        
      • 编写核心配置文件

        • 名称:mybatis-config.xml

        • 位置:src/main/resources

        • 示例代码

          <?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 default="development">
                  <environment id="development">
                      <transactionManager type="JDBC"/>
                      <dataSource type="POOLED">
                          <property name="driver" value="com.mysql.jdbc.Driver"/>
                          <property name="url" value="jdbc:mysql://localhost:3306/db220212"/>
                          <property name="username" value="root"/>
                          <property name="password" value="root"/>
                      </dataSource>
                  </environment>
              </environments>
              <!--    加载映射文件路径-->
              <mappers>
                  <mapper resource="mapper/EmployeeMapper.xml"/>
              </mappers>
          </configuration>
          
      • 编写映射文件及相关接口【Dao->Mapper】

        • 名称:接口名一致【XXXMapper.xml】

        • 位置:src/main/resouces/mapper/

        • 作用:为接口映射SQL语句,有以下三点注意事项

          1. 映射文件名称与接口名称一致
          2. 映射文件namespace与接口全类名一致
          3. 映射文件SQL的ID与接口中方法名一致
        • 示例代码

          <?xml version="1.0" encoding="UTF-8" ?>
          <!DOCTYPE mapper
                  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
          <mapper namespace="com.atguigu.mapper.EmployeMapper">
              <!--    定义查询语句SQL
                          预期希望查询结果集,返回类型:
              -->
            <select id="selectEmpById" resultType="com.atguigu.pojo.Employee">
                  SELECT
                      id,
                      last_name lastName,
                      email,
                      salary
                  FROM
                      tbl_employee
                  WHERE
                      id = #{id}
            </select>
          </mapper>
          
      • 测试【SqlSession】

        • 先newSqlSessionFacotry工厂对象
        • 再通过SqlSessionFactory获取SqlSession对象
        • 通过SqlSession对象获取EmployeeMapper代理对象【实现类】
        • 测试

    2.3 搭建Mybatis框架环境常见问题

    • 不能找到核心配置文件错误:java.io.IOException: Could not find resource mybati-config.xml

      • 解决方案
        1. 检查文件名称及位置
        2. 使用maven的clean及compile重新编译
    • 不能找到映射文件错误:Caused by: java.io.IOException: Could not find resource mapper/EmployeeMaper.xml

      • 解决方案:检查核心配置文件【mybatis-config.xml中mapper是否正确】
    • 绑定异常,statement not found:org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.atguigu.mapper.EmployeMapper.selectEmpById

      • 解决方案:检查【三个一致】问题

    第三章 Log4j日志框架

    • 导入log4j的jar包

      <!-- https://mvnrepository.com/artifact/log4j/log4j -->
      <dependency>
          <groupId>log4j</groupId>
          <artifactId>log4j</artifactId>
          <version>1.2.17</version>
      </dependency>
      
    • 编写配置文件

      • 名称:log4j.xml

      • 位置:src/main/resources

      • 示例代码

        <?xml version="1.0" encoding="UTF-8" ?>
        <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
         
        <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
         
         <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
           <param name="Encoding" value="UTF-8" />
           <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m  (%F:%L) \n" />
           </layout>
         </appender>
         <logger name="java.sql">
           <level value="debug" />
         </logger>
         <logger name="org.apache.ibatis">
           <level value="info" />
         </logger>
         <root>
           <level value="debug" />
           <appender-ref ref="STDOUT" />
         </root>
        </log4j:configuration>
        

    第四章 Mybatis核心配置文件详解

    4.1 概述

    • MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息 ,配置文档的顶层结构如下:

    image-20220415142357242

    4.2 核心配置文件详解

    • 根标签:configuration,所有子标签均需要书写在根标签内部

    • 子标签

      • properties属性标签

        • 语法:

          <properties resource="db.properties"></properties>
          
        • 作用:定义property属性,也可以引入外部Properties属性文件。

        • 示例代码

          <properties resource="db.properties"></properties>
          <!--    设置数据库连接环境-->
          <environments default="development">
              <environment id="development">
                  <transactionManager type="JDBC"/>
                  <dataSource type="POOLED">
                      <property name="driver" value="${db.driverClassName}"/>
                      <property name="url" value="${db.url}"/>
                      <property name="username" value="${db.username}"/>
                      <property name="password" value="${db.password}"/>
                  </dataSource>
              </environment>
          </environments>
          
      • settings子标签

        • 作用:这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。
        • 属性
          • mapUnderscoreToCamelCase:开启驼峰命名自动映射
            • false:关闭【默认值】
            • true:开启
          • 注意:只能将a_col与aCol自动映射,不能将a_col与aCo自动映射
      • typeAliases子标签

        • 作用:类型别名可为 Java 类型设置一个缩写名字。【为POJO定义别名】

        • 语法

          <typeAliases>
          <!--        为指定类设置别名-->
          <!--        <typeAlias type="com.atguigu.pojo.Employee" alias="employee"></typeAlias>-->
          <!--        为当前包下所有类,设置别名【类名首字母小写作为别名】-->
                  <package name="com.atguigu.pojo"/>
              </typeAliases>
          
        • Mybatis自定义别名

          别名 类型
          _int int
          _double double
          int或integer Integer
          list List
          map Map
      • environments子标签:环境配置

        • 作用:设置多个数据库连接环境

        • 语法

          <!--    设置数据库连接环境-->
              <environments default="development2">
                  
                  <environment id="development">
                      <!-- 设置事务管理器-->
                      <transactionManager type="JDBC"/>
                      <!-- 设置连接池-->
                      <dataSource type="POOLED">
                          <property name="driver" value="${db.driverClassName}"/>
                          <property name="url" value="${db.url}"/>
                          <property name="username" value="${db.username}"/>
                          <property name="password" value="${db.password}"/>
                      </dataSource>
                  </environment>
              </environments>
          
      • mappers子标签:映射器

        • 作用:设置映射文件路径

        • 语法

          <!--    加载映射文件路径-->
          <mappers>
              <mapper resource="mapper/EmployeeMapper.xml"/>
              <!-- 老版本使用【要求:配置文件与接口必须在同一个包下】-->
              <!-- <package name="com.atguigu.mapper"/>-->
          </mappers>
          
    • 总结:核心配置文件中子标签,是有顺序要求的,顺序参考官网文档即可!

    第五章 Mybatis映射文件详解

    5.1 映射文件概述

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

    5.2 映射文件标签详解

    • 根标签mapper标签

      • 语法

        <mapper namespace="com.atguigu.mapper.EmployeMapper">
        
      • mapper中的namespace要求与接口的全类名一致

    • 子标签

      映射文件共有9个子标签,需要掌握如下8个子标签

      • insert标签:定义添加SQL语句
      • delete标签:定义删除SQL语句
        • 删除共分为两种
          • 物理删除:真实删除数据库中数据
          • 逻辑删除:不删除数据库中数据【不显示】
      • update标签:定义修改SQL语句
      • select标签:定义查询SQL语句
      • sql标签:定义SQL片段
      • resultMap标签:自定义映射,resultType解决不了的问题交给resultMap
      • cache标签:定义当前命名空间【名称空间】的缓存配置
      • cache-ref标签:定义当前命名空间【名称空间】的缓存因为

    5.3 子标签中常用属性

    • resultType:设置期望结果集返回类型【类型全类名或别名】
      • 注意
        • 如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。
        • resultTyperesultMap 之间只能同时使用一个。

    day03

    第六章 获取主键自增及数据库受影响行数

    6.1 获取主键自增数据【一般指的是id】

    • useGeneratedKeys:true

    • 开启主键生成策略

    • keyProperty:设置生成数据存储对象属性

      <insert id="insertEmp" keyProperty="id" useGeneratedKeys="true">
          INSERT INTO tbl_employee(last_name,email,salary)
          VALUES(#{lastName},#{email},#{salary})
      </insert>
      

    6.2 获取数据库受影响行数

    • 将接口返回值设置int或boolean即可

    • 示例代码

      /**
       * 添加员工信息
       */
      public boolean insertEmp(Employee employee);
      /**
       * 通过id修改员工其他信息
       */
      public int updateEmp(Employee employee);
      

    第七章 Mybatis中参数传递问题【重点】

    7.1 单个普通参数

    • Mybatis中传递单个参数时,是可以任意入参的

      • 任意入参,指的是任意参数名及参数类型
    • 示例代码

      /**
       * 通过员工Id获取员工信息
       */
      public Employee selectEmpById(int empId);
      
      <select id="selectEmpById" resultType="employee">
          SELECT
              id,
              last_name,
              email,
              salary
          FROM
              tbl_employee
          WHERE
              id = #{empId}
      </select>
      

    7.2 多个普通参数

    • Mybatis中传递多个普通参数时,不能任意入参,如任意使用会报如下错误

      Caused by: org.apache.ibatis.binding.BindingException: Parameter 'empId' not found. Available parameters are [arg1, arg0, param1, param2]

    • Mybatis中传递多个普通参数时,底层会将参数封装为一个Map,Map的key是【param1、param2、...】和

      【arg0、arg1、...】

    • 示例代码

      /**
       * 通过员工id和姓名获取员工信息
       * @param empId
       * @param lastName
       */
      public Employee selectEmpByIdAndLastName(int empId,String lastName);
      
      <select id="selectEmpByIdAndLastName" resultType="employee">
          SELECT
              id,
              last_name,
              email,
              salary
          FROM
              tbl_employee
          WHERE
              id = #{arg0}
          AND
              last_name = #{arg1}
      </select>
      

    7.3 POJO参数

    • Mybatis中POJO入参时,使用POJO中属性直接入参即可

    • 示例代码

      /**
       * 通过员工id和姓名获取员工信息
       * @param employee
       */
      public Employee selectEmpByOpr(Employee employee);
      
      <select id="selectEmpByOpr" resultType="employee">
          SELECT
              id,
              last_name,
              email,
              salary
          FROM
              tbl_employee
          WHERE
              id = #{id}
          AND
              last_name = #{lastName}
      </select>
      

    7.4 命名参数

    • 语法:@Param

    • Mybatis中使用@Param注解为参数命名,底层将参数封装为Map,key是命的名【命名参数支持param1,param2,...】

    • 示例代码

      /**
       * 通过员工id和姓名获取员工信息
       */
      public Employee selectEmpByNamedParam(@Param(value = "empId") int empId,
                                            @Param("lastName") String lastName);
      
      <!--    测试命名参数-->
      <select id="selectEmpByNamedParam" resultType="employee">
           SELECT
              id,
              last_name,
              email,
              salary
          FROM
              tbl_employee
          WHERE
              id = #{empId}
          AND
              last_name = #{lastName}
      </select>
      
    • 源码解析

      image-20220416104742168

      public static class ParamMap<V> extends HashMap<String, V> {}
      

    7.5 Map参数

    • Mybatis中可以支持Map入参,map的key就是参数key

    • 示例代码

      /**
       * 通过员工id和姓名获取员工信息
       * @param map
       * @return
       */
      public List<Employee> selectEmpByMap(Map<String,Object> map);
      
      <select id="selectEmpByMap" resultType="employee">
          SELECT
              id,
              last_name,
              email,
              salary
          FROM
              tbl_employee
          WHERE
              id = #{empId}
          AND
              last_name = #{lName}
      </select>
      
      Map<String,Object> map = new HashMap<>();
      map.put("empId",1);
      map.put("lName","cuicui");
      
      List<Employee> employees = mapper.selectEmpByMap(map);
      for (Employee employee : employees) {
          System.out.println("employee = " + employee);
      }
      

    7.6 List|Array|Collection等

    • Mybatis支持List、Array、Collection等入参,key分别是【list,array,collection(list)】

    第八章 #与$区别【面试题】

    8.1 回顾JDBC底层对象

    • DriverManager
    • Connection
    • Statement:执行SQL语句,入参使用SQL拼接【字符串拼接】方式
    • PreparedStatement:执行SQL语句,入参使用预编译SQL方式【占位符】
    • ResultSet

    8.2 #{}与${}区别

    • {}:底层执行SQL语句,使用PreparedStatement,预编译SQL,防止SQL注入安全隐患,相对比较安全

    • ${}:底层执行SQL语句,使用Statement,字符串拼接SQL,未防止SQL注入安全隐患,相对不安全

    8.3 #与$使用场景

    • 单表查询语句:select col,col2,... from table1 where col=? and col2=? group by ?,order by? limit ?,?

    • 【#{}】使用场景:SQL语句中占位符(?)位置均可以使用#{}

    • {}】使用场景:#解决不了的问题时,使用

      • 如:查询表中数据【动态化表名】

      • 示例代码

        /**
         * 动态查询表名
         * @return
         */
        public List<Employee> selectEmpByDynamicTable(@Param("tblName") String tblName);
        
        <select id="selectEmpByDynamicTable" resultType="employee">
            SELECT id,last_name,email,salary FROM ${tblName}
        </select>
        

    第九章 Mybatis中返回结果四种情况

    9.1 查询单行数据返回单个对象

    /**
     * 查询单行数据返回单个对象
     */
    public Employee selectEmpById(int empId);
    
    <select id="selectEmpById" resultType="employee">
        select
            id,
            last_name,
            email,
            salary
        from
            tbl_employee
        where
            id = #{empId}
    </select>
    

    9.2 查询多行数据返回对象集合

    /**
     * 查询所有员工信息【查询多行数据返回对象集合】
     */
    public List<Employee> selectAllEmps();
    
    <select id="selectAllEmps" resultType="employee">
        select
            id,
            last_name,
            email,
            salary
        from
            tbl_employee
    </select>
    

    9.3 查询单行数据返回Map

    • Mybatis底层返回结果:字段名作为Map的key,查询结果作为Map的value

    • 示例代码

      /**
       * 通过id获取员工信息【查询单行数据返回Map】
       */
      public Map<String,Object> selectEmpRetMap(int empId);
      
      <select id="selectEmpRetMap" resultType="map">
          select
              id,
              last_name,
              email,
              salary
          from
              tbl_employee
          where
              id = #{empId}
      </select>
      

    9.4 查询多行数据返回Map

    • 注意:Map<Integer,Employee>

      • key:empId
      • value:employee
      • 必须使用@MapKey指定Map的key是Employee中的哪个属性
    • 示例代码

      /**
           * 查询所有员工信息【查询多行数据返回Map】
           * map
           *  key:empId
           *  value:employee
           */
      //    public List<Map<String,Object>> selectAllEmpsReturnMap();
          @MapKey("id")
          public Map<Integer,Employee> selectAllEmpsReturnMap();
      
      <select id="selectAllEmpsReturnMap" resultType="map">
          select
              id,
              last_name,
              email,
              salary
          from
              tbl_employee
      </select>
      

    第十章 自动映射及自定义映射【重点】

    10.1 自动映射与自定义映射

    • 自动映射【resultType】

      • 概述:指的是可以自动将类中的属性与表中字段进行关联映射

      • 自动映射无法解决问题

        • 多表连接查询时,需要返回多张表的结果集时,此时resultType解决不了

          image-20220416151726394

        • 单表查询时,不支持驼峰式命名映射【不为字段定义别名】,此时resultType解决不了

    • 自定义映射【resultMap】

      • 概述:程序员自己定义字段与属性映射关系,称之为自定义映射【自动映射解决不了问题,交给自定义映射解决】
    • 注意:自动映射与自定义映射只能同时使用一个

    10.2 Mybatis中自定义映射【级联映射】

    /**
     * 通过员工Id获取员工信息及员工所属部门信息
     */
    public Employee selectEmpAndDeptByEmpId(int empId);
    
    <!--    自定义映射-->
    <resultMap id="empAndDeptResultMap" type="employee">
        <!--id标签: 定义表中的【主键】字段与类中属性关联关系-->
        <id property="id" column="id"></id>
        <!--result标签: 定义表中的【非主键】字段与类中属性关联关系-->
        <result property="lastName" column="last_name"></result>
        <result property="email" column="email"></result>
        <result property="salary" column="salary"></result>
        <result property="dept.deptId" column="dept_id"></result>
        <result property="dept.deptName" column="dept_name"></result>
    </resultMap>
    <select id="selectEmpAndDeptByEmpId" resultMap="empAndDeptResultMap">
        SELECT
            e.`id`,
            e.`last_name`,
            e.`email`,
            e.`salary`,
            d.`dept_id`,
            d.`dept_name`
        FROM
            tbl_employee e,
            tbl_dept d
        WHERE
            e.`dept_id` = d.`dept_id`
        AND
            e.`id` = #{empId}
    </select>
    

    image-20220416154725606

    10.3 Mybatis中自定义映射【association映射(一对一或多对一)】

    /**
     * 通过员工Id获取员工信息及员工所属部门信息【association映射】
     */
    public Employee selectEmpAndDeptByEmpIdass(int empId);
    
    <!--    association自定义映射-->
    <resultMap id="empAndDeptResultMapAssociation" type="employee">
        <id property="id" column="id"></id>
        <result property="lastName" column="last_name"></result>
        <result property="email" column="email"></result>
        <result property="salary" column="salary"></result>
        <!--association自定义映射-->
        <association  property="dept"
                        javaType="com.atguigu.pojo.Dept">
            <id property="deptId" column="dept_id"></id>
            <result property="deptName" column="dept_name"></result>
        </association>
    </resultMap>
    <select id="selectEmpAndDeptByEmpIdass" resultMap="empAndDeptResultMapAssociation">
        SELECT
            e.`id`,
            e.`last_name`,
            e.`email`,
            e.`salary`,
            d.`dept_id`,
            d.`dept_name`
        FROM
            tbl_employee e,
            tbl_dept d
        WHERE
            e.`dept_id` = d.`dept_id`
        AND
            e.`id` = #{empId}
    </select>
    
    • association自定义映射【分步查询】
    • association自定义映射【延迟查询】

    10.4 Mybatis中自定义映射【collection映射(一对多)】

    /**
     * 通过部门id获取部门信息,及部门所属员工信息
     */
    public Dept selectDeptAndEmpByDeptId(int deptId);
    
    <resultMap id="deptAndEmpResultMap" type="dept">
        <id property="deptId" column="dept_id"></id>
        <result property="deptName" column="dept_name"></result>
        <!-- 定义一对多关联映射-->
        <collection property="empList"
                    ofType="com.atguigu.pojo.Employee">
            <id property="id" column="id"></id>
            <result property="lastName" column="last_name"></result>
            <result property="email" column="email"></result>
            <result property="salary" column="salary"></result>
        </collection>
    
    </resultMap>
    <select id="selectDeptAndEmpByDeptId" resultMap="deptAndEmpResultMap">
        SELECT
            e.`id`,
            e.`last_name`,
            e.`email`,
            e.`salary`,
            d.`dept_id`,
            d.`dept_name`
        FROM
            tbl_employee e,
            tbl_dept d
        WHERE
            e.`dept_id` = d.`dept_id`
        AND
            d.dept_id = #{deptId}
    </select>
    

    10.5 分步查询

    • 为什么分步查询

      • 提高程序性能
    • association分步查询

      /**
       * association分步查询
           *  通过员工Id获取员工信息及员工所属部门信息【多表连接查询】
                 1. 通过员工Id获取员工信息【dept_id】【单表】
                 2. 通过部门Id获取部门信息【单表】
       */
      public Employee selectEmpAndDeptByEmpIdAssociationStep(int empId);
      
      <!--    association分步查询-->
      <resultMap id="empAndDeptRmAssociationStep" type="employee">
          <id property="id" column="id"></id>
          <result property="lastName" column="last_name"></result>
          <result property="email" column="email"></result>
          <result property="salary" column="salary"></result>
          <!-- Association分步查询-->
          <association property="dept"
                      select="com.atguigu.mapper.DeptMapper.selectDeptByDeptId"
                      column="dept_id">
          </association>
      </resultMap>
      <select id="selectEmpAndDeptByEmpIdAssociationStep" resultMap="empAndDeptRmAssociationStep">
          select
              id,
              last_name,
              email,
              salary,
              dept_id
          from
              tbl_employee
          where
              id = #{empId}
      </select>
      
      /**
       * 通过部门Id获取部门信息【单表】
       */
      public Dept selectDeptByDeptId(int deptId);
      
      <select id="selectDeptByDeptId" resultType="dept">
          select
              dept_id,
              dept_name
          from
              tbl_dept
          where
              dept_id=#{deptId}
      </select>
      
    • collection分步查询

      /**
       * 通过部门id获取部门信息,及部门所属员工信息【分步查询】
              1. 通过部门id获取部门信息【单表】
              2. 通过部门id获取员工信息【单表】
       */
      public Dept selectDeptAndEmpByDeptIdStep(int deptId);
      
      <!--    定义collection分步查询-->
      <resultMap id="deptAndEmpRmStep" type="dept">
          <id property="deptId" column="dept_id"></id>
          <result property="deptName" column="dept_name"></result>
          <collection property="empList"
                      select="com.atguigu.mapper.EmployeeMapper.selectEmpByDeptId"
                      column="dept_id"
                      fetchType="lazy">
      
          </collection>
      </resultMap>
      <select id="selectDeptAndEmpByDeptIdStep" resultMap="deptAndEmpRmStep">
          select
              dept_id,
              dept_name
          from
              tbl_dept
          where
              dept_id=#{deptId}
      </select>
      
      /**
       * 2. 通过部门id获取员工信息
       */
      public List<Employee> selectEmpByDeptId(int deptId);
      
      <select id="selectEmpByDeptId" resultType="employee">
          select
              id,
              last_name,
              email,
              salary,
              dept_id
          from
              tbl_employee
          where
              dept_id = #{deptId}
      </select>
      

    10.6 resultMap标签详解

    • resultMap属性
      • id:定义resultMap唯一标识
      • type:定义resultMap类型
    • resultMap子标签
      • id标签:定义表中的【主键】字段与类中属性关联关系
      • result标签:定义表中的【非主键】字段与类中属性关联关系
      • association标签:自定义映射
        • 属性
          • property:定义关联属性
          • javaType:定义关联属性对应java类型或别名
          • select:定义分步查询Sql的Id
          • column:定义分步查询Sql的参数
          • fetchType:局部延迟加载开关
            • lazy:开启局部延迟加载
            • eager:关闭局部延迟加载
        • 子标签
          • id标签:与resultMap的id子标签一致
          • result标签:与resultMap的result子标签一致
      • collection标签
        • 属性
          • property:定义关联属性
          • ofType:定义关联属性对应java类型或别名
          • select:定义分步查询Sql的Id
          • column:定义分步查询Sql的参数
          • fetchType:局部延迟加载开关
            • lazy:开启局部延迟加载
            • eager:关闭局部延迟加载

    day04

    第十一章 Mybatis延迟加载【懒加载】

    11.1 延迟加载概述

    • 前提:在分步查询基础上

    • 延迟加载:使用数据时加载,不使用数据时不加载

      • 使用数据,指的是第二步查询的数据

    11.2 延迟加载语法

    • 全局设置

      <settings>
      	<!-- 开启延迟加载 -->
      	<setting name="lazyLoadingEnabled" value="true"/>
      	<!-- 设置加载的数据是按需加载【3.4.1及以前版本,默认值:true】-->
      	<setting name="aggressiveLazyLoading" value="false"/>
      </settings>
      
    • 局部设置【fetchType】

      • 属性位置

        • association标签内部或collection标签内部
      • 属性数值

        • eager:关闭局部延迟加载
        • lazy【默认值】:开启局部延迟加载
      • 示例代码

        <association property="dept"
                     select="com.atguigu.mapper.DeptMapper.selectDeptByDeptId"
                     column="dept_id"
                     fetchType="eager">
        </association>
        <collection property="empList"
                    select="com.atguigu.mapper.EmployeeMapper.selectEmpByDeptId"
                    column="dept_id"
                    fetchType="lazy">
        
        </collection>
        

    11.3 扩展

    • 如果分步查询时,需要多个参数,需要将多个参数封装为map结构即可
      • 语法:

    第十二章 Mybatis动态SQL【重点】

    12.1 动态SQL概述

    • 动态SQL:指的是SQL语句可动态变化

      • 概述

        /**
         * 通过条件查询员工信息【条件不确定】
                 private Integer id;         //员工id
                 private String lastName;    //员工姓名
                 private String email;       //员工邮箱
                 private Double salary;      //员工薪资
            Employee对象中属性不为空,就添加到查询条件中
         id not null->sql:          select id,last_name,email,salary from tbl_employee where id=#{id}
         lastName not null -> sql   select id,last_name,email,salary from tbl_employee where last_name=#{lastName}
         */
        public List<Employee> selectEmpByDynamicOpr(Employee employee);
        
      • Mybatis中不支持方法重载

        • 因为:Mybatis规定方法名与SQL的Id一致【SQL的Id不能重复】导致了方法不能重载
      • Mybatis映射文件中注释

        • 推荐使用:
        • 不推荐使用:--
    • 动态数据:使用Thymeleaf动态渲染html【静态页面】

    • Mybatis动态SQL中支持:OGNL

      • OGNL( Object Graph Navigation Language )对象图导航语言,这是一种强大的

        表达式语言,通过它可以非常方便的来操作对象属性。 类似于我们的EL,SpEL等

    12.2 动态SQL常用标签

    • if标签:主要应用于基本if判断

    • where标签:解决where关键字及多出前面第一个and或or关键字问题

    • trim标签:可以在条件判断完的SQL语句前后添加或者去掉指定的字符

      • prefix: 添加前缀

      • prefixOverrides: 去掉前缀

      • suffix: 添加后缀

      • suffixOverrides: 去掉后缀

    • choose标签:主要是用于分支判断,类似于java中的if-else【switch case】,只会满足所有分支中的一个

    • set标签:解决set关键字及多出一个逗号【,】问题

    • foreach标签:类似java中for循环

      • collection: 要迭代的集合

      • item: 当前从集合中迭代出的元素

      • open: 开始字符

      • close:结束字符

      • separator: 元素与元素之间的分隔符

      • index:

        • 迭代的是List集合: index表示的当前元素的下标

        • 迭代的Map集合: index表示的当前元素的key

    • sql标签:定义SQL片段

    12.3 示例代码

    <!--    通过条件查询员工信息【动态SQL】-->
    <select id="selectEmpByDynamicOpr" resultType="employee">
        select
            id,
            last_name,
            email,
            salary,
            dept_id
        from
            tbl_employee
        <where>
            <!-- 1=1 -->
            <if test="id != null">
                id = #{id}
            </if>
            <if test="lastName != null">
                and last_name = #{lastName}
            </if>
            <if test="email != null">
                and email = #{email}
            </if>
            <if test="salary != null">
                and salary = #{salary}
            </if>
        </where>
    </select>
    
    <!--    测试trim-->
    <select id="selectEmpByDynamicTrim" resultType="employee">
        select
            id,
            last_name,
            email,
            salary,
            dept_id
        from
            tbl_employee
        <trim prefix="where" suffixOverrides="and">
            <if test="id != null">
                id = #{id} and
            </if>
            <if test="lastName != null">
                last_name = #{lastName} and
            </if>
            <if test="email != null">
                email = #{email} and
            </if>
            <if test="salary != null">
                salary = #{salary}
            </if>
        </trim>
    </select>
    <!--    测试choose-->
    <select id="selectEmpByDynamicChoose" resultType="employee">
        select
            id,
            last_name,
            email,
            salary,
            dept_id
        from
            tbl_employee
        <where>
            <choose>
                <when test="id!=null">
                    id=#{id}
                </when>
                <when test="lastName!=null">
                    last_name=#{lastName}
                </when>
                <when test="email!=null">
                    email=#{email}
                </when>
                <when test="salary!=null">
                    salary=#{salary}
                </when>
                <otherwise>
                    1=1
                </otherwise>
            </choose>
        </where>
    </select>
    
    <update id="updateEmp">
            update
                tbl_employee
            <set>
                <if test="lastName != null">
                    last_name = #{lastName},
                </if>
                <if test="email != null">
                    email = #{email},
                </if>
                <if test="salary != null">
                    salary = #{salary}
                </if>
            </set>
            where
                id = #{id}
        </update>
    
        <select id="selectEmpsByIds" resultType="employee">
            SELECT
                id,
                last_name,
                email,
                salary,
                dept_id
            FROM
                tbl_employee
            WHERE
                id IN(
                    <foreach collection="ids" item="id" separator=",">
                        #{id}
                    </foreach>
                )
        </select>
    
    
        <insert id="batchInsertEmp">
            INSERT INTO tbl_employee(last_name,email,salary) VALUES
            <foreach collection="emps" item="emp" separator=",">
                (#{emp.lastName},#{emp.email},#{emp.salary})
            </foreach>
        </insert>
    <sql id="col_emp">
            id,
            last_name,
            email,
            salary
        </sql>
        
        <sql id="select_tbl_emp">
            select
               <include refid="col_emp"></include>
            from
                tbl_employee
        </sql>
            
        
        <!--    通过条件查询员工信息【动态SQL】-->
        <select id="selectEmpByDynamicOpr" resultType="employee">
            <include refid="select_tbl_emp"></include>
            <where>
                <!-- 1=1 -->
                <if test="id != null">
                    id = #{id}
                </if>
                <if test="lastName != null">
                    and last_name = #{lastName}
                </if>
                <if test="email != null">
                    and email = #{email}
                </if>
                <if test="salary != null">
                    and salary = #{salary}
                </if>
            </where>
        </select>
    

    第十三章 Mybatis中缓存机制

    13.1 缓存概述

    • 生活中缓存
      • 下载【缓存】音频、视频
        • 节约数据流量
        • 提高播放性能
    • 程序中缓存【Mybatis缓存】
      • 使用缓存机制优势
        • 提高查询效率
        • 降低服务器压力

    13.2 Mybatis中支持三种缓存

    • 一级缓存
    • 二级缓存
    • 第三方缓存

    13.3 Mybatis缓存之一级缓存

    • 概述:一级缓存【也叫本地缓存(SqlSession级别缓存)】

    • 特点

      • 一级缓存不能被关闭,可以清空
      • 一级默认开启状态
    • 缓存原理

      • 第一次获取数据时,先从数据库中获取数据,将数据缓存至一级缓存【key:hashCode+查询的SqlId+编写的sql查询语句+参数(不重复)】
      • 以后再次获取相同数据时,先从一级缓存中获取,如未获取到数据,再从数据库中获取数据。
    • 一级缓存五种失效情况

      1. 不同的SqlSession对应不同的一级缓存

      2. 同一个SqlSession但是查询条件不同

      3. 同一个SqlSession两次查询期间执行了任何一次增删改操作

      • 执行增删改操作后,默认清空一级缓存
      1. 同一个SqlSession两次查询期间手动清空了缓存
      • sqlSession.clearCache();
      1. 同一个SqlSession两次查询期间提交了事务
      • 提交事务,默认清空一级缓存

    13.4 Mybatis中二级缓存

    • 二级缓存概述:二级缓存【(second level cache),全局作用域缓存】,也叫SqlSessionFactory级别缓存

    • 二级缓存特点:

      • 二级缓存默认关闭,需要手动开启才能使用
      • 二级缓存需要提交sqlSession或关闭sqlSession时,才会有效缓存
    • 二级缓存,缓存机制

      • 第一次获取数据时,先从数据库中获取数据,将数据缓存至一级缓存,当提交sqlSession或关闭sqlSession时,将数据缓存至二级缓存
      • 以后再次获取数据时,先从一级缓存中获取数据,如一级缓存中未获取到数据时,再从二级缓存中获取数据,如二级缓存中也未获取到数据,需要去数据库中获取数据,....
    • 二级缓存使用步骤

      ① 全局配置【mybatis-config.xml】文件中开启二级缓存

      ② 需要使用二级缓存的映射文件处使用cache配置缓存

      ③ 注意:POJO需要实现Serializable接口

      image-20220418162517798

      image-20220418162525420

      关闭sqlSession或提交sqlSession时,将数据缓存到二级缓存

    • 二级缓存相关属性

      • eviction:设置缓存回收策略

        • LRU:最近最少使用的,先移除
        • FIFO:先进先出,按照对象进入顺序,去移除对象。
      • flushInternal:设置刷新间隔,单位毫秒

      • size:设置缓存数量,正整数

      • readOnly:设置缓存为只读缓存【不能进行增删改操作】

        <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="false"></cache>
        
    • 二级缓存失效情况

      • 两次查询期间执行了任何一次增删改操作,清空所有缓存
      • sqlSession.clearCache()方法,只能清空一级缓存,无法清空二级缓存

    13.5 第三方缓存插件【EhCache】

    • EhCache概述

      • EhCache是缓存插件
      • EhCache是一个纯Java进程内的缓存框架
    • EhCache使用步骤

      • 导入jar包

        <!-- mybatis-ehcache -->
        <dependency>
            <groupId>org.mybatis.caches</groupId>
            <artifactId>mybatis-ehcache</artifactId>
            <version>1.0.3</version>
        </dependency>
        
        <!-- slf4j-log4j12 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.6.2</version>
            <scope>test</scope>
        </dependency>
        
      • 编写配置文件【ehcache.xml】,位置:src/main/resources

        <?xml version="1.0" encoding="UTF-8"?>
        <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
            <!-- 磁盘保存路径 -->
            <diskStore path="E:\mybatis\ehcache" />
        
            <defaultCache
                    maxElementsInMemory="1"
                    maxElementsOnDisk="10000000"
                    eternal="false"
                    overflowToDisk="true"
                    timeToIdleSeconds="120"
                    timeToLiveSeconds="120"
                    diskExpiryThreadIntervalSeconds="120"
                    memoryStoreEvictionPolicy="LRU">
            </defaultCache>
        
        </ehcache>
        
        <!--
        属性说明:
        l diskStore:指定数据在磁盘中的存储位置。
        l defaultCache:当借助CacheManager.add("demoCache")创建Cache时,EhCache便会采用<defalutCache/>指定的的管理策略
        
        以下属性是必须的:
        l maxElementsInMemory - 在内存中缓存的element的最大数目
        l maxElementsOnDisk - 在磁盘上缓存的element的最大数目,若是0表示无穷大
        l eternal - 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断
        l overflowToDisk - 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上
        
        以下属性是可选的:
        l timeToIdleSeconds - 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大
        l timeToLiveSeconds - 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大
         diskSpoolBufferSizeMB 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区.
        l diskPersistent - 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。
        l diskExpiryThreadIntervalSeconds - 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作
        l memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)
         -->
        
      • 在映射文件中,加载第三方缓存插件【EhcacheCache】

        <cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
        
    • 注意事项

      • 第三方缓存插件,需要开启二级缓存基础上,才能使用第三方缓存
      • 设置二级缓存失效,第三方缓存同时失效

    day05

    第十四章 Mybatis逆向工程

    14.1 逆向工程概述

    • 逆向工程:数据库中表,会影响应用程序中的代码
      • 【表->Java对象(POJO&EmpMapper&EmpMapper.xml)】
    • 正向工程:应用程序中的代码,影响数据库中表的数据【Java对象->表】

    14.2 MBG简介

    • Mybatis Generator:简称MBG
    • 是一个专门为MyBatis框架使用者定制的代码生成器
    • 可以快速的根据表生成对应的映射文件,接口,以及POJO类
    • 支持基本的增删改查,以及QBC风格的条件查询
    • MBG只可以生成单表CRUD操作,多表连接查询、存储过程等复杂SQL定义,任然需要程序员手动编写。

    14.3 MBG使用

    • 导入jar包

      <!-- mybatis-generator-core -->
      <dependency>
          <groupId>org.mybatis.generator</groupId>
          <artifactId>mybatis-generator-core</artifactId>
          <version>1.3.6</version>
      </dependency>
      
    • 编写核心配置文件

      • 名称:【mbg.xml】

      • 位置:src/main/resources

      • 示例代码

        <!DOCTYPE generatorConfiguration PUBLIC
                "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
                "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
        <generatorConfiguration>
            <!--
               id属性:设置一个唯一标识
               targetRuntime属性值说明:
                 MyBatis3Simple:基本的增删改查
                 MyBatis3:带条件查询的增删改查【QBC:Query By Criteria】
            -->
            <context id="simple" targetRuntime="MyBatis3Simple">
                <!--设置连接数据库的相关信息-->
                <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                                connectionURL="jdbc:mysql://localhost:3306/db220212"
                                userId="root"
                                password="root">
                </jdbcConnection>
        
                <!--设置JavaBean【POJO】的生成策略-->
                <javaModelGenerator targetPackage="com.atguigu.mybatis.mbg.pojo" targetProject="src/main/java"/>
        
                <!--设置SQL映射文件的生成策略-->
                <sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources"/>
        
                <!--设置Mapper接口的生成策略-->
                <javaClientGenerator type="XMLMAPPER" targetPackage="com.atguigu.mybatis.mbg.mapper" targetProject="src/main/java"/>
        
                <!--逆向分析的表-->
                <table tableName="tbl_employee" domainObjectName="Employee"/>
                <table tableName="tbl_dept" domainObjectName="Department"/>
        
            </context>
        </generatorConfiguration>
        
    • 执行代码生成器,进行测试

      @Test
      public void testMBG() throws Exception{
      
          List<String> warnings = new ArrayList<String>();
          boolean overwrite = true;
          File configFile = new File("src/main/resources/mbg.xml");
          ConfigurationParser cp = new ConfigurationParser(warnings);
          Configuration config = cp.parseConfiguration(configFile);
          DefaultShellCallback callback = new DefaultShellCallback(overwrite);
          MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
          myBatisGenerator.generate(null);
      
      }
      

    14.4 MBG使用风格

    • 普通风格:targetRuntime="MyBatis3Simple"

      image-20220419103112541

    • QBC风格:targetRuntime="MyBatis3"

      image-20220419103343078

    第十五章 Mybatis分页插件【PageHelper】

    15.1 为什么需要分页

    • 提高用户体验度
    • 降低服务器压力

    15.2 设计分页Page类

    自己实现分页思路

    • 设计业务bean【Page】
      • 47/60 【当前页码/总页数】
      • pageNum:当前页码
      • pages:总页数【计算得来的,总页数=总数据数量/每页显示数据数量】
      • total:总数据数量
      • pageSize:每页显示数据数量
      • List:当前页显示数据集合
    • 设计业务层【service】,不足:不可重用

    15.3 PageHelper概述

    15.4 PageHelper基本使用

    • 使用步骤

      1. 导入jar包

        <!-- pagehelper -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.0.0</version>
        </dependency>
        
      2. 在mybatis-config.xml中配置分页插件

        <plugins>
            <!--配置PageHelper分页插件-->
            <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
        </plugins>
        
      3. 查询数据之前,使用PageHelper开启分页

        • PageHelper.startPage(pageNum,pageSize);

          • pageNum:当前页码
          • pageSize:每页显示数据数量
        • 细节

          • 分页实现有两条SQL
            • 一条查询总数据数量
            • 另一条查询当前页显示数据集合
          • PageHelper.startPage(pageNum,pageSize)与SQL中的limit index,pageSize
            • 开始下标【index】= (pageNum-1)*pageSize
        • 示例代码

          //查询数据之前,使用PageHelper开启分页
          Page<Object> page = PageHelper.startPage(3, 3);
          
          EmployeeExample ee = new EmployeeExample();
          List<Employee> employees = mapper.selectByExample(ee);
          
          System.out.println(page.getPageNum()+"/"+page.getPages());
          System.out.println("总数据数量:"+page.getTotal());
          System.out.println("每页显示数据数量:"+page.getPageSize());
          for (Object o : page.getResult()) {
              System.out.println("o = " + o);
          }
          
      4. 查询数据之后,将结果封装PageInfo中,使用PageInfo实现分页其他效果

        • PageInfo构造器共有三个,常用构造器如下

          /**
           * 包装Page对象
           *
           * @param list          page结果
           * @param navigatePages 页码数量[5]  共7页
           	[1] 2 3 4 5
           	1 [2] 3 4 5
           	1 2 [3] 4 5
           	2 3 [4] 5 6
           	3 4 [5] 6 7
           	3 4 5 [6] 7
           	3 4 5 6 [7]
           */
          public PageInfo(List<T> list, int navigatePages) {}
          
        //PageInfo业务bean基本使用
        PageInfo<Employee> pageInfo = new PageInfo<>(employees,5);
        System.out.println(pageInfo.getPageNum()+"/"+pageInfo.getPages());
        System.out.println("总数据数量:"+pageInfo.getTotal());
        System.out.println("每页显示数据数量:"+pageInfo.getPageSize());
        for (Employee employee : pageInfo.getList()) {
            System.out.println("employee = " + employee);
        }
        System.out.println("页码信息:");
        int[] navigatepageNums = pageInfo.getNavigatepageNums();
        for (int navigatepageNum : navigatepageNums) {
            System.out.print(navigatepageNum+"  ");
        }
        System.out.println("是否有上一页:"+pageInfo.isHasPreviousPage());
        System.out.println("上一页是:"+pageInfo.getPrePage());
        System.out.println("是否有下一页:"+pageInfo.isHasNextPage());
        System.out.println("下一页是:"+pageInfo.getNextPage());
        System.out.println("是否是第一页:"+pageInfo.isIsFirstPage());
        System.out.println("是否是最后一页:"+pageInfo.isIsLastPage());
        System.out.println("导航页的第一个页码是:"+pageInfo.getNavigateFirstPage());
        System.out.println("导航页的最后一个页码是:"+pageInfo.getNavigateLastPage());
        System.out.println("导航页的总页码是:"+pageInfo.getNavigatePages());
        
    posted @   有空就一起吃饭吧  阅读(38)  评论(0编辑  收藏  举报
    相关博文:
    阅读排行:
    · 快收藏!一个技巧从此不再搞混缓存穿透和缓存击穿
    · Blazor Hybrid适配到HarmonyOS系统
    · 支付宝 IoT 设备入门宝典(下)设备经营篇
    · 万字调研——AI生成内容检测
    · 解决跨域问题的这6种方案,真香!
    点击右上角即可分享
    微信分享提示