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 @ 2022-05-12 11:27  有空就一起吃饭吧  阅读(33)  评论(0编辑  收藏  举报