Mybatis学习笔记

Mybatis学习笔记

一、简介

  • Mybatis历史

    • MyBatis最初是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation迁 移到了Google Code。随着开发团队转投Google Code旗下, iBatis3.x正式更名为MyBatis。代码于 2013年11月迁移到Github。 iBatis一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。 iBatis提供的持久层框架 包括SQL Maps和Data Access Objects(DAO)。
  • Mybatis特性

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

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

  • github官网下载地址

二、Mybatis基础功能

一、配置数据库相关信息

spring:
  # 配置项目名称
  application:
    name: mybatis-demo
  # 配置数据库连接
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis
    username: root
    password: 1234
# 配置mapper接口目录下的所有日志级别都是debug(可以看到sql语句)
logging:
  level:
    pers:
      jl:
        mapper: debug
# 给包下所有实体设置别名,后续映射文件就可以写别名
mybatis:
  type-aliases-package: pers.jl.entity

二、Mybatis的映射文件的讲解和编写

  1. 创建User表、实体类

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
        private Integer id;
        private String name;
        private String password;
        private String sex;
        private String email;
        private String age;
    }
    
  2. 创建UserMapper接口

    public interface UserMapper {
        int userInsert();
    }
    
  3. 映射文件

    注意映射文件放在资源目录下时,创建目标要使用pers/jl/mapper,用斜线,不能用点

    <?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="pers.jl.mapper.UserMapper">
        <insert id="userInsert">
            insert into t_user values (null,'小明','1234','男','1235@qq.com','23')
        </insert>
    </mapper>
    
  4. 启动类添加注解

    @SpringBootApplication
    @MapperScan("pers.jl.mapper")//扫描所有mapper接口,生成代理对象,也会扫描映射文件,因为编译后都在一个包下
    public class MybatisDemoApplication {
        public static void main(String[] args) {
            SpringApplication.run(MybatisDemoApplication.class, args);
        }
    }
    

三、Mybatis实现增删改查

mapper接口

public interface UserMapper {
    int userInsert();
    int userUpdate();
    int userDelete();
    User getUserById();
    List<User> getAllUser();
}

映射文件

<?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="pers.jl.mapper.UserMapper">
    <!--添加数据-->
    <insert id="userInsert">
        insert into t_user values (null,'小明','1234','男','1235@qq.com','23')
    </insert>
    <!--更新数据-->
    <update id="userUpdate">
        update t_user set name = '小红',password = '5678',sex = '女' where id = 2
    </update>
    <!--删除数据-->
    <delete id="userDelete">
        delete from t_user where id = 3
    </delete>
    <!--根据id查询用户-->
    <select id="getUserById" resultType="pers.jl.entity.User">
        select * from t_user where id = 2
    </select>
    <!--查询所有用户-->
    <select id="getAllUser" resultType="pers.jl.entity.User">
        select * from t_user
    </select>
</mapper>

四、Mybatis获取参数数值的两种方式

  • Mybatis获取参数的两种方式:${} 和 #{}

    ${}本质是字符串拼接,#{}本质是占位符赋值

  • ${}使用字符串拼接的方式拼接sql,若为字符串类型或日期类型的字段进行赋值时,需要手动加单引 号

  • 但是#{}使用占位符赋值的方式拼接sql,此时为字符串类型或日期类型的字段进行赋值时,可以自动添加单引号

1. 单个字面量类型的参数
  • 就是一个形参的情况。

  • 直接使用#{}或者${}获取变量值,但是要注意后者是字符串拼接,所以需要手动添加单引号,然后就是变量名不重要,它会自动把参数值传给{}里面。

mapper接口

    User getUserByName(String name);

映射文件

    <!--根据用户名查询用户,单个字面量传参-->
    <select id="getUserByName" resultType="User">
        select * from t_user where name = #{name}
    </select>
2.多个字面量类型的参数
  • 获取多个参数时,mybatis会把所有参数封装成一个Map集合,可以使用参数名来获取参数,也可以使用param1、param2来获取。

mapper接口

    User getUserByNameAndPassword(String name,String password);

映射文件

    <!--根据用户名、密码查询用户-->
    <select id="getUserByNameAndPassword" resultType="User">
        <!--select * from t_user where name = #{param1} and password = #{param2}-->
        select * from t_user where name = #{name} and password = #{password}
    </select>
3.map集合类型的参数
  • 当用户有多个参数需要传递时,我们可以选择将所有参数都封装到一个Map集合中,每个参数值有对应的键,然后直接使用#{}取值即可。

mapper接口

    <T> User getUserByMap(Map<String,T> map);

映射文件

    <!--根据map集合查询用户对象-->
    <select id="getUserByMap" resultType="User">
        select * from t_user where name = #{n} and password = #{p}
    </select>
4.实体类型的参数
  • 当参数是实体对象时,拼接SQL时,直接使用#{}获取传参实体属性就可以获取参数值。

mapper接口

    int insertUser(User user);

映射文件

    <!--新增用户-->
    <insert id="insertUser">
        insert into t_user values (null,#{name},#{password},#{sex},#{email},#{age})
    </insert>
5.使用@Param标识参数
  • 在mapper接口上使用@Param注解标识参数,就是将所有参数放到Map集合中,然后使用@Param的值作为键,参数值作为值,然后在映射文件可以使用#{}直接引用

mapper接口

    User getUserByParam(@Param("na") String name, @Param("pa") String password);

映射文件

    <!--获取用户信息,使用注解起别名-->
    <select id="getUserByParam" resultType="User">
        select * from t_user where name = #{na} and password = #{pa}
    </select>

五、Mybatis的各种查询功能

1. 查询一个实体类对象
2. 查询一个list集合
  • 根据查询结果,我们需要选择合适的类型去接收。
  • 比如:查询结果是一个对象,我们可以选择用一个对象或者对象集合接收;如果查询结果是一个对象集合,那么我们只能用对象集合来接收
3.查询单个数据
  • Mybatis中设置了默认的类型别名(常用)

    • java.lang.Integer–>int、integer(大小写均可)

    • int–>int、integer(大小写均可)

    • Map–>map

    • String–>string

mapper接口

    Integer getCount();

映射文件

    <!--查询数据数量-->
    <select id="getCount" resultType="java.lang.Integer">
        select count(*) from t_user
    </select>
4.查询一条数据为map集合
  • 当我们查询的数据没有实体对象时,我们可以选择使用map来接收,或者我们是联表查询,也可以使用map来封装结果。

mapper接口

    <M> Map<String,M> getUserByIdToMap(Integer id);

映射文件

    <!--查询用户信息,封装成一个map集合-->
    <select id="getUserByIdToMap" resultType="java.util.Map">
        select * from t_user where id = #{id}
    </select>
5.查询多条数据为map集合
  • 当我们查询多条数据,使用map来封装时,我们可以使用List集合来封装每一个Map数据;也可以使用一个map来封装所有map数据,键是数据的id,值就是map数据,相当于就是map里面装的map。

mapper接口

//    List<Map<String,O>> getUserListByMap();
    @MapKey("id")//使用id作为键,单挑用户数据作为值
    Map<String,O> getUserListByMap();

映射文件

    <!--查询多条用户数据封装成Map集合-->
    <select id="getUserListByMap" resultType="map">
        select *
        from t_user;
    </select>

六、特殊SQL的执行

1. 模糊查询
  • 模糊查询有三种方式,第一种使用${}获取参数,第二种使用字符串函数拼接,第三种使用双引号把%包裹然后使用#{}获取参数,最常用的是第三种方式。

mapper接口

    List<User> getLikeUser(String name);

映射文件

    <!--模糊查询-->
    <select id="getLikeUser" resultType="User">
        <!-- select * from t_user where name like '%${name}%'-->
        <!--select * from t_user where name like concat('%',#{name},'%')-->
        select * from t_user where name like "%"#{name}"%"
    </select>
2. 批量删除
  • 注意批量删除数据时,只能使用${}来获取参数值
  • 我们传给sql的是一个用逗号隔开的id字符串,在sql中我们使用${}就可以获取字符串中的每个id

mapper接口

    int removeInId(String ids);

映射文件

    <!--批量删除-->
    <delete id="removeInId">
        delete from t_user where id in (${ids})
    </delete>
3. 动态设置表名
  • 有些时候我们需要把一张表水平切割成多个表,所以我们有时候需要指定表名来查询数据,我们只能用${}来获取表名。

mapper接口

    List<User> getAllUserWithTableName(@Param("tableName") String tableName);

映射文件

    <!--动态设置表名-->
    <select id="getAllUserWithTableName" resultType="User">
        select * from ${tableName}
    </select>
4. 添加功能获取自增的主键
  • 在映射文件上设置两个参数,就可以获取到我们的自增主键id,有些时候插入数据,我们后续需要使用这个自增的id来进行其他业务,就需要使用这个功能。
  • useGeneratedKeys="true"表示SQL使用的是自增主键id
  • keyProperty="id"表示将生成的主键id赋值给实体对象的属性id中。

mapper接口

    int insertUserReturnId(User user);

映射文件

    <!--新增用户,可以获取自增id-->
    <insert id="insertUserReturnId" useGeneratedKeys="true" keyProperty="id">
    insert into t_user values (null,#{name},#{password},#{sex},#{email},#{age})
    </insert>

七、Mybatis的自定义映射resultMap

1.数据库字段名与实体类属性名不一致的解决方案(三种)
  • 数据库字段符合数据库设计规则,实体类名符合实体类命名规则,所以不能随便更改。

  • 解决数据库字段名与实体类属性不一致,导致数据无法封装的情况

1.设置字段别名对应实体类属性

mapper接口

    List<Emp> getAllEmp();

映射文件

    <!--设置别名查询数据-->
    <select id="getAllEmp" resultType="Emp">
        select eid,emp_name empName,age,sex,email from t_emp
    </select>
2.配置全局配置,下划线自动映射成驼峰命名
mybatis:
  # 给包下所有实体设置别名,后续映射文件就可以写别名
  type-aliases-package: pers.jl.entity
  configuration:
    # 下划线自动映射成驼峰命名
    map-underscore-to-camel-case: true
3.自定义映射关系
  • resultType只能解决默认的映射关系,也就是字段名匹配属性名的情况,如果不匹配,就不能用。

自定义映射标签

  • resultMap是自定义映射关系
  • id是映射关系的唯一标识
  • type是设置映射关系中的实体类类型,也就是需要映射的实体类

子标签

  • id是设置主键映射关系
  • result是设置普通字段的映射关系

属性

  • property是设置映射关系中的属性名,必须是type属性所设置的实体类中的属性
  • colume是设置映射关系中的字段名,必须是sql语句查询出的字段名
    <resultMap id="mapEmp" type="Emp">
        <id property="eid" column="eid"></id>
        <result property="empName" column="emp_name"></result>
        <result property="age" column="age"></result>
        <result property="sex" column="sex"></result>
        <result property="email" column="email"></result>
        <result property="did" column="did"></result>
    </resultMap>

    <!--自定义映射查询数据-->
    <select id="getAllEmp" resultMap="mapEmp">
        select eid,emp_name,age,sex,email from t_emp
    </select>
2.处理多对一情况
  • 实体类多对一时,我们在多的那一个实体类中存放一的实体类属性。
1.级联查询

映射文件

    <resultMap id="cascading" type="Emp">
        <id property="eid" column="eid"></id>
        <result property="empName" column="emp_name"></result>
        <result property="age" column="age"></result>
        <result property="sex" column="sex"></result>
        <result property="email" column="email"></result>
        <result property="did" column="did"></result>
        <result property="dept.did" column="did"></result>
        <result property="dept.deptName" column="dept_name"></result>
    </resultMap>

    <!--左外连接查询员工以及员工对应的部门信息-->
    <select id="getEmpWithDept" resultMap="cascading">
        select * from t_emp left join t_dept on t_emp.did = t_dept.did where t_emp.eid = #{id}
    </select>
2.使用多对一标签
  • association是处理多对一的映射关系的标签
  • property是待赋值实体类的属性名,也就是Emp的dept属性
  • javaType是该属性的类型,指定为部门Dept
    <resultMap id="cascading" type="Emp">
        <id property="eid" column="eid"></id>
        <result property="empName" column="emp_name"></result>
        <result property="age" column="age"></result>
        <result property="sex" column="sex"></result>
        <result property="email" column="email"></result>
        <result property="did" column="did"></result>
        <association property="dept" javaType="Dept">
            <id property="did" column="did"></id>
            <result property="deptName" column="dept_name"></result>
        </association>
    </resultMap>

    <!--左外连接查询员工以及员工对应的部门信息-->
    <select id="getEmpWithDept" resultMap="cascading">
        select * from t_emp left join t_dept on t_emp.did = t_dept.did where t_emp.eid = #{id}
    </select>
3. 分步查询
  • 直接查询员工信息,然后根据员工的部门id查询部门信息,最后封装到结果中

  • property就是Emp的属性

  • select是查询当前属性所调用的sql语句,需要使用唯一标识全类名+方法名

  • column是传递给子查询的查询条件,这里因为需要根据员工部门did查询部门信息,所有传递did

  • 分步查询的优点是可以开启延迟加载,减少不必要的sql语句执行。

    全局的分步查询开启延迟加载

    mybatis:
      # 给包下所有实体设置别名,后续映射文件就可以写别名
      type-aliases-package: pers.jl.entity
      configuration:
        #下划线自动映射成驼峰命名
        map-underscore-to-camel-case: true
        # 开启延迟加载
        lazy-loading-enabled: true
    

    如果在全局延迟加载的条件下,某个分步查询sql不需要延迟加载,就在映射文件那里使用fetchType指定eager立即加载

        <resultMap id="step" type="Emp">
            <id property="eid" column="eid"></id>
            <result property="empName" column="emp_name"></result>
            <result property="age" column="age"></result>
            <result property="sex" column="sex"></result>
            <result property="email" column="email"></result>
            <result property="did" column="did"></result>
            <association property="dept"
                         select="pers.jl.mapper.DeptMapper.getDeptByDid"
                         column="did"
                         fetchType="eager"></association>
        </resultMap>
    

先查询员工信息

    <resultMap id="step" type="Emp">
        <id property="eid" column="eid"></id>
        <result property="empName" column="emp_name"></result>
        <result property="age" column="age"></result>
        <result property="sex" column="sex"></result>
        <result property="email" column="email"></result>
        <result property="did" column="did"></result>
        <association property="dept"
                     select="pers.jl.mapper.DeptMapper.getDeptByDid"
                     column="did"></association>
    </resultMap>

    <!--分步查询员工信息-->
    <select id="getEmpAndDeptByStepOne" resultMap="step">
        select * from t_emp where eid = #{eid}
    </select>

第二部查询部门调用的SQL

    <!--根据did查询部门信息-->
    <select id="getDeptByDid" resultType="Dept">
        select * from t_dept where did = #{did}
    </select>
3.处理一对多情况
  • 实体类一对多时,我们在一的那一个实体类中存放一个集合存放多的实体类即可。
1.使用一对多标签
  • oftype就是指定集合中存放的实体类型。
  • 注意这时员工信息不用封装部门id了,不然会循环套娃。
  • 也是查出所有数据,然后返回自定义映射封装的结果

mapper接口

    Dept getDeptById(Integer did);

映射文件

    <resultMap id="getDeptAndEmp" type="Dept">
        <id property="did" column="did"></id>
        <result property="deptName" column="dept_name"></result>
        <collection property="emps" ofType="Emp">
            <id property="eid" column="eid"></id>
            <result property="empName" column="emp_name"></result>
            <result property="age" column="age"></result>
            <result property="sex" column="sex"></result>
            <result property="email" column="email"></result>
        </collection>
    </resultMap>


    <!--根据did左外连接查询部门及其部门的员工-->
    <select id="getDeptById" resultMap="getDeptAndEmp">
        select * from t_dept left join t_emp on t_dept.did = t_emp.did where t_dept.did = #{did}
    </select>
2.分步查询

mapper接口

    Dept getDeptStepOne(Integer did);

映射文件

    <resultMap id="getDeptOne" type="Dept">
        <id property="did" column="did"></id>
        <result property="deptName" column="dept_name"></result>
        <collection property="emps"
                    select="pers.jl.mapper.EmpMapper.getEmpByDid"
                    column="did"></collection>
    </resultMap>

    <!--分步查询部门信息-->
    <select id="getDeptStepOne" resultMap="getDeptOne">
        select * from t_dept where did = #{did}
    </select>

调用的sql

    <!--根据部门did查询员工集合-->
    <select id="getEmpByDid" resultType="Emp">
        select * from t_emp where did = #{did}
    </select>

八、Mybatis的动态SQL

  • Mybatis框架的动态SQL技术是一种根据特定条件动态拼装SQL语句的功能,它存在的意义是为了解决拼接SQL语句字符串时的痛点问题
if标签
  • test里面就是拼接条件,可以直接写参数的属性,不用使用#{}
  • where 1=1,无论后面是否有条件成立,sql都可以拼接成功,成功执行
    <!--多条件查询员工信息-->
    <select id="getEmpByCondition" resultType="Emp">
        select * from t_emp where 1=1
        <if test="empName !=null and empName!= ''">
            and emp_name = #{empName}
        </if>
        <if test="age !=null and age!= ''">
            and age = #{age}
        </if>
        <if test="sex !=null and sex!= ''">
            and sex = #{sex}
        </if>
        <if test="email !=null and email!= ''">
            and email = #{email}
        </if>;
    </select>
where标签
  • 当where标签中有内容时,会自动生成where关键字,并且将内容前多余的and或or去掉。(注意不能去掉内容后面的,所以and或or都写前面)。
  • 当where标签中没有内容时,此时的where标签没有任何效果
    <!--多条件查询员工信息-->
    <select id="getEmpByCondition" resultType="Emp">
        select * from t_emp
        <where>
            <if test="empName !=null and empName!= ''">
                and emp_name = #{empName}
            </if>
            <if test="age !=null and age!= ''">
                and age = #{age}
            </if>
            <if test="sex !=null and sex!= ''">
                and sex = #{sex}
            </if>
            <if test="email !=null and email!= ''">
                and email = #{email}
            </if>
        </where>
    </select>
trim标签
  • prefix | suffix 在内容前面或后面添加指定内容
  • suffixOverrides | prefixOverrides 去掉内容前面或后面的指定内容
  • 如果trim标签中没有内容,trim标签没有任何效果
    <!--多条件查询员工信息-->
    <select id="getEmpByCondition" resultType="Emp">
        select * from t_emp
            <trim prefix="where" suffixOverrides="and|or">
                <if test="empName !=null and empName!= ''">
                    emp_name = #{empName} and
                </if>
                <if test="age !=null and age!= ''">
                    age = #{age} and
                </if>
                <if test="sex !=null and sex!= ''">
                    sex = #{sex} or
                </if>
                <if test="email !=null and email!= ''">
                    email = #{email}
                </if>
            </trim>
    </select>
choose、when、otherwise标签
  • 相当于if…else if …else,也类似switch…case的作用,只会寻找一个满足条件的when,如果没有就拼接otherwise中的条件。
  • when可以有多个,otherwise最多一个
    <!--多条件查询员工信息-->
    <select id="getEmpByCondition" resultType="Emp">
        select * from t_emp
        <trim prefix="where">
            <choose>
                <when test="empName !=null and empName!= ''">
                    emp_name = #{empName}
                </when>
                <when test="age !=null and age!= ''">
                    age = #{age}
                </when>
                <when test="sex !=null and sex!= ''">
                    sex = #{sex}
                </when>
                <when test="email !=null and email!= ''">
                    email = #{email}
                </when>
                <otherwise>
                    did = 1
                </otherwise>
            </choose>
        </trim>
    </select>
foreach标签
  • collection指定访问数组或集合
  • item是数组或集合中的每一个数据
  • separator可以给循环体对象之间加分隔符
通过数组实现批量删除功能

方式一

    <!--根据数组id批量删除员工-->
    <delete id="removeEmpByArrays">
        delete from t_emp where eid in
        <foreach collection="eids" item="eid" open="(" close=")" separator=",">
            #{eid}
        </foreach>
    </delete>

方式二

    <!--根据数组id批量删除员工-->
    <delete id="removeEmpByArrays">
        delete from t_emp where
        <foreach collection="eids" item="eid" separator="or">
            eid = #{eid}
        </foreach>
    </delete>
通过集合实现批量添加功能
    <!--根据集合批量添加员工-->
    <insert id="addEmpByList">
        insert into t_emp values
        <foreach collection="list" item="emp" separator=",">
            (null,#{emp.empName},#{emp.age},#{emp.sex},#{emp.email},null)
        </foreach>
    </insert>
SQL标签
  • sql标签截取重复片段,以供其他sql使用
  • include标签在需要的地方引入sql片段
    <sql id="empCloumns">eid, emp_name, age, sex, email</sql>

    <!--自定义映射查询数据-->
    <select id="getAllEmp" resultMap="mapEmp">
        select <include refid="empCloumns"></include>
        from t_emp
    </select>

三、Mybatis的缓存

1.一级缓存

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

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

    • 不同的SqlSession对应不同的一级缓存
    • 同一个SqlSession但是查询条件不同
    • 同一个SqlSession两次查询期间执行了任何一次增删改操作(任何增删改都会使一级缓存失效)
    • 同一个SqlSession两次查询期间手动清空了缓存
  • 虽然一级缓存默认开启,但是如果我们在springboot中需要使用一级缓存,需要添加注解@Transactional才能获取到同一个SqlSession,然后才能生效

        //查询员工以及员工对应的部门信息
        @Test
        @Transactional
        void getEmpWithDept() {
            Emp emp = empMapper.getEmpWithDept(1);
            System.out.println(emp);
            Emp emp2 = empMapper.getEmpWithDept(1);
            System.out.println(emp2);
        }
    

2.二级缓存

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

  • 二级缓存开启的条件:

    1. 在核心配置文件中,设置全局配置属性cacheEnabled=“true”,默认为true,不需要设置
    2. 在映射文件中设置标签<cache/>
    3. 二级缓存必须在SqlSession关闭或提交之后有效 (springboot中,mapper接口的代理对象执行完操作自动提交)
    4. 查询的数据所转换的实体类类型必须实现序列化的接口
  • 使二级缓存失效的情况:

    • 两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效
  • 二级缓存标签的一些属性配置

    • eviction属性,缓存回收策略

      • 缓存回收策略 LRU(Least Recently Used) – 最近最少使用的:移除最长时间不被使用的对象。
      • FIFO(First in First out) – 先进先出:按对象进入缓存的顺序来移除它们。
      • SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
      • WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
      • 默认的是 LRU。
    • flushInterval属性:刷新间隔,单位毫秒 默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新

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

    • readOnly属性:只读,true/false

      • true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了 很重要的性能优势。
      • false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是 false。
  • 二级缓存,我们就不用使用注解@Transactional,这样获取的SqlSession对象就不是同一个

        //查询员工以及员工对应的部门信息
        @Test
        void getEmpWithDept() {
            Emp emp = empMapper.getEmpWithDept(1);
            System.out.println(emp);
            Emp emp2 = empMapper.getEmpWithDept(1);
            System.out.println(emp2);
        }
    

注意:二级缓存在springboot中对应namespace命名空间的范围,一旦一个命名空间开启二级缓存,该命名空间下的所有查询操作都会走二级缓存,如果不需要走二级缓存,可以在sql上使用useCache=“false” 配置即可。

3.缓存查询顺序

  • 先查询二级缓存,因为二级缓存中可能会有其他程序(SqlSession)已经查出来的数据,可以拿来直接使用。
  • 如果二级缓存没有命中,再查询一级缓存
  • 如果一级缓存也没有命中,则查询数据库
  • SqlSession关闭之后,一级缓存中的数据会写入二级缓存(springboot中一级缓存自动提交)
  • springboot中,mapper执行完操作,自动提交SqlSession,就会写入一级缓存和二级缓存。
    //二级缓存
    @Test
    void getEmpWithDept() {
        //1.先查询二级缓存,二级缓存未命中,
        getEmpWithDept2();
        //4.二级缓存,命中
        Emp emp2 = empMapper.getEmpWithDept(1);
        System.out.println("2"+emp2);
    }

    //一级缓存
    @Test
    @Transactional
    void getEmpWithDept2() {
        //2.查询一级缓存,一级缓存未命中,查询数据库,然后写入一级缓存,自动提交SqlSession,然后写入二级缓存
        Emp emp0 = empMapper.getEmpWithDept(1);
        System.out.println("0"+emp0);
        //3.二级缓存命中
        Emp emp1 = empMapper.getEmpWithDept(1);
        System.out.println("1"+emp1);
    }

4.二级缓存整合第三方缓存EHCache

  1. 添加依赖

    slf4j就是一个日志门面,logback是它的一个具体实现

            <!-- 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>
    
  2. 添加ehcache配置文件

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

    spring:
    
      ......
      
      cache:
        ehcache:
          # 整合ehcache
          config: ehcache.xml
    
  4. 添加logback日志配置文件

    <?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="pers.jl.mapper" level="DEBUG"/>
    </configuration>
    

四、Mybatis的逆向工程

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

        <!-- 控制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.29</version>
                        </dependency>
                    </dependencies>
                </plugin>
            </plugins>
        </build>
    
  2. 添加配置文件generatorConfig.xml

    MyBatis3Simple和MyBatis3自行选择

    <?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="MyBatis3">
            <!-- 数据库的连接信息 -->
            <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                            connectionURL="jdbc:mysql://localhost:3306/mybatis"
                            userId="root"
                            password="1234">
            </jdbcConnection>
            <!-- javaBean的生成策略-->
            <javaModelGenerator targetPackage="pers.jl.pojo" targetProject=".\src\main\java">
                <!--能够使用子包-->
                <property name="enableSubPackages" value="true" />
                <!--去掉数据库字段名前后的空格-->
                <property name="trimStrings" value="true" />
            </javaModelGenerator>
            <!-- SQL映射文件的生成策略 -->
            <sqlMapGenerator targetPackage="pers.jl.mapper"  targetProject=".\src\main\resources">
                <property name="enableSubPackages" value="true" />
            </sqlMapGenerator>
            <!-- Mapper接口的生成策略 -->
            <javaClientGenerator type="XMLMAPPER" targetPackage="pers.jl.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"/>
            <table tableName="t_user" domainObjectName="User"/>
        </context>
    </generatorConfiguration>
    
  3. 插件执行mybatis-generator

    直接点击就行。

注意:

  • 生成的实体类或者接口带有Example都是条件的意思,例如deleteByExample就是根据条件删除。
  • 生成的实体类或者接口带有Selective都是选择性的意思,例如insertSelective就是该方法可能只插入指定的非空字段,而忽略其他为空的字段。(如果不懂看映射文件sql)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一码一上午

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值