Mybatis 框架使用指南(进阶)3

简介: Mybatis 框架使用指南(进阶)3

MyBatis 缓存


参考:https://blog.csdn.net/jinhaijing/article/details/84230810

缓存是服务器内存的一块区域。经常访问但又不会时时发生变化的数据适合使用缓存。


mybatis也支持缓存:提高查询速度,减轻数据库访问压力。




一级缓存(本地缓存)

MyBatis自带一级缓存且不可卸载


当执行查询以后,查询的结果会同时缓存到SqlSession为我们提供的一块区域中,该区域的结构是一个Map,当再次查询同样的数据时,mybatis会先去sqlsession中查询缓存,有的话直接拿出来用。当SqlSession对象消失时,mybatis的一级缓存也就消失了,同时一级缓存是SqlSession范围的缓存,当调用SqlSession的修改、添加、删除、commit(),close等方法时,就会清空一级缓存。


特点:随着sqlSession的创建而存在,随着SqlSession的销毁而销毁

简称:sqlSession级别的缓存



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


sqlSession不同(会话不同)


sqlSession相同,查询缓存中没有的数据


sqlSession相同,但两次查询之间执行了增删改操作

说明:为了防止增删改对当前数据的影响,即使查的同一个对象,也会重新查数据库

原因:每个增删改标签都有默认刷新缓存配置:flushCache=“true”(刷新一级和二级缓存)


sqlSession相同,但手动清楚了一级缓存(缓存清空)

手动清空一级缓存:openSession.clearCache();


没有使用到当前一级缓存的情况,就会再次向数据库发出查询

二级缓存(全局缓存)


基于 mapper.xml 中 namespace 命名空间级别的缓存,即同一个 namespace 对应一个二级缓存。两个mapper.xml的namespace如果相同,则这两个mapper执行sql查询到数据将存在相同的二级缓存区域中。


也称为SqlSessionFactory级别的缓存,由同一个SqlSessionFactory对象创建的多个SqlSession共享其缓存,但是其中缓存的是数据而不是对象,所以从二级缓存再次查询出得结果的对象与第一次存入的对象是不一样的。



注意:不是程序自带的,需要配置。仅做了解,一般不推荐使用(一般使用Redis缓存)。

配置流程:

  1. 在核心配置文件 SqlMapConfig.xml 中开启二级缓存(可以省略)
<settings>
    <!--开启对二级缓存的支持 默认是支持的可以不用配置-->
    <setting name="cacheEnabled" value="true"/>
</settings>

若是springboot集成,在yaml配置文件中配置开启二级缓存(可以省略)

mybatis:
  configuration:
    cache-enabled: true #打开全局缓存开关(二级环境),默认值就是true



2.在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="com.atguigu.mybatis.dao.EmployeeMapper">
    <!-- 声明使用二级缓存 -->
  <cache><cache/>
<!--  <cache eviction="FIFO" flushInterval="60000" readOnly="false" size="1024"></cache>  -->
  <select id="getEmpByLastNameLikeReturnMap" resultType="com.atguigu.mybatis.bean.Employee">
    select * from tbl_employee where last_name like #{lastName}
  </select>
</mapper>


<cache>中可以配置一些参数:


   eviction:缓存的回收策略:

       LRU – 最近最少使用的:移除最长时间不被使用的对象。默认

       FIFO – 先进先出:按对象进入缓存的顺序来移除它们。


       SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。


       WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。


   flushInterval:缓存刷新间隔。缓存多长时间清空一次,默认不清空,可以设置一个毫秒值

   readOnly:是否只读:


       true:只读;mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。


       mybatis为了加快获取速度,直接就会将数据在缓存中的引用交给用户。不安全,速度快

       false:非只读:mybatis觉得获取的数据可能会被修改。


       mybatis会利用序列化&反序列的技术克隆一份新的数据给你。安全,速度慢

   size:缓存存放多少元素

   type=“”:指定自定义缓存的全类名



3.实体类对象需要实现序列化接口

public class User implements Serializable


4.操作的过程中需要提交之后才会存入到二级缓存

查询数据提交到二级缓存中:sqlsession.commit | sqlsession.close

**注意:**如果 openSession.close(); 在第二次查询之后才关闭,则第二次查询会从一级缓存中查,如果不是一个session,则查询不到数据,然后就会发送sql去查数据库;

@Test
public void testSecondLevelCache() throws IOException{
  SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
  SqlSession openSession = sqlSessionFactory.openSession();
  SqlSession openSession2 = sqlSessionFactory.openSession();
  try{
    EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
    EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);
    Employee emp01 = mapper.getEmpById(1);
    System.out.println(emp01);
    Employee emp02 = mapper2.getEmpById(1);
    System.out.println(emp02);
    openSession.close();
    openSession2.close();
  }
}

工作机制:


1.一个会话,查询一条数据,这个数据会默认先被放在当前会话的一级缓存中;


2.当会话关闭或者提交后,一级缓存中的数据会被保存到二级缓存中;

然后当有新的会话查询数据,若是同一个 namespace 就可以获取二级缓存中的内容。

sqlSession ==> EmployeeMapper ==> Employee

   不同namespace查出的数据会放在自己对应的缓存中(map)

   效果:数据会从二级缓存中获取



缓存有关的设置/属性

  1. mybatis全局配置文件中配置全局缓存开启和清空
    1)控制二级缓存的开启和关闭
  <setting name="cacheEnabled" value="true"/>
  <!-- cacheEnabled=true:开启缓存;false:关闭缓存(二级缓存关闭)(一级缓存一直可用的) -->


2)控制一级缓存的开启和关闭

  <setting name="localCacheScope" value="SESSION"/>
  <!-- localCacheScope:本地缓存作用域(一级缓存SESSION);
       当前会话的所有数据保存在会话缓存中;STATEMENT:可以禁用一级缓存 -->


注意:一级缓存关闭后,二级缓存自然也无法使用;


mapper.xml 中也可以配置一级和二级缓存开启和使用


1)每个select标签都默认配置了useCache=“true”,表示会将本条语句的结果进行二级缓存。

如果useCache= false:则表示不使用缓存(一级缓存依然使用,二级缓存不使用)


2)每个 增删改 标签都默认配置了 flushCache=“true”,表示增删改执行完成后就会刷新一级二级缓存。

注意:查询标签 <select> 默认 flushCache=“false”:如果 flushCache=true; 则每次查询之后都会清空缓存;一级和二级缓存都无法使用。



多数据库支持


如果配置了 DatabaseIdProvider,可以在动态代码中使用名为 “_databaseId” 的变量来为不同的数据库构建特定的语句。

示例:

<insert id="insert">
    <selectKey keyProperty="id" resultType="int" order="BEFORE">
        <if test="_databaseId == 'oracle'">
            select seq_users.nextval from dual
        </if>
        <if test="_databaseId == 'db2'">
            select nextval for seq_users from sysibm.sysdummy1"
        </if>
    </selectKey>
    insert into users values (#{id}, #{name})
</insert>



DatabaseIdProvider 配置

  @Bean
    public DatabaseIdProvider databaseIdProvider() {
        DatabaseIdProvider databaseIdProvider = new VendorDatabaseIdProvider();
        Properties p = new Properties();
        p.setProperty("Oracle", "oracle");
        p.setProperty("TiDB", "tidb");
        p.setProperty("PostgreSQL", "postgresql");
        p.setProperty("DB2", "db2");
        p.setProperty("SQL Server", "sqlserver");
        databaseIdProvider.setProperties(p);
        return databaseIdProvider;
    }



动态 SQL 中的插入脚本语言


MyBatis 从 3.2 版本开始支持插入脚本语言,这允许插入一种语言驱动,并基于这种语言来编写动态 SQL 查询语句。


可以通过实现以下接口来插入一种语言:

public interface LanguageDriver {
  ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql);
  SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType);
  SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType);
}


实现自定义语言驱动后,就可以在 mybatis-config.xml 文件中将它设置为默认语言:

<typeAliases>
  <typeAlias type="org.sample.MyLanguageDriver" alias="myLanguage"/>
</typeAliases>
<settings>
  <setting name="defaultScriptingLanguage" value="myLanguage"/>
</settings>

或者,也可以使用 lang 属性为特定的语句指定语言:

<select id="selectBlog" lang="myLanguage">
  SELECT * FROM BLOG
</select>


或者,在 mapper 接口上添加 @Lang 注解:

public interface Mapper {
  @Lang(MyLanguageDriver.class)
  @Select("SELECT * FROM BLOG")
  List<Blog> selectBlog();
}



MyBatis 注解开发

单表注解

* @Insert:实现新增,代替了<insert></insert>
* @Update:实现更新,代替了<update></update>
* @Delete:实现删除,代替了<delete></delete>
* @Select:实现查询,代替了<select></select>
* @Result:实现结果集封装,代替了<result></result>
* @Results:可以与@Result 一起使用,封装多个结果集,代替了<resultMap></resultMap>


注解对用户表实现增删改查操作

import cn.test.doman.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
// 注解的单表crud操作
@Mapper
public interface UserMapper {
    //1 根据id做查询
    @Select("select * from user where id =#{id}")
    public User findById(int id);
    //2 全查
    @Select("SELECT id AS iid,username AS name,birthday AS bir,sex AS se,address AS addr FROM USER")
    @Results({
            /*column:字段
            * property:对象属性
            * id:默认是false 代表当前不是主键
            * */
            @Result(column = "iid",property = "id",id=true),
            @Result(column = "name",property = "username"),
            @Result(column = "bir",property = "birthday"),
            @Result(column = "se",property = "sex"),
            @Result(column = "addr",property = "address")
    })
    public List<User> findAll();
    //3 新增
    @Insert("insert into user(username,birthday,sex,address) "
            + "values(#{username},#{birthday},#{sex},#{address})")
    public void save(User user);
    //4 修改
    @Update("update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} "
            + "where id=#{id}")
    public void update(User user);
    //5 删除
    @Delete("delete from user where id=#{id}")
    public void delete(int id);
}


多表注解

注解开发的多表查询是嵌套查询方式

@Results  代替的是标签<resultMap>。该注解中可以使用单个@Result注解,也可以使用@Result集合。
      使用格式:@Results({@Result(), @Result()})或@Results(@Result())
@Result   代替<id>标签和<result>标签
      @Result中属性介绍:
        column:数据库的列名
        property:需要装配的属性名
        one:需要使用@One注解(@Result(one=@One ()))
        many:需要使用@Many注解(@Result(many=@Many ()))
@One(一对一) 代替了<assocation>标签,是多表查询的关键,在注解中用来指定子查询返回单一对象。
        @One注解属性介绍:
          select:指定用来多表查询的 sqlmapper
        使用格式:@Result(column=" ", property=" ", one=@One(select=""))
@Many(一对多)  代替了<collection>标签,是多表查询的关键,在注解中用来指定子查询返回对象集合。
        使用格式:@Result(column=" ", property=" ", many=@Many(select=""))


1)一对一

import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface OrdersMapper {
    // 查询订单
    @Select("select * from orders where id=#{id}")
    @Results({
            @Result(column = "id",property = "id",id = true),
            @Result(column = "ordertime",property = "ordertime"),
            @Result(column = "money",property = "money"),
            /* 组装用户
            *    一对一 one=@one
            *    select:代表要封装的数据方法来源
            *    column:方法需要的参数
            *    property:要封装的对象属性名
            *    javaType:对象属性名的字节码(.class)类型
            * */
            @Result(one=@One(select = "cn.test.mapper.UserMapper.findById")
                    ,property = "user",javaType = User.class, column = "uid")
    })
    public Orders findOrders(int id);
}


@Mapper
public interface UserMapper {
    //1 根据id做查询
    @Select("select * from user where id =#{id}")
    public User findById(int id);
}


2)一对多

@Mapper
public interface UserMapper {
    //1 根据id做查询
    @Select("select * from user where id =#{id}")
    @Results({
            @Result(column = "id",property = "id",id=true),
            @Result(column = "username",property = "username"),
            @Result(column = "birthday",property = "birthday"),
            @Result(column = "sex",property = "sex"),
            @Result(column = "address",property = "address"),
            /*组装订单orders
            *     一对多
            *     many=@many
             *     select:代表要封装的数据方法来源
             *     column:方法需要的参数
             *     property:要封装的对象属性名
             *     javaType:对象属性名的字节码(.class)类型
            * */
            @Result(many = @Many(select = "cn.test.mapper.OrdersMapper.findOrdersList"),
                    property = "ordersList",javaType =List.class,column = "id")
    })
    public User findById(int id);
}    


@Mapper
public interface OrdersMapper {
  /*根据用户id获取订单集合*/
    @Select("select * from orders where uid=#{用户的id}")
    public List<Orders> findOrdersList(int id);
}


3)多对多

@Mapper
public interface UserMapper {
    //1 根据id做查询
    @Select("select * from user where id =#{id}")
    @Results({
            @Result(column = "id",property = "id",id=true),
            @Result(column = "username",property = "username"),
            @Result(column = "birthday",property = "birthday"),
            @Result(column = "sex",property = "sex"),
            @Result(column = "address",property = "address"),
            // 组装订单orders
            @Result(many = @Many(select = "cn.test.mapper.OrdersMapper.findOrdersList"
                                 , fetchType = FetchType.LAZY)
                    , property = "ordersList",javaType =List.class,column = "id"),
      // 组装角色
            @Result(many = @Many(select = "cn.test.mapper.RoleMapper.findRoles")
                    ,property = "roleList",javaType = List.class,column = "id")
    })
    public User findById(int id);
}       


@Mapper
public interface RoleMapper {
    /*查询指定用户的角色*/
    @Select("SELECT * FROM user_role ur INNER JOIN role r ON ur.rid=r.id WHERE ur.uid=#{id}")
    @Results({
            @Result(column = "id",property = "id",id=true),
            @Result(column = "role_name",property = "roleName"),
            @Result(column = "role_desc",property = "roleDesc")
    })
    public List<Role> findRoles(int id);
}
相关文章
|
2天前
|
SQL Java 数据库连接
持久层框架MyBatisPlus
持久层框架MyBatisPlus
持久层框架MyBatisPlus
|
16天前
|
缓存 Cloud Native 安全
探索阿里巴巴新型ORM框架:超越MybatisPlus?
【10月更文挑战第9天】在Java开发领域,Mybatis及其增强工具MybatisPlus长期占据着ORM(对象关系映射)技术的主导地位。然而,随着技术的发展,阿里巴巴集团推出了一种新型ORM框架,旨在提供更高效、更简洁的开发体验。本文将对这一新型ORM框架进行探索,分析其特性,并与MybatisPlus进行比较。
22 0
|
3月前
|
Java 数据库连接 Spring
后端框架入门超详细 三部曲 Spring 、SpringMVC、Mybatis、SSM框架整合案例 【爆肝整理五万字】
文章是关于Spring、SpringMVC、Mybatis三个后端框架的超详细入门教程,包括基础知识讲解、代码案例及SSM框架整合的实战应用,旨在帮助读者全面理解并掌握这些框架的使用。
后端框架入门超详细 三部曲 Spring 、SpringMVC、Mybatis、SSM框架整合案例 【爆肝整理五万字】
|
3月前
|
Java 数据库连接 mybatis
mybatis框架图
文章介绍了MyBatis框架的起源、发展和其作为持久层框架的功能,提供了MyBatis的框架图以帮助理解其结构和组件。
mybatis框架图
|
3月前
|
Java 数据库连接 mybatis
后端框架的学习----mybatis框架(9、多对一处理和一对多处理)
这篇文章介绍了在MyBatis框架中如何处理多对一和一对多的关联查询,通过定义`<resultMap>`和使用`<association>`与`<collection>`元素来实现对象间的关联映射。
|
3月前
|
Java 数据库连接 测试技术
后端框架的学习----mybatis框架(8、lombok)
这篇文章介绍了如何在MyBatis框架中使用lombok库来简化Java实体类的编写,包括在IDEA中安装Lombok插件、在项目中导入lombok依赖以及在实体类上使用Lombok提供的注解。
|
3月前
|
SQL Java 数据库连接
【Java 第十三篇章】MyBatis 框架介绍
MyBatis 原名 iBATIS,2001 年由 Clinton Begin 创建,以其简易灵活著称。2010 年更名以重塑品牌形象。MyBatis 通过 SQL 映射文件将 SQL 语句与 Java 代码分离,支持编写原生 SQL 并与方法映射。具备对象关系映射功能,简化数据库记录处理。支持动态 SQL 构建,灵活应对不同查询条件。内置缓存机制,提升查询效率。相比全功能 ORM,MyBatis 提供更高 SQL 控制度和更好的维护性,并易于与 Spring 等框架集成,广泛应用于 Java 数据访问层。
34 0
|
3月前
|
druid Java 数据库连接
SpringBoot项目整合MybatisPlus持久层框架+Druid数据库连接池,以及实现增删改查功能
SpringBoot项目整合MybatisPlus和Druid数据库连接池,实现基本的增删改查功能。
310 0
|
3月前
|
SQL Java 数据库连接
后端框架的学习----mybatis框架(5、分页)
这篇文章介绍了如何在MyBatis框架中实现分页功能,包括使用SQL的`limit`语句进行分页和利用MyBatis的`RowBounds`对象进行分页的方法。
|
3月前
|
SQL Java 数据库连接
后端框架的学习----mybatis框架(7、使用注解开发)
这篇文章讲述了如何使用MyBatis框架的注解方式进行开发,包括在接口上使用注解定义SQL语句,并通过动态代理实现对数据库的增删改查操作,同时强调了接口需要在核心配置文件中注册绑定。