MyBatis实践:提高持久层数据处理效率

news2024/11/26 15:20:57

文章目录

  • 1 Mybatis简介
    • 1.1 简介
    • 1.2 持久层框架对比
  • 2 快速入门
    • 2.1 准备数据库
    • 2.2 项目搭建
    • 2.3 依赖导入
    • 2.4 准备MyBatis配置文件
    • 2.5 实体类准备
    • 2.6 准备Mapper接口和MapperXML文件
    • 2.7 运行和测试
  • 3. 核心配置文件
  • 4. MyBatis进阶使用
    • 4.0 以包为单位,引入所有的映射文件
    • 4.1 开启日志功能
    • 4.2 SQL语句传参形式
    • 4.3 MapperXML模版文件
    • 4.4 SqlSessionUtil工具类
    • 4.5 数据输入
      • 4.5.1 Mybatis总体机制概括
      • 4.5.2 概念说明
      • 4.5.3 单个字面量类型的参数
      • 4.5.3 多个字面量类型的参数
      • 4.5.5 map集合类型的参数
      • 4.5.6 实体类类型的参数
      • 4.5.7 使用@Param标识参数
    • 4.6 数据输出
      • 4.6.1 输出概述
      • 4.6.2 单个简单类型
      • 4.6.3 返回实体类对象
      • 4.6.4 返回单条map类型
      • 4.6.5 返回多条map类型
      • 4.6.6 返回list集合类型
      • 4.6.7 返回自增主键值
    • 4.7 增删改查
    • 4.8 自定义映射resultMap
      • 4.8.0 驼峰转换
      • 4.8.1 起别名
      • 4.8.2 resultMap
    • 4.9 模糊查询
    • 4.10 批量删除
    • 4.11 多表映射
      • 4.11.1 多对一映射
      • 4.11.2 一对多映射
    • 4.12 分步查询
    • 4.13 实体类别名
    • 4.14 使用注解增删改查
  • 5.动态SQL
    • if
    • where
    • set
    • trim
    • choose、when、otherwise
    • foreach
    • sql片段
  • 6. MyBatis的缓存
    • 一级缓存
    • 二级缓存
    • 二级缓存的相关配置
    • MyBatis缓存查询的顺序
    • 整合第三方缓存EHCache
  • 7. MyBatis的逆向工程
  • 8. MyBatis X
  • 9. 分页

1 Mybatis简介

1.1 简介

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

1.2 持久层框架对比

  • JDBC
    • SQL 夹杂在Java代码中耦合度高,导致硬编码内伤
    • 维护不易且实际开发需求中 SQL 有变化,频繁修改的情况多见
    • 代码冗长,开发效率低
  • Hibernate 和 JPA
    • 操作简便,开发效率高
    • 程序中的长难复杂 SQL 需要绕过框架
    • 内部自动生成的 SQL,不容易做特殊优化
    • 基于全映射的全自动框架,大量字段的 POJO 进行部分映射时比较困难。
    • 反射操作太多,导致数据库性能下降
  • MyBatis
    • 轻量级,性能出色
    • SQL 和 Java 编码分开,功能边界清晰。Java代码专注业务、SQL语句专注数据
    • 开发效率稍逊于 Hibernate,但是完全能够接收

开发效率:Hibernate>Mybatis>JDBC

运行效率:JDBC>Mybatis>Hibernate

2 快速入门

2.1 准备数据库

  • 用户表(t_user)
    在这里插入图片描述

    CREATE DATABASE `mybatis-example`;
    
    USE `mybatis-example`;
    
    CREATE TABLE `t_user` (
      `id` int NOT NULL AUTO_INCREMENT,
      `username` varchar(20),
      `password` varchar(20),
      `age` int,
      `gender` char(1),
      `email` varchar(50),
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
    
    INSERT INTO `t_user`(username,password,age,gender,email) VALUES("tom","123",25,'男',"12345@abc.com");
    INSERT INTO `t_user`(username,password,age,gender,email) VALUES("jerry","456",23,'男',"45678@abc.com");
    
  • 员工表(t_emp)在这里插入图片描述

    CREATE TABLE `t_emp`(
      emp_id INT AUTO_INCREMENT,
      emp_name CHAR(100),
      emp_salary DOUBLE(10,5),
      PRIMARY KEY(emp_id)
    );
    
    INSERT INTO `t_emp`(emp_name,emp_salary) VALUES("tom",200.33);
    INSERT INTO `t_emp`(emp_name,emp_salary) VALUES("jerry",666.66);
    

2.2 项目搭建

在这里插入图片描述

2.3 依赖导入

pom.xml

<dependencies>
  <!-- mybatis依赖 -->
  <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.11</version>
  </dependency>

  <!-- MySQL驱动 mybatis底层依赖jdbc驱动实现,本次不需要导入连接池,mybatis自带! -->
  <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.25</version>
  </dependency>

  <!--junit5测试-->
  <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-api</artifactId>
      <version>5.3.1</version>
  </dependency>
</dependencies>

2.4 准备MyBatis配置文件

在resources目录下创建mybatis框架配置文件: 数据库连接信息,mybatis配置,引入mapper.xml配置等!

  • jdbc.properties

    jdbc.driver=com.mysql.cj.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/mybatis-example
    jdbc.username=root
    jdbc.password=root
    
  • mybatis-config.xml
    习惯上命名为 mybatis-config.xml,这个文件名仅仅只是建议,并非强制要求。将来整合 Spring 之后,这个配置文件可以省略,所以大家操作时可以直接复制、粘贴。

    <?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>
    
        <!--引入properties文件,此时就可以${属性名}的方式访问属性值-->
        <properties resource="jdbc.properties"></properties>
    
        <!--设置连接数据库的环境-->
        <!-- environments表示配置Mybatis的开发环境,可以配置多个环境,在众多具体环境中,使用default属性指定实际运行时使用的环境。
        default属性的取值是environment标签的id属性的值。 -->
        <environments default="development">
    
            <!-- environment表示配置Mybatis开发环境里一个具体的环境 -->
            <environment id="development">
    
                <!-- Mybatis的内置的事务管理器 -->
                <transactionManager type="JDBC"/>
    
                <!--
                    dataSource:配置数据源
                    属性:type:设置数据源的类型,type="POOLED|UNPOOLED|JNDI"
                    type="POOLED":使用数据库连接池,即会将创建的连接进行缓存,下次使用可以从缓存中直接获取,不需要重新创建
                    type="UNPOOLED":不使用数据库连接池,即每次使用连接都需要重新创建
                    type="JNDI":调用上下文中的数据源
                -->
                <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>
        </environments>
    
        <!--引入mybatis映射文件-->
        <mappers>
            <!-- Mapper注册:指定Mybatis映射文件的具体位置 -->
            <!-- mapper标签:配置一个具体的Mapper映射文件 -->
            <!-- resource属性:指定Mapper映射文件的实际存储位置,这里需要使用一个以类路径根目录为基准的相对路径 -->
            <!-- 对Maven工程的目录结构来说,resources目录下的内容会直接放入类路径,所以这里我们可以以resources目录为基准 -->
            <mapper resource="mappers/UserMapper.xml"/>
            <mapper resource="mappers/EmpMapper.xml"/>
        </mappers>
    </configuration>
    

2.5 实体类准备

  • 用户实体类(属性名与数据库字段名一致)

    package com.xxxx.lln.pojo;
    
    public class User {
    
        private Integer id;
    
        private String username;
    
        private String password;
    
        private Integer age;
    
        private String gender;
    
        private String email;
    
    	(此处省略get||set||toString方法)
    }
    
  • 员工实体类

    package com.xxxx.lln.pojo;
    
    public class Emp {
    
        private Integer empId;
    
        private String empName;
    
        private Double empSalary;
        
        (此处省略get||set||toString方法)
    }
    

2.6 准备Mapper接口和MapperXML文件

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

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

如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码!!

一般编写SQL语句的文件命名:XxxMapper.xml Xxx一般取表名!!

Mybatis 中的 Mapper 接口相当于以前的 Dao。但是区别在于,Mapper 仅仅只是建接口即可,我们不需要提供实现类,具体的SQL写到对应的Mapper文件,该用法的思路如下图所示:

在这里插入图片描述

  • 定义UserMapper接口

    package com.xxxx.lln.mapper;
    
    import com.xxxx.lln.pojo.User;
    
    /**
     *  t_user表对应数据库SQL语句映射接口!
     *  接口只规定方法,参数和返回值!
     *  mapper.xml中编写具体SQL语句!
     */
    public interface UserMapper {
        /**
         * 根据用户id查询用户数据方法
         * @param id
         * @return 用户实体对象
         */
        User getUserById(Integer id);
    
    }
    
    
  • 配置UserMapperXML文件
    位置: resources/mappers/UserMapper.xml

    <?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.xxxx.lln.mapper.UserMapper">
    
        <!--
            mapper接口和映射文件要保证两个一致:
            1.mapper接口的全类名和映射文件的namespace一致
            2.mapper接口中的方法的方法名要和映射文件中的sql语句的id保持一致
        -->
        
        <!-- 查询使用 select标签
            id = 方法名
            resultType = 返回值类型
            标签内编写SQL语句
         -->
         
        <!-- 编写具体的SQL语句,使用id属性唯一的标记一条SQL语句 -->
        <!-- resultType属性:指定封装查询结果的Java实体类的全类名 -->
        <select id="getUserById" resultType="com.xxxx.lln.pojo.User">
            <!-- Mybatis负责把SQL语句中的#{}部分替换成“?”占位符 -->
            <!-- 给数据库每一个字段设置一个别名,让别名和Java实体类中属性名一致 -->
            select id, username, password, age, gender, email
            from t_user
            where id = #{id}
        </select>
    
    </mapper>
    
  • 定义EmpMapper接口

    package com.xxxx.lln.mapper;
    
    public interface EmpMapper {
        
    }
    
  • 配置EmpMapperXML文件

    <?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.xxxx.lln.mapper.EmpMapper">
        
    </mapper>
    
  • 注意:

    • 方法名和SQL的id一致
    • 方法返回值和resultType一致
    • 方法的参数和SQL的参数一致
    • 接口的全类名和映射配置文件的名称空间一致

2.7 运行和测试

package com.xxxx.lln;

import com.xxxx.lln.mapper.UserMapper;
import com.xxxx.lln.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.InputStream;

public class MyTest {

    @Test
    public void testSelectUser() throws IOException {
        //获取核心配置文件的输入流
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        //获取SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        //获取SqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
        //获取sql的会话对象SqlSession(不会自动提交事务),是MyBatis提供的操作数据库的对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //获取sql的会话对象SqlSession(会自动提交事务),是MyBatis提供的操作数据库的对象
        //SqlSession sqlSession = sqlSessionFactory.openSession(true);
        //获取UserMapper的代理实现对象getMapper(Class<T> var1)
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        //调用mapper接口中的方法,实现根据Id查询用户信息的功能
        User user = userMapper.getUserById(1);
        //通过sql语句的唯一标识找到sql并执行,唯一标识是namespace.sqlId
        //User user = sqlSession.selectOne("com.xxxx.lln.mapper.UserMapper.getUserById",1);
        System.out.println("返回结果:"+user);
        //提交事务,查询其实不用
        sqlSession.commit();
        //关闭会话
        sqlSession.close();
    }

}
  • 说明:

    • SqlSession:代表Java程序和数据库之间的会话。(HttpSession是Java程序和浏览器之间的会话)
    • SqlSessionFactory:是“生产”SqlSession的“工厂”。
    • 工厂模式:如果创建某一个对象,使用的过程基本固定,那么我们就可以把创建这个对象的相关代码封装到一个“工厂类”中,以后都使用这个工厂类来“生产”我们需要的对象。

  • SqlSession和HttpSession区别
    • HttpSession:工作在Web服务器上,属于表述层。
      • 代表浏览器和Web服务器之间的会话。
    • SqlSession:不依赖Web服务器,属于持久化层。
      • 代表Java程序和数据库之间的会话。
         
        在这里插入图片描述

3. 核心配置文件

<?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>


    <!--引入properties文件,此时就可以${属性名}的方式访问属性值-->
    <properties resource="jdbc.properties"></properties>


    <settings>
        <!-- 使用Log4j2作为日志实现! -->
        <setting name="logImpl" value="LOG4J2"/>
        <!--将表中字段的下划线自动转换为驼峰-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!--开启延迟加载-->
        <setting name="lazyLoadingEnabled" value="true"/>
    </settings>



    <typeAliases>
        <!--
            typeAlias:设置某个具体的类型的别名
            属性: type:需要设置别名的类型的全类名
            alias:设置此类型的别名,若不设置此属性,该类型拥有默认的别名,即类名且不区分大小写
            若设置此属性,此时该类型的别名只能使用alias所设置的值
        -->
        <!--<typeAlias type="com.atguigu.mybatis.bean.User"></typeAlias>-->
        <!--<typeAlias type="com.atguigu.mybatis.bean.User" alias="abc"> </typeAlias>-->
        <!--以包为单位,设置改包下所有的类型都拥有默认的别名,即类名且不区分大小写-->
        <package name="com.atguigu.mybatis.bean"/>
    </typeAliases>


    <!--environments:设置多个连接数据库的环境 属性: default:设置默认使用的环境的id -->
    <environments default="mysql_test">
    
        <!--
            environment:设置具体的连接数据库的环境信息
            属性: id:设置环境的唯一标识,可通过environments标签中的default设置某一个环境的id, 表示默认使用的环境
        -->
        <environment id="mysql_test">
        
            <!--
                transactionManager:设置事务管理方式 属性:
                type:设置事务管理方式,type="JDBC|MANAGED" type="JDBC":设置当前环境的事务管理都必须手动处理
                type="MANAGED":设置事务被管理,例如spring中的AOP
            -->
            <transactionManager type="JDBC"/>

            <!--
                dataSource:设置数据源
                属性:type:设置数据源的类型,type="POOLED|UNPOOLED|JNDI"
                type="POOLED":使用数据库连接池,即会将创建的连接进行缓存,下次使用可以从缓存中直接获取,不需要重新创建
                type="UNPOOLED":不使用数据库连接池,即每次使用连接都需要重新创建
                type="JNDI":调用上下文中的数据源
            -->
            <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>
        
    </environments>

    <!--引入映射文件-->
    <mappers>
        <!-- Mapper注册:指定Mybatis映射文件的具体位置 -->
        <!-- mapper标签:配置一个具体的Mapper映射文件 -->
        <!-- resource属性:指定Mapper映射文件的实际存储位置,这里需要使用一个以类路径根目录为基准的相对路径 -->
        <!-- 对Maven工程的目录结构来说,resources目录下的内容会直接放入类路径,所以这里我们可以以resources目录为基准 -->
        <!--<mapper resource="mappers/UserMapper.xml"/>-->
        <!-- <mapper resource="mappers/EmpMapper.xml"/>-->
        <!--
            以包为单位,将包下所有的映射文件引入核心配置文件
            注意:此方式必须保证mapper接口和mapper映射文件必须在相同的包下
            接口名和映射文件名字一致
            创建包的时候以斜线为分隔符
        -->
        <package name="com.atguigu.mybatis.mapper"/>
    </mappers>

</configuration>

4. MyBatis进阶使用

4.0 以包为单位,引入所有的映射文件

  • 在resource文件下,创建以下文件夹,以斜线为分隔符
    在这里插入图片描述

  • 将刚才的MapperXML文件移动到新创建的文件夹下
    在这里插入图片描述

  • 修改mybatis-config.xml配置文件

        <!--引入mybatis映射文件-->
        <mappers>
    
            <!--<mapper resource="mappers/UserMapper.xml"/>-->
            <!-- <mapper resource="mappers/EmpMapper.xml"/>-->
    
            <!--
                以包为单位,将包下所有的映射文件引入核心配置文件
                注意:此方式必须保证mapper接口和mapperXML映射文件必须在相同的包下
                接口名和映射文件名字一致
                创建包的时候以斜线为分隔符
            -->
            <package name="com.xxxx.lln.mapper"/>
    
        </mappers>
    

4.1 开启日志功能

  • 添加以下配置以启用 MyBatis 日志

    <settings>
      <!-- 使用Log4j2作为日志实现! -->
      <setting name="logImpl" value="LOG4J2"/>
    </settings>
    
  • 引入相应的日志实现依赖

    <!--log4j2的依赖-->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.19.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-slf4j2-impl</artifactId>
        <version>2.19.0</version>
    </dependency>
    
  • resources下配置log4j2.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration status="OFF">
    
        <properties>
            <!-- 日志打印级别 -->
            <property name="LOG_LEVEL">INFO</property>
            <!-- APP名称 -->
            <property name="APP_NAME" value="framework-project"/>
            <!-- 日志文件存储路径 -->
            <property name="LOG_HOME">./logs/</property>
            <!-- 存储天数 -->
            <property name="LOG_MAX_HISTORY" value="60d"/>
            <!-- 单个日志文件最大值, 单位 = KB, MB, GB -->
            <property name="LOG_MAX_FILE_SIZE" value="10 MB"/>
            <!-- 每天每个日志级别产生的文件最大数量 -->
            <property name="LOG_TOTAL_NUMBER_DAILY" value="10"/>
            <!-- 压缩文件的类型,支持zip和gz,建议Linux用gz,Windows用zip -->
            <property name="ARCHIVE_FILE_SUFFIX" value="zip"/>
            <!-- 日志文件名 -->
            <property name="LOG_FILE_NAME" value="${LOG_HOME}"/>
            <property name="FILE_NAME_PATTERN" value="${LOG_HOME}%d{yyyy-MM-dd}"/>
    
            <!--
                格式化输出:
                %date{yyyy-MM-dd HH:mm:ss.SSS}: 简写为%d 日期 2023-08-12 15:04:30,123
                %thread: %t 线程名, main
                %-5level:%p 日志级别,从左往右至少显示5个字符宽度,不足补空格 INFO
                %msg:%m 日志消息 info msg
                %n: 换行符
                {cyan}: 蓝绿色(青色)
                %logger{36}: %c 表示 Logger 名字最长36个字符
                %C: 类路径 com.qq.demolog4j2.TestLog4j2
                %M: 方法名 main
                %F: 类名 TestLog4j2.java
                %L: 行号 12
                %l: 日志位置, 相当于 %C.%M(%F.%L)  com.qq.demolog4j2.TestLog4j2.main(TestLog4j2.java:16)
            -->
            <!-- %d: 日期
            %-5level: 日志级别,显示时占5个字符不足
            [%t]: 线程名
            %c{1.}: 显示调用者,只显示包名最后一截及方法名,前面的只取首字母
            .%M(代码行号%L):
            %msg%n": 需要打印的日志信息,换行:INFO>[MsgToMP:99]
            Bright: 加粗 -->
            <!--日志输出格式-控制台彩色打印-->
            <property name="ENCODER_PATTERN_CONSOLE">%blue{%d{yyyy-MM-dd HH:mm:ss.SSS}} | %highlight{%-5level}{ERROR=Bright RED, WARN=Bright Yellow, INFO=Bright Green, DEBUG=Bright Cyan, TRACE=Bright White} | %yellow{%t} | %cyan{%c{1.}}  : %white{%msg%n}</property>
            <!--日志输出格式-文件-->
            <property name="ENCODER_PATTERN">%d{yyyy-MM-dd HH:mm:ss.SSS}  %-5level %5pid --- [%15.15t] %c{1.} [%L] : %m%n</property>
            <!--日志输出格式-控制台彩色打印-->
            <property name="DEFAULT_PATTERN">%d{yyyy-MM-dd HH:mm:ss.SSS}  %highlight{%-5level} %style{%5pid}{bright,magenta} --- [%15.15t] %cyan{%c{1.} [%L]} : %m%n</property>
        </properties>
    
        <Appenders>
            <!-- 控制台的输出配置 -->
            <Console name="Console" target="SYSTEM_OUT">
                <!--输出日志的格式-->
                <PatternLayout pattern="${DEFAULT_PATTERN}" />
            </Console>
            <!-- 打印出所有的info及以下级别的信息,每次大小超过size进行压缩,作为存档-->
            <RollingFile name="RollingFileAll" fileName="${LOG_FILE_NAME}/${date:yyyy-MM-dd}/info.log" filePattern="${FILE_NAME_PATTERN}/info.log">
                <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
                <ThresholdFilter level="${LOG_LEVEL}" onMatch="ACCEPT" onMismatch="DENY" />
                <!--输出日志的格式-->
                <PatternLayout pattern="${ENCODER_PATTERN}" />
                <Policies>
                    <!-- 归档每天的文件 -->
                    <TimeBasedTriggeringPolicy />
                    <!-- 限制单个文件大小 -->
                    <SizeBasedTriggeringPolicy size="${LOG_MAX_FILE_SIZE}" />
                </Policies>
                <!-- 限制每天文件个数 -->
                <DefaultRolloverStrategy compressionLevel="9" max="${LOG_TOTAL_NUMBER_DAILY}">
                    <Delete basePath="${LOG_HOME}" maxDepth="1">
                        <IfFileName glob=".info.*.log" />
                        <IfLastModified age="${LOG_MAX_HISTORY}" />
                    </Delete>
                </DefaultRolloverStrategy>
            </RollingFile>
    
            <RollingFile name="RollingFileDebug"
                         fileName="${LOG_FILE_NAME}/${date:yyyy-MM-dd}/debug.log"
                         filePattern="${FILE_NAME_PATTERN}/debug.log">
                <Filters>
                    <ThresholdFilter level="DEBUG" />
                    <ThresholdFilter level="INFO" onMatch="DENY"
                                     onMismatch="NEUTRAL" />
                </Filters>
                <PatternLayout pattern="${ENCODER_PATTERN}" />
                <Policies>
                    <!-- 归档每天的文件 -->
                    <TimeBasedTriggeringPolicy />
                    <!-- 限制单个文件大小 -->
                    <SizeBasedTriggeringPolicy size="${LOG_MAX_FILE_SIZE}" />
                </Policies>
                <!-- 限制每天文件个数 -->
                <DefaultRolloverStrategy compressionLevel="9"
                                         max="${LOG_TOTAL_NUMBER_DAILY}">
                    <Delete basePath="${LOG_HOME}" maxDepth="1">
                        <IfFileName glob="*.debug.*.log" />
                        <IfLastModified age="${LOG_MAX_HISTORY}" />
                    </Delete>
                </DefaultRolloverStrategy>
            </RollingFile>
    
            <RollingFile name="RollingFileWarn" fileName="${LOG_FILE_NAME}/${date:yyyy-MM-dd}/warn.log"
                         filePattern="${FILE_NAME_PATTERN}.warn.log">
                <Filters>
                    <ThresholdFilter level="WARN" />
                    <ThresholdFilter level="ERROR" onMatch="DENY"
                                     onMismatch="NEUTRAL" />
                </Filters>
                <PatternLayout pattern="${ENCODER_PATTERN}" />
                <Policies>
                    <!-- 归档每天的文件 -->
                    <TimeBasedTriggeringPolicy />
                    <!-- 限制单个文件大小 -->
                    <SizeBasedTriggeringPolicy size="${LOG_MAX_FILE_SIZE}" />
                </Policies>
                <!-- 限制每天文件个数 -->
                <DefaultRolloverStrategy compressionLevel="9"
                                         max="${LOG_TOTAL_NUMBER_DAILY}">
                    <Delete basePath="${LOG_HOME}" maxDepth="1">
                        <IfFileName glob="*.warn.*.log" />
                        <IfLastModified age="${LOG_MAX_HISTORY}" />
                    </Delete>
                </DefaultRolloverStrategy>
            </RollingFile>
    
            <RollingFile name="RollingFileError"
                         fileName="${LOG_FILE_NAME}/${date:yyyy-MM-dd}/error.log"
                         filePattern="${FILE_NAME_PATTERN}.error.log">
                <Filters>
                    <ThresholdFilter level="ERROR" />
                </Filters>
                <PatternLayout pattern="${ENCODER_PATTERN}" />
                <Policies>
                    <TimeBasedTriggeringPolicy />
                    <SizeBasedTriggeringPolicy size="${LOG_MAX_FILE_SIZE}" />
                </Policies>
                <DefaultRolloverStrategy compressionLevel="9"   max="${LOG_TOTAL_NUMBER_DAILY}">
                    <Delete basePath="${LOG_HOME}" maxDepth="1">
                        <IfFileName glob="*.error.*.log" />
                        <IfLastModified age="${LOG_MAX_HISTORY}" />
                    </Delete>
                </DefaultRolloverStrategy>
            </RollingFile>
        </Appenders>
    
        <!--只有定义了logger并引入以上Appenders,Appender才会生效-->
        <Loggers>
            <!-- 将业务dao接口所在的包填写进去,并用在控制台和文件中输出 -->
            <logger name="com.xxxx.lln.mapper" level="TRACE" additivity="false">
                <AppenderRef ref="Console" />
            </logger>
    
            <root level="${LOG_LEVEL}">
                <appender-ref ref="Console"/>
                <appender-ref ref="RollingFileAll"/>
                <appender-ref ref="RollingFileDebug"/>
                <appender-ref ref="RollingFileWarn"/>
                <appender-ref ref="RollingFileError"/>
            </root>
        </Loggers>
    </configuration>
    

4.2 SQL语句传参形式

  • ${}形式
    ${}形式传参,底层Mybatis做的是字符串拼接操作。
    在这里插入图片描述

    通常不会采用${}的方式传值。一个特定的适用场景是:通过Java程序动态生成数据库表,表名部分需要Java程序通过参数传入;而JDBC对于表名部分是不能使用问号占位符的,此时只能使用 ${}。

    结论:实际开发中,能用#{}实现的,肯定不用${}。

    特殊情况: #{}只能替换值,但是有时候动态的不是值,是表名,列名或者关键字,需要使用${}拼接。

  • #{}形式
    Mybatis会将SQL语句中的#{}转换为问号占位符。

    在这里插入图片描述

4.3 MapperXML模版文件

在这里插入图片描述

4.4 SqlSessionUtil工具类

  • 提取工具类

    package com.xxxx.lln.utils;
    
    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    
    import java.io.IOException;
    import java.io.InputStream;
    
    public class SqlSessionUtil {
    
        public static SqlSession getSqlSession(){
            //创建sql的会话对象SqlSession
            SqlSession sqlSession = null;
            try {
                //获取核心配置文件的输入流
                InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
                //获取SqlSessionFactoryBuilder对象
                SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
                //获取SqlSessionFactory对象
                SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
                //配置sql的会话对象SqlSession(会自动提交事务),是MyBatis提供的操作数据库的对象
                sqlSession = sqlSessionFactory.openSession(true);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return sqlSession;
        }
    
    }
    
  • mapper的接口

    /**
     * 根据用户id删除用户信息
     * @param id
     * @return
     */
    int deleteUserById(int id);
    
  • xml文件的sql

        <!--根据用户id删除用户信息-->
       <delete id="deleteUserById">
            delete
            from t_user
            where id = #{id}
       </delete>
    
  • 使用工具类进行删除

    /**
     * 测试删除用户信息
     */
    @Test
    public void testDeleteUserById(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        int num = userMapper.deleteUserById(6);
        System.out.println(num);
        sqlSession.close();
    }
    

4.5 数据输入

4.5.1 Mybatis总体机制概括

在这里插入图片描述

4.5.2 概念说明

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

  • 简单类型:只包含一个值的数据类型
    • 基本数据类型:int、byte、short、double、……
    • 基本数据类型的包装类型:Integer、Character、Double、……
    • 字符串类型:String
  • 复杂类型:包含多个值的数据类型
    • 实体类类型:Employee、Department、……
    • 集合类型:List、Set、Map、……
    • 数组类型:int[]、String[]、……
    • 复合类型:List、实体类中包含集合……

4.5.3 单个字面量类型的参数

若mapper接口中的方法参数为单个的字面量类型 此时可以使用$ { }和 # { }以任意的名称获取参数的值,注意${}需要手动加单引号

但单个字面量其实目前跟参数名字没有关系,但是不能是数值。
在这里插入图片描述

4.5.3 多个字面量类型的参数

若mapper接口中的方法参数为多个时,MyBatis会自动将这些参数放在一个map集合中,以arg0,arg1…为键,以参数为值;以 param1,param2…为键,以参数为值;因此只需要通过${}和#{}访问map集合的键就可以获取相对应的值。

  • 多个字面量正常按参数名字是取不到的
    在这里插入图片描述

  • 通过arg0,arg1或param1,param2取值
    在这里插入图片描述

4.5.5 map集合类型的参数

若mapper接口中的方法需要的参数为多个时,此时可以手动创建map集合,将这些数据放在map中 只需要通过 $ {}和#{}访问map集合的键就可以获取相对应的值,注意${}需要手动加单引号

在这里插入图片描述

4.5.6 实体类类型的参数

若mapper接口中的方法参数为实体类对象时
此时可以使用$ {}和#{},通过访问实体类对象中的属性名获取属性值,注意${}需要手动加单引号
只跟get/set方法有关
在这里插入图片描述

在这里插入图片描述

4.5.7 使用@Param标识参数

命名参数
可以通过@Param注解标识mapper接口中的方法参数
此时,会将这些参数放在map集合中,以@Param注解的value属性值为键,以参数为值;以 param1,param2…为键,以参数为值;只需要通过${}和#{}访问map集合的键就可以获取相对应的值, 注意 ${}需要手动加单引号

在这里插入图片描述

4.6 数据输出

4.6.1 输出概述

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

  • 增删改操作返回的受影响行数:直接使用 int 或 long 类型接收即可
  • 查询操作的查询结果

我们需要做的是,指定查询的输出数据类型即可!

并且插入场景下,实现主键数据回显示!

4.6.2 单个简单类型

Mybatis 内部给常用的数据类型设定了很多别名。 以 int 类型为例,可以写的名称有:int、integer、Integer、java.lang.Integer、Int、INT、INTEGER 等等。

在这里插入图片描述

4.6.3 返回实体类对象

在这里插入图片描述

4.6.4 返回单条map类型

在这里插入图片描述

4.6.5 返回多条map类型

在这里插入图片描述

4.6.6 返回list集合类型

在这里插入图片描述

在这里插入图片描述

4.6.7 返回自增主键值

  • 自增长类型主键
    Mybatis是将自增主键的值设置到实体类对象中,而不是以Mapper接口方法返回值的形式返回。在这里插入图片描述

  • 非自增长类型主键
    而对于不支持自增型主键的数据库(例如 Oracle)或者字符串类型主键,则可以使用 selectKey 子元素:selectKey 元素将会首先运行,id 会被设置,然后插入语句会被调用!

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

    <insert id="insertUser" parameterType="User">
        <selectKey keyProperty="id" resultType="java.lang.String"
            order="BEFORE">
            SELECT UUID() as id
        </selectKey>
        INSERT INTO user (id, username, password) 
        VALUES (
            #{id},
            #{username},
            #{password}
        )
    </insert>
    

    在上例中,我们定义了一个 insertUser 的插入语句来将 User 对象插入到 user 表中。我们使用 selectKey 来查询 UUID 并设置到 id 字段中。

    通过 keyProperty 属性来指定查询到的 UUID 赋值给对象中的 id 属性,而 resultType 属性指定了 UUID 的类型为 java.lang.String

    需要注意的是,我们将 selectKey 放在了插入语句的前面,这是因为 MySQL 在 insert 语句中只支持一个 select 子句,而 selectKey 中查询 UUID 的语句就是一个 select 子句,因此我们需要将其放在前面。

    最后,在将 User 对象插入到 user 表中时,我们直接使用对象中的 id 属性来插入主键值。

    使用这种方式,我们可以方便地插入 UUID 作为字符串类型主键。当然,还有其他插入方式可以使用,如使用Java代码生成UUID并在类中显式设置值等。需要根据具体应用场景和需求选择合适的插入方式。

4.7 增删改查

  • mapper接口

     //添加用户信息
     int insertUser();
    
     //删除用户信息
     void deleteUser();
    
     //修改用户信息
     void updateUser();
    
     //通过id查询一个实体类对象
     User getUserById();
    
     //查询实体类集合
     List<User> getUserList();
    
  • MapperXML

    <?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.xxxx.lln.mapper.UserMapper">
    
        <!--
            mapper接口和映射文件要保证两个一致:
            1.mapper接口的全类名和映射文件的namespace一致
            2.mapper接口中的方法的方法名要和映射文件中的sql语句的id保持一致
        -->
    
        <!--添加用户信息-->
        <insert id="insertUser">
            insert into t_user(username,password,age,gender,email) values(null,'张三','123',23,'男',"123@163.com")
        </insert>
    
        <!--删除用户信息-->
        <delete id="deleteUser">
            delete from t_user where id = 2
        </delete>
    
        <!--修改用户信息-->
        <update id="updateUser">
            update t_user set username='ybc',password='123' where id = 3
        </update>
    
        <!--
            resultType:自动映射,用于属性名和表中字段名一致的情况
            resultMap:自定义映射,用于一对多或多对一或字段名和属性名不一致的情况
        -->
    
        <!--通过id查询一个实体类对象-->
        <select id="getUserById" resultType="com.xxxx.lln.pojo.User">
            select * from t_user where id = 3
        </select>
    
        <!--查询实体类集合-->
        <select id="getUserList" resultType="com.xxxx.lln.pojo.User">
            select * from t_user
        </select>
    
    </mapper>
    
    

4.8 自定义映射resultMap

需要解决数据表字段名与实体类属性名对应不上时的情况:
在这里插入图片描述

4.8.0 驼峰转换

全局配置

    <settings>
        <!-- 使用Log4j2作为日志实现! -->
        <setting name="logImpl" value="LOG4J2"/>
        <!--将表中字段的下划线自动转换为驼峰-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

在这里插入图片描述

4.8.1 起别名

在这里插入图片描述

4.8.2 resultMap

使用resultMap标签定义对应关系,再在后面的SQL语句中引用这个对应关系
在这里插入图片描述

4.9 模糊查询

<!--select * from t_user where username like "%"#{name}"%"-->
<!--select * from t_user where username like '%${name}%'-->
select * from t_user where username like concat('%', #{name}, '%')

在这里插入图片描述

4.10 批量删除

在这里插入图片描述
在这里插入图片描述

4.11 多表映射

4.11.1 多对一映射

假设用户对象里面包含员工对象

public class User {
    private Integer id;
    private String username;
    private String password;
    private Integer age;
    private String gender;
    private String email;
    private Emp emp;
}

级联方式处理映射关系只用resultMap
在这里插入图片描述

使用association处理映射关系

在这里插入图片描述

4.11.2 一对多映射

懒得动表结构了,直接用之前的笔记
在这里插入图片描述

4.12 分步查询

在这里插入图片描述

  • 开启局部延迟加载

    • 延迟加载的核心原理是:用的时候再执行查询语句,不用的时候不查询

    • 作用:提高性能,尽可能的不查,或者尽可能的少查,来提高效率

    • 在mybatis当中怎么开启延迟加载呢?

      • association标签之哦给你添加fetchType=“lazy”
      • 注意,默认情况下是没有开启延迟加载的,需要设置,fetchType=“lazy”
      • 这种在association标签中配置fetchType=“lazy” 是局部的设置,只对当前association关联的sql语句起作用
      • fetchType=“eager” 表示立即加载
    • 实际的开发中的模式:

      • 把全局的延迟加载打开。
      • 如果某一步不需要使用延迟加载,请设置fetchType=“eager” 即可

    在这里插入图片描述

  • 开启全局延迟加载

        <settings>
            <!-- 使用Log4j2作为日志实现! -->
            <setting name="logImpl" value="LOG4J2"/>
            <!--将表中字段的下划线自动转换为驼峰-->
            <setting name="mapUnderscoreToCamelCase" value="true"/>
            <!--开启延迟加载-->
            <setting name="lazyLoadingEnabled" value="true"/>
        </settings>
    

    在这里插入图片描述

4.13 实体类别名

差点忘了…核心配置文件里,可以设置类的别名,resultType的时候,就不用每次写类的全类名了。

    <typeAliases>
        <!--
            typeAlias:设置某个具体的类型的别名
            属性: type:需要设置别名的类型的全类名
            alias:设置此类型的别名,若不设置此属性,该类型拥有默认的别名,即类名且不区分大小写
            若设置此属性,此时该类型的别名只能使用alias所设置的值
        -->
        <!--<typeAlias type="com.atguigu.mybatis.bean.User"></typeAlias>-->
        <!--<typeAlias type="com.atguigu.mybatis.bean.User" alias="abc"> </typeAlias>-->
        <!--以包为单位,设置改包下所有的类型都拥有默认的别名,即类名且不区分大小写-->
        <package name="com.xxxx.lln.pojo"/>
    </typeAliases>

4.14 使用注解增删改查

mybatis不建议,我也不想学,用到了百度吧。。。

5.动态SQL

Mybatis框架的动态SQL技术是一种根据特定条件动态拼装SQL语句的功能,它存在的意义是为了解决拼接SQL语句字符串时的痛点问题。

if

if标签可通过test属性的表达式进行判断,若表达式的结果为true,则标签中的内容会执行;反之标签中的内容不会执行。

    <!--List<Emp> getEmpListByMoreTJ(Emp emp);-->
    <select id="getEmpListByMoreTJ" resultType="Emp">
        select * from t_emp where 1=1
        <if test="ename != '' and ename != null">
            and ename = #{ename}
        </if>
        <if test="age != '' and age != null">
            and age = #{age}
        </if>
        <if test="sex != '' and sex != null">
            and sex = #{sex}
        </if>
    </select>

where

    <select id="getEmpListByMoreTJ2" resultType="Emp">
        select * from t_emp
        <where>
            <if test="ename != '' and ename != null">
                ename = #{ename}
            </if>
            <if test="age != '' and age != null">
                and age = #{age}
            </if>
            <if test="sex != '' and sex != null">
                and sex = #{sex}
            </if>
        </where>
    </select>
  • where和if一般结合使用:
    • 若where标签中的if条件都不满足,则where标签没有任何功能,即不会添加where关键字
    • 若where标签中的if条件满足,则where标签会自动添加where关键字,并将条件最前方多余的 and去掉
    • 注意:where标签不能去掉条件最后多余的and

set

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

trim

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

  • prefix属性:指定要动态添加的前缀
  • suffix属性:指定要动态添加的后缀
  • prefixOverrides属性:指定要动态去掉的前缀,使用“|”分隔有可能的多个值
  • suffixOverrides属性:指定要动态去掉的后缀,使用“|”分隔有可能的多个值
<!-- List<Employee> selectEmployeeByConditionByTrim(Employee employee) -->
<select id="selectEmployeeByConditionByTrim" resultType="com.atguigu.mybatis.entity.Employee">
    select emp_id,emp_name,emp_age,emp_salary,emp_gender
    from t_emp
    
    <!-- prefix属性指定要动态添加的前缀 -->
    <!-- suffix属性指定要动态添加的后缀 -->
    <!-- prefixOverrides属性指定要动态去掉的前缀,使用“|”分隔有可能的多个值 -->
    <!-- suffixOverrides属性指定要动态去掉的后缀,使用“|”分隔有可能的多个值 -->
    <!-- 当前例子用where标签实现更简洁,但是trim标签更灵活,可以用在任何有需要的地方 -->
    <trim prefix="where" suffixOverrides="and|or">
        <if test="empName != null">
            emp_name=#{empName} and
        </if>
        <if test="empSalary &gt; 3000">
            emp_salary>#{empSalary} and
        </if>
        <if test="empAge &lt;= 20">
            emp_age=#{empAge} or
        </if>
        <if test="empGender=='male'">
            emp_gender=#{empGender}
        </if>
    </trim>
</select>

choose、when、otherwise

choose、when、otherwise相当于if…else if…else

    <!--List<Emp> getEmpListByChoose(Emp emp);-->
    <select id="getEmpListByChoose" resultType="Emp">
        select * from t_emp
        <where>
            <choose>
                <when test="ename != '' and ename != null">
                    ename = #{ename}
                </when>
                <when test="age != '' and age != null">
                    age = #{age}
                </when>
                <when test="sex != '' and sex != null">
                    sex = #{sex}
                </when>
                <when test="email != '' and email != null">
                    email = #{email}
                </when>
                <otherwise>1=1</otherwise>
            </choose>
        </where>
    </select>

foreach

    <!--int insertMoreEmp(List<Emp> emps);-->
    <insert id="insertMoreEmp">
        insert into t_emp values
        <foreach collection="emps" item="emp" separator=",">
            (null,#{emp.ename},#{emp.age},#{emp.sex},#{emp.email},null)
        </foreach>
    </insert>
    
    <!--int deleteMoreByArray(int[] eids);-->
    <delete id="deleteMoreByArray">
        delete from t_emp where
        <foreach collection="eids" item="eid" separator="or">
            eid = #{eid}
        </foreach>
    </delete> 
    
    <!--int deleteMoreByArray(int[] eids);-->
    <delete id="deleteMoreByArray">
        delete from t_emp where eid in
        <foreach collection="eids" item="eid" separator="," open="(" close=")">
            #{eid}
        </foreach>
    </delete>

sql片段

抽取重复的SQL片段

<!-- 使用sql标签抽取重复出现的SQL片段 -->
<sql id="empColumns">
    eid,ename,age,sex,did
</sql>

引用已抽取的SQL片段

<!-- 使用include标签引用声明的SQL片段 -->
<select id="xxx" resultMap="deptEmpMap">
    select <include refid="empColumns"></include> from t_emp
</select>

6. MyBatis的缓存

一级缓存

默认开启

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

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

不同的SqlSession对应不同的一级缓存
同一个SqlSession但是查询条件不同
同一个SqlSession两次查询期间执行了任何一次增删改操作
同一个SqlSession两次查询期间手动清空了缓存 (sqlsession.clearCache())

二级缓存

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

二级缓存开启的条件:

a>在核心配置文件中,设置全局配置属性cacheEnabled=“true”,默认为true,不需要设置

b>在映射文件中设置标签< cache />

c>二级缓存必须在SqlSession关闭或提交之后有效

d>查询的数据所转换的实体类类型必须实现序列化的接口

使二级缓存失效的情况:
两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效

二级缓存的相关配置

在mapper配置文件中添加的cache标签可以设置一些属性:

eviction属性:缓存回收策略 默认的是 LRU。
LRU(Least Recently Used) – 最近最少使用的:移除最长时间不被使用的对象。
FIFO(First in First out) – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。

flushInterval属性:刷新间隔,单位毫秒 默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新

size属性:引用数目,正整数 代表缓存最多可以存储多少个对象,太大容易导致内存溢出

readOnly属性:只读,true/false
true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。
false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是 false。

MyBatis缓存查询的顺序

先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。
如果二级缓存没有命中,再查询一级缓存
如果一级缓存也没有命中,则查询数据库
SqlSession关闭之后,一级缓存中的数据会写入二级缓存

整合第三方缓存EHCache

a>添加依赖

<!-- Mybatis EHCache整合包 --> 
<dependency>    
	<groupId>org.mybatis.caches</groupId>    
	<artifactId>mybatis-ehcache</artifactId>    
	<version>1.2.1</version> 
</dependency> 
<!-- slf4j日志门面的一个具体实现 -->
<dependency>    
	<groupId>ch.qos.logback</groupId>    
	<artifactId>logback-classic</artifactId>    
	<version>1.2.3</version> 
</dependency>

b>各jar包功能
在这里插入图片描述

c>创建EHCache的配置文件ehcache.xml

<?xml version="1.0" encoding="utf-8" ?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:noNamespaceSchemaLocation="../config/ehcache.xsd"> 
    <!-- 磁盘保存路径 --> 
    <diskStore path="D:\atguigu\ehcache"/> 
    <defaultCache 
            maxElementsInMemory="1000" 
            maxElementsOnDisk="10000000" 
            eternal="false" 
            overflowToDisk="true" 
            timeToIdleSeconds="120" 
            timeToLiveSeconds="120" 
            diskExpiryThreadIntervalSeconds="120" 
            memoryStoreEvictionPolicy="LRU"> 
    </defaultCache> 
</ehcache>

d>设置二级缓存的类型

<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>


e>加入logback日志

存在SLF4J时,作为简易日志的log4j将失效,此时我们需要借助SLF4J的具体实现logback来打印日志。
创建logback的配置文件logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
    <!-- 指定日志输出的位置 -->
    <appender name="STDOUT"
              class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
        	<!-- 日志输出的格式 -->
        	<!-- 按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体内容、换行 -->
            <pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern>
        </encoder>
    </appender>

    <!--设置全局日志级别。日志级别按顺序分别是:DEBUG、INFO、WARN、ERROR -->
    <!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 -->
    <root level="DEBUG">
        <!-- 指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender -->
        <appender-ref ref="STDOUT"/>
    </root>

    <!-- 根据特殊需求指定局部日志级别 -->
    <logger name="com.atguigu.crowd.mapper" level="DEBUG"/>
    
</configuration>

f>EHCache配置文件说明

在这里插入图片描述

7. MyBatis的逆向工程

  • 正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表。Hibernate是支持正向工程 的。
  • 逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成如下资源:
    • Java实体类
    • Mapper接口
    • Mapper映射文件

a>添加依赖和插件

    <!-- 依赖MyBatis核心包 -->
    <dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.7</version>
        </dependency>
    </dependencies>
    <!-- 控制Maven在构建过程中相关配置 -->
    <build>
        <!-- 构建过程中用到的插件 -->
        <plugins>
            <!-- 具体插件,逆向工程的操作是以构建过程中插件形式出现的 -->
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.0</version>
                <!-- 插件的依赖 -->
                <dependencies>
                    <!-- 逆向工程的核心依赖 -->
                    <dependency>
                        <groupId>org.mybatis.generator</groupId>
                        <artifactId>mybatis-generator-core</artifactId>
                        <version>1.3.2</version>
                    </dependency>
                    <!-- 数据库连接池 -->
                    <dependency>
                        <groupId>com.mchange</groupId>
                        <artifactId>c3p0</artifactId>
                        <version>0.9.2</version>
                    </dependency>
                    <!-- MySQL驱动 -->
                    <dependency>
                        <groupId>mysql</groupId>
                        <artifactId>mysql-connector-java</artifactId>
                        <version>8.0.23</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>

b>创建MyBatis的核心配置文件

c>创建逆向工程的配置文件
文件名必须是:generatorConfig.xml

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
    <!--targetRuntime: 执行生成的逆向工程的版本
    MyBatis3Simple: 生成基本的CRUD(清新简洁版)
    MyBatis3: 生成带条件的CRUD(奢华尊享版) -->
    <context id="DB2Tables" targetRuntime="MyBatis3Simple">
        <!-- 数据库的连接信息 -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/mybatis"
                        userId="root"
                        password="123456">
        </jdbcConnection>
        <!-- javaBean的生成策略-->
        <javaModelGenerator targetPackage="com.atguigu.mybatis.pojo" targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true" />
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        <!-- SQL映射文件的生成策略 -->
        <sqlMapGenerator targetPackage="com.atguigu.mybatis.mapper"
                         targetProject=".\src\main\resources">
            <property name="enableSubPackages" value="true" />
        </sqlMapGenerator>
        <!-- Mapper接口的生成策略 -->
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.atguigu.mybatis.mapper"
                             targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true" />
        </javaClientGenerator>
        <!-- 逆向分析的表 -->
        <!-- tableName设置为*号,可以对应所有表,此时不写domainObjectName -->
        <!-- domainObjectName属性指定生成出来的实体类的类名 -->
        <table tableName="t_emp" domainObjectName="Emp"/>
        <table tableName="t_dept" domainObjectName="Dept"/>
    </context>

</generatorConfiguration>

d>执行MBG插件的generate目标

在这里插入图片描述

8. MyBatis X

9. 分页

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2219324.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

算法Day-4

24. 两两交换链表中的节点 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&#xff09;。 示例 1&#xff1a; 输入&#xff1a;head [1,2,…

Windows10去掉隐藏文件仍找不到hosts文件的解决办法

正常情况下hosts文件在目录C:\Windows\System32\drivers\etc中&#xff0c;最近新装的Windows10系统发现该目录下没有hosts文件。 执行如下命令hosts文件出现&#xff1a; 执行 for /f %P in (dir %windir%\WinSxS\hosts /b /s) do copy %P %windir%\System32\drivers\etc &am…

ubuntu 20.04 网卡启用后,只有ipv6 没有 ipv4 无法上网

&#x1f3c6;本文收录于《全栈Bug调优(实战版)》专栏&#xff0c;主要记录项目实战过程中所遇到的Bug或因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&am…

JS | JS之深入理解客户区尺寸client系列属性

目录 一、客户区大小 clientHeight clientWidth clientLeft clientTop 二、页面大小 三、注意事项 关于元素尺寸&#xff0c;一般地&#xff0c;有偏移大小offset、客户区大小client和滚动大小scroll。前文已经介绍过偏移属性&#xff0c;后文将介绍scroll滚动大小&…

责任链模式下,解决开闭原则问题实践

前言 在现代软件工程中&#xff0c;设计模式是解决常见问题的有效工具之一。它们吸收了前人的经验&#xff0c;不仅帮助开发者编写更清晰、更可维护的代码&#xff0c;还能促进团队之间的沟通和协作。责任链模式&#xff08;Chain of Responsibility Pattern&#xff09;作为一…

C语言 | Leetcode C语言题解之第485题最大连续1的个数

题目&#xff1a; 题解&#xff1a; int findMaxConsecutiveOnes(int* nums, int numsSize) {int maxCount 0, count 0;for (int i 0; i < numsSize; i) {if (nums[i] 1) {count;} else {maxCount fmax(maxCount, count);count 0;}}maxCount fmax(maxCount, count);…

最近网站频繁跳转到黑产网站,怀疑是51.la统计代码的问题

最近我的几个网站&#xff0c;都出现了一个问题&#xff0c;就是访问的时候会莫名其妙的跳转到黑产网站。 通过排查了网页代码&#xff0c;发现网页都有一个共同点&#xff0c;就是使用了51.la统计。为什么会怀疑是51la统计代码问题&#xff1f;因为我的网页只有统计代码外没有…

Vulnhub打靶-jangow

基本信息 靶机下载&#xff1a;https://www.vulnhub.com/entry/jangow-101,754/ 攻击机器&#xff1a;192.168.20.128&#xff08;Windows操作系统&#xff09;& 192.168.20.138&#xff08;kali&#xff09; 提示信息&#xff1a;这个框的秘密是枚举&#xff01; 靶机…

汽车票预订系统:SpringBoot框架的优势

6系统测试 6.1概念和意义 测试的定义&#xff1a;程序测试是为了发现错误而执行程序的过程。测试(Testing)的任务与目的可以描述为&#xff1a; 目的&#xff1a;发现程序的错误&#xff1b; 任务&#xff1a;通过在计算机上执行程序&#xff0c;暴露程序中潜在的错误。 另一个…

软考(网工)——网络安全

文章目录 &#x1f550;网络安全基础1️⃣网络安全威胁类型2️⃣网络攻击类型 &#x1f551;现代加密技术1️⃣私钥密码/对称密码体制2️⃣对称加密算法总结3️⃣公钥密码/非对称密码4️⃣混合密码5️⃣国产加密算法 - SM 系列6️⃣认证7️⃣基于公钥的认证 &#x1f552;Hash …

证件照小程序源码,前后端稳定运行

演示&#xff1a;证寸照制作 运行环境: Linux Nginx PHP >5.6 MySQL>5.6 安装步骤: 1.下载源码上传至你的服务器宝塔面板 2.直接添加站点选择源码目录&#xff0c;新建数据库 3.设置代码执行目录为/web 4.在浏览器中输入你的域名&#xff0c;会提示安装&#xff0c;填写…

Flink消费Kafka实时写入Doris

本文模拟实际生产环境&#xff0c;通过FileBeat采集日志信息到Kafka&#xff0c;再通过Flink消费Kafka实时写入Doris。 文章目录 Filebeat采集日志到KafkaFlink消费Kafka实时写入Doris总结 Filebeat采集日志到Kafka 常见的日志采集工具有以下几种&#xff1a;Flume、Logstash和…

自动机器学习(AutoML)

utoML是PAI的提供的自动寻找超参组合的机器学习增强型服务。您在训练模型时&#xff0c;如果超参组合复杂度过高&#xff0c;需大量训练资源和手工调试工作&#xff0c;可以使用AutoML来节省模型调参时间&#xff0c;提升模型调优效率和模型质量。 基础概念 超参数&#xff1a;…

Spring 获取URL中的参数

PathVariable 获取多个变量参数重命名 获取 URL 中的 Id&#xff0c;可以根据 Id 到数据库中筛选相应的内容 Id 的类型是可以定义的&#xff0c;这里定义为 Integer 类型 并且在 RequestMapping中需要定义路径 {articleId} PathVariable 从路径中获取 变量 获取多个变量 参数…

【软件运行类文档】项目试运行方案,试运行计划书(word原件)

一、 试运行目的 &#xff08;一&#xff09; 系统功能、性能与稳定性考核 &#xff08;二&#xff09; 系统在各种环境和工况条件下的工作稳定性和可靠性 &#xff08;三&#xff09; 检验系统实际应用效果和应用功能的完善 &#xff08;四&#xff09; 健全系统运行管理体制&…

jmeter发送post请求

在jmeter中&#xff0c;有两种常用的请求方式&#xff0c;get和post.它们两者的区别在于get请求的参数一般是放在路径中&#xff0c;可以使用用户自定义变量和函数助手等方式进行参数化&#xff0c;而post请求的参数不能随url发送&#xff0c;而是作为请求体提交给服务器。而在…

打开游戏提示丢失(或找不到)XINPUT1_3.DLL的多种解决办法

xinput1_3.dll是一个动态链接库&#xff08;DLL&#xff09;文件&#xff0c;它在Windows操作系统中扮演着重要的角色。该文件作为系统库文件&#xff0c;通常存放于C:\Windows\System32目录下&#xff08;对于32位系统&#xff09;或C:\Windows\SysWOW64目录下&#xff08;对于…

PPT自动化:快速更换PPT图片(如何保留原图片样式等参数更换图片)

文章目录 📖 介绍 📖🏡 演示环境 🏡📒 PPT更换图片 📒1. 安装 `python-pptx` 模块2. 加载PPT文件3. 查找并替换图片3.1 查找图片形状3.2 获取原图片的样式和位置3.3 替换图片4. 保存修改后的PPT文件5. 设置图片的相关参数5.1 设置透明度5.2 设置边框🚀 保留所有参…

FFmpeg 4.3 音视频-多路H265监控录放C++开发四 :RGB颜色

一 RGB 的意义&#xff1f; 为什么要从RGB 开始讲起呢&#xff1f; 因为最终传输到显卡显示器的颜色都是RGB 即使能处理YUV的API&#xff0c;本质上也是帮你做了从 YUV 到 RGB的转换。 RGB888 表示 R 占8bit&#xff0c;G 占8bit&#xff0c;B 占8bit&#xff0c;也就是每一…

内网微隔离,三步防横移——基于微隔离的横移攻击防护方案

引言 在网络攻防的战场上&#xff0c;横移攻击如同隐匿的刺客&#xff0c;一旦突破边界&#xff0c;便在内网肆意游走&#xff0c;给企业网络安全带来致命损害。当前多数企业数据中心内网如同未设防的城池&#xff0c;面对突破外围边界的“刺客”毫无招架之力。蔷薇灵动基于多…