• 尚硅谷SSM整合之mybatis学习打卡


    MyBatis⭐️

    1. MyBatis 简介

    1.1 MyBatis 历史

    MyBatis 前身为ibatis,提供包括SQL Maps Data Access Objects (DAO)

    1.2 MyBatis 特性

    1. MyBatis 是支持定制化SQL,存储过程以及高级映射的优秀的持久层框架
    2. MyBatis避免了几乎所有的JDBC 代码和手动设置参数以及获取结果集
    3. MyBatis可以使用简单的xml或注解用于配置和原始映射,将接口和java的POJO(Plain Old java Objects, 普通的java对象)映射成数据库中的记录
    4. MyBatis 是一个半自动的ORM(Object Relation Mapping)框架

    1.3 MyBatis 和其他持久化层技术的对比

    • JDBC

      • SQL夹杂在java代码中,造成耦合度太高,导致硬编码内伤

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

      • 代码冗长,开发效率低

    • Hibernate和JPA(没用过)

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

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

    2. 搭建mybatis

    2.1 创建Maven工程

    1. 设置打包方式为jar
    2. 引入依赖

    2.2 创建mybatis核心配置文件

    mybatis.pdf 官方文档中直接可以找到

    
    DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        
        <properties resource="jdbc.properties"/>
        
        <typeAliases>
            
            
            
            
            <package name="com.cczj.mybatis.pojo"/>
        typeAliases>
        
        <environments default="development">
            
            <environment id="development">
                
                <transactionManager type="JDBC"/>
                
                <dataSource type="POOLED">
                    <property name="driver" value="${jdbc.driver}"/>
                    <property name="url" value="${jdbc.url}"/>
                    <property name="username" value="${jdbc.username}"/>
                    <property name="password" value="${jdbc.password}"/>
                dataSource>
            environment>
    
            <environment id="test">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC"/>
                    <property name="username" value="root"/>
                    <property name="password" value="123456"/>
                dataSource>
            environment>
        environments>
        
        <mappers>
            
            
            <package name="com.cczj.mybatis.mapper"/>
        mappers>
    configuration>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    注意:

    The content of element type “configuration” must match “(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?)”.

    配置tag要按照要求的顺序

    2.3 创建mapper接口

    该mapper接口类似于DAO (Data Access Object) 数据访问对象

    2.4 创建mapper接口的映射文件

    相关概念ORM (Object Relationship Mapping)对象映射关系

    • 对象:java的实体类对象
    • 关系:关系型数据库
    • 映射:二者之间对应的关系
    java概念数据库概念
    属性字段/列
    对象记录/行
    
    DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.cczj.mybatis.mapper.UserMapper">
        
        <insert id="insertUser">
            insert into ssm.t_user
            values (1, 'admin', '123456', 18, '男', '123456@qq.com');
        insert>
    mapper>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2.5 log4j.xml配置文件

    日志的级别

    FATAL(致命) -> ERROR(错误) -> WARN(警告) -> INFO(信息) -> DEBUG(调试)

    
    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>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    3. MyBatis获取参数值的两种方式

    3.1 Mybatis 中获取参数方式的介绍

    1. ${} 本质是字符串拼接、#{} 本质是占位符赋值
    2. 使用${} 拼接时,若为字符串类型或日期类型的字段进行赋值时,需要手动加单引号
    3. 使用#{} 赋值时,若为字符串类型或日期类型的字段进行赋值时,可以自动加单引号

    3.2 传入单个参数的情况

    1. 在接口中定义的方法,如果是单个参数,最好加入@param("指定的变量名") 作为指定名
    2. ${} 中可以使用任意变量名,但是需要加上'' 作为字符串拼接来使用。
    3. #{}中可以使用任意的变量名,因为在底层实现过程中,此标记仅仅作为 ? 这个占位符来使用,而传进来的具体参数值作为一个字面量是不会改变的。

    3.3 传入多个参数的情况

    1. 在Mybatis内部,会将多个参数封装进一个集合中,这时的自变量名只能使用系统定义的参数名
    2. 集合中的参数调用名有两种:arg0、arg1、arg2...param1、param2、...
    3. 自定义Map集合:将参数值手动放入Map中,并将map作为参数传入接口中。
    4. 使用自定义Map集合时、参数调用名就是自己设定的Key

    Mybatis 映射中传入参数时,为以下结果自定义了别名

    右侧为Map类型,左侧为类型别名:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DMWtEsQC-1660203247212)(后端框架.assets/image-20220808192144372.png)]

    3.4 解决模糊查询的三个方法

    由于普通的#{username} 在引号'' 中会失去占位符的效果

    1. select * from table where username like '%${usename}%'
    2. select * from table where username like "%"#{username}"%"
    3. select * from table where username like concat('%', #{username}, '%')

    3.5 获取自增的主键

    增删改的操作下返回值都是一个int的该变量,所以我们要手动设置一个主键KEY

    <insert id="insertUser" useGeneratedKeys="true" keyProperty="id">insert>
    
    • 1

    useGeneratedKeys : 表示当前添加功能使用了自增的主键

    keyProperty : 将添加的数据的自增主键作为实体类对象参数的主键名

    4. ResultMap

    4.1 如果pojo包中属性名和数据库表中字段名不同的解决办法

    1. 由JDBC可知,查询结果会通过getter、setter的方法呈现。如果名称不一致,那么无法进行存储值。因此解决办法就是为SQL语句结果设置别名。将查询语句结果别名设置为成员属性名。

    2. 在Mybatis的核心配置文件中设置一个全局变量,可以自动将下划线映射为驼峰

      1. <setting>
             mapUnderscoreToCamelCase 将 蛇形命名法 映射为 小驼峰命名法
            <setting name="mapUnderscoreToCamelCase" value="true"/>
        setting>
        
        • 1
        • 2
        • 3
        • 4

    4.2 自定义映射关系

    
    <resultMap id="empResultMap" type="Emp">
    	<id column="emp_id" property="empId"/>
        <result column="emp_name" property="empName"/>
        <result column="emp_gender" property="empGender"/>
        <result column="emp_age" property="empAge"/>
    resultMap>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    4.3 多对一的处理方式

    因为成员属性的类型可能是一个类,那么要将多张表查询到一张表中,就需要用到以下处理方式

    1. 级联处理方式:

      1. <resultMap id="empResultMap" type="Emp">
        	<id column="emp_id" property="empId"/>
            <result column="emp_name" property="empName"/>
            <result column="emp_gender" property="empGender"/>
            <result column="emp_age" property="empAge"/>
            
            <result column="dept_id" property="dept.deptId"/>
            <result column="dept_name" property="dept.deptName"/>
        resultMap>
        
        <select id="getEmpAndDeptByEmpId" resultMap="empResultMap">
            select 
            t_dept.*, t_emp.* 
            from t_emp 
            left join t_dept 
            on t_emp.dept_id = t_dept.dept_id 
            where t_emp.emp_id = #{empId};
        select>
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
    2. association:处理多对一的映射关系 (处理实体类类型的属性)

      1. <resultMap id="empResultMap" type="Emp">
        	<id column="emp_id" property="empId"/>
            <result column="emp_name" property="empName"/>
            <result column="emp_gender" property="empGender"/>
            <result column="emp_age" property="empAge"/>
            
            <association property="dept" javaType="Dept">
            	<id column="dept_id" property="deptId"/>
            	<result column="dept_name" property="deptName"/>
            association>
        resultMap>
        
        <select id="getEmpAndDeptByEmpId" resultMap="empResultMap">
            select 
            t_dept.*, t_emp.* 
            from t_emp 
            left join t_dept 
            on t_emp.dept_id = t_dept.dept_id 
            where t_emp.emp_id = #{empId};
        select>
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
    3. 分步查询方式:

      1. 通过两个不同的接口,调用不同的方法。即用子表的结果查询父表。

      2. <resultMap id="empResultMap" type="Emp">
        	<id column="emp_id" property="empId"/>
            <result column="emp_name" property="empName"/>
            <result column="emp_gender" property="empGender"/>
            <result column="emp_age" property="empAge"/>
            
            <association property="dept"
                         select="com.cczj.mybatis.mapper.DeptMapper.方法名"
                         column="dept_id">
            association>
        resultMap>
        
        <select id="getEmpAndDeptByEmpId" resultMap="empResultMap">
            select * from t_emp where t_emp.emp_id = #{empId}
        select>
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
      3. <resultMap id="empResultMap" type="Emp">
        	<id column="dept_id" property="deptId"/>
            <result column="dept_name" property="deptName"/>
        resultMap>
        
        <select id="getEmpAndDeptByEmpId" resultMap="empResultMap">
            select * from t_dept where t_dept.dept_id = #{deptId}
        select>
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
    4. 分布查询有什么优势呢?

      1. 可以实现延迟加载,但是必须在核心配置文件中进行全局配置

      2. <settings>
            
            <settins name="lazyLoadingEnabled" value="true"/>
            <settins name="aggressiveLazyLoading" value="false"/>
        settings>
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10

    4.4. 一对多的处理方式

    1. collection: 处理一对多和多对多的方式

      1. <resultMap id="empResultMap" type="Emp">
            <id column="dept_id" property="deptId"/>
            <result column="dept_name" property="deptName"/>
            
            <collection property="emps" ofType="Emp">
                <id column="emp_id" property="empId"/>
                <result column="emp_name" property="empName"/>
            	<result column="emp_gender" property="empGender"/>
            	<result column="emp_age" property="empAge"/>
            collection>
        resultMap>
        
        <select id="getEmpAndDeptByEmpId" resultMap="empResultMap">
            select 
            t_emp.* , t_dept.*
            from t_dept
            left join t_emp 
            on t_emp.dept_id = t_dept.dept_id 
            where t_emp.emp_id = #{empId};
        select>
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
    2. 分布查询方法和多对一基本一致

    5. 动态SQL

    5.1 If where

    1. 动态拼接SQL语句的第一种写法:
    <select id="方法名" resultType="结果类型">
    	select * from t_emp where 1=1
        <if test="empName != null and empName != ''">
        	and empName = #{empName}
        if>
        <if test="empAge != null and empAge != ''">
        	and empAge = #{empAge}
        if>
    select>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    1. 动态拼接SQL语句的第二种写法:
    <select id="方法名" resultType="结果类型">
    	select * from t_emp
        <where>
            <if test="empName != null and empName != ''">
                empName = #{empName} and
            if>
            <if test="empAge != null and empAge != ''">
                empAge = #{empAge}
            if>
        where>
    select>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    5.2 trim

    trim:

    prefix、suffix: 在标签中内容前面或者后面添加指定内容

    prefixOverrides、suffixOverrides: 在标签内容前面或者后面去掉指定内容

    <select id="方法名" resultType="结果类型">
    	select * from t_emp
        <trim prefix="where" suffixOverrides="and">
            <if test="empName != null and empName != ''">
                empName = #{empName}
            if>
            <if test="empAge != null and empAge != ''">
                empAge = #{empAge}
            if>
        trim>
    select>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    5.3 choose、when、otherwise

    if、if else 和 else

    <select id="方法名" resultType="结果类型">
    	select * from t_emp
        <where>
        	<choose>
            	<when test="empName != null and empName != ''">
                	empName = #{empName}
                when>
                <when test="empAge != null and empAge != ''">
                	empAge = #{empAge}
                when>
                <otherwise>
                	类似于else ... 也可以看作default
                otherwise>
            choose>
        where>
    select>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    5.4 foreach

    collection: 表示当前传入集合的参数名 (建议通过@param进行手动设置)

    item: 表示当前循环的参数名 (类似于foreach中的每一次循环的结果)

    separate: 循环分隔符

    open: 当前循环所有的内容以什么开始 close:循环的内容以什么结束

    index: 当前索引的下标是多少

    <insert id="insertMoreEmp">
    	insert into t_emp values
        <foreach collection="emps" item="emp" separator=",">
            	(null, #{emp.empName}, #{emp.empAge})
        foreach>
    insert>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    <delete id="deleteMoreEmp">
    	delete from t_emp where emp_id in
        (
        	<foreach collection="empIds" item="empId" separator=",">
        		#{empId}
        	foreach>
        )
        delete from t_emp where emp_id in
        <foreach collection="empIds" item="empId" separator="or">
            emp_id = #{empId}
        foreach>
    delete>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    5.5 SQL片段

    将部分sql语句通过sql片段存起来,用到的时候通过include标签进行调用

    <sql id="empColumns">
    	emp_id,emp_name,emp_age,emp_gender
    sql>
    <select>
    	select <include refid="empColumns">include> from t_emp
    select>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    6. Mybatis Cache

    6.1 Mybatis 的一级缓存

    一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库中重新访问。

    一级缓存失效的四种情况:

    1. 不同的SqlSession对应不同的一级缓存
    2. 同一个SqlSession但是查询条件不同
    3. 同一个SqlSession两次查询期间执行了任何一次增删改操作(即使更新的不是缓存数据)
    4. 同一个SqlSession两次查询期间手动清空了缓存
      1. sqlSession.clearCache(); 该方法用于清除一级缓存

    6.2 Mybatis 的二级缓存

    二级缓存是SqlSessionFactory级别的,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存;此后如果再次执行相同的查询语句,结果就会从缓存中获取

    二次缓存的开启条件:

    • 在核心配置文件中,设置全局配置属性cacheEnabled="true" 默认为true,不需要设置
    • 在映射文件中设置标签cache (仅仅添加一个即可)
    • 二级缓存必须在SqlSession关闭或提交之后有效
    • 查询的数据所转换的实体类类型必须实现序列化的接口

    使二级缓存失效的情况:

    两次查询过程中执行了任意增删改的操作,会使一级和二级缓存同时失效

    6.3 Mybatis 二级缓存中的配置信息

    eviction: 缓存回收策略,有这几种回收策略

    • LRU - 最近最少回收,移除最长时间不被使用的对象
    • FIFO - 先进先出,按照缓存进入的顺序来移除它们
    • SOFT - 软引用,移除基于垃圾回收器状态和软引用规则的对象
    • WEAK - 弱引用,更积极的移除基于垃圾收集器和弱引用规则的对象

    默认是 LRU 最近最少回收策略

    • flushinterval 缓存刷新间隔,缓存多长时间刷新一次,默认不清空,设置一个毫秒值
    • readOnly: 是否只读;true 只读,MyBatis 认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。MyBatis 为了加快获取数据,直接就会将数据在缓存中的引用交给用户。不安全,速度快。读写(默认):MyBatis 觉得数据可能会被修改
    • size : 缓存存放多少个元素
    • type: 指定自定义缓存的全类名(实现Cache 接口即可)
    • blocking: 若缓存中找不到对应的key,是否会一直blocking,直到有对应的数据进入缓存。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HKQwPEJ0-1660203247214)(后端框架.assets/33b6b5887c9a4a198964ca58ae86f04e.png)]

    6.4 Mybatis 缓存查询的顺序

    先查询二级缓存,因为二级缓存中可能会有其他程序已经查询出来的结果,可以直接拿来使用

    如果二级缓存也没有命中,那么再查询一级缓存 (当一级缓存没有结束时的情况下会发生该情况)

    如果一级缓存也没有命中,那么再查询数据库

    SqlSession 关闭后,一级缓存中的数据会写入二级缓存

  • 相关阅读:
    【VueUse】重新定义状态管理在 Vue 中的体验
    基于JSP的图书销售管理系统
    基于FPGA的自动白平衡算法实现
    java数组基础
    springcloud学习笔记(3)-服务管理组件Nacos
    2023.9.26 IO 文件操作详解
    Mybatis学习笔记-映射文件,标签,插件
    AI大预言模型——ChatGPT在地学、GIS、气象、农业、生态、环境等应用
    C++之继承
    properties文件
  • 原文地址:https://blog.csdn.net/cczj0/article/details/126286526