MyBatis学习笔记(全)


本文出现的案例:

MyBatis-01入门案例

MyBatis–03自定义MyBatis的实现

MyBatis–04CRUD操作

MyBatis–05通过实现类完成dao层的开发

MyBatis–06多表查询操作

MyBatis–07JNDI扩展知识

MyBatis–08如何使用延迟加载

MyBatis–09验证MyBatis一级缓存

MyBatis–10使用MyBatis的二级缓存

MyBatis–11注解开发

MyBatis概述

框架是什么?

  • 一套解决问题的方案,一个可以根据开发者自定义以实现特定功能的软件。

框架能做什么?

  • 实现技术整合,提供一些功能帮助程序员快速开发。

  • 框架一般处于低层应用平台和高层业务逻辑之间的中间层,它能够帮助两层平台快速整合技术。

MyBatis框架

  • 是一个基于java的持久层框架,内部封装了jdbc,使开发者只用关注sql语句,不必花费时间去加载驱动,创建连接,获取预处理对象,执行sql语句和释放资

    源等繁琐操作。

  • MyBatis 通过 xml 或注解的方式将要执行的各种 statement 配置起来,并通过 java 对象和 statement 中sql 的动态参数进行映射,生成最终执行的 sql 句,

    最后由 mybatis 框架执行 sql 并将结果映射为 java 对象并返回。

  • 采用 ORM 思想解决了实体和数据库映射的问题,对 jdbc 进行了封装,屏蔽了 jdbc api 底层访问细节,使我们不用与 jdbc api 打交道,就可以完成对数据库

    的持久化操作。

    ​ ORM:Object Relational Mapping 对象关系映射,可以让数据库里的实体和java里的实体类的属性对应起来。

软件开发分层的重要性

  • 实现低内聚,高耦合。

  • 通过分层,能够更好的实现各部分的职责, 使得每部分只用专注解决各层的问题。

Jdbc操作数据库的问题分析

java连接mysql数据库代码

public class UserDaoImpl implements IUserDao {
    public List<User> findAll() throws Exception {
        //加载驱动类
        Class.forName("com.mysql.jdbc.Driver");
        //获取连接
        Connection conn = DriverManager.getConnection("jdbc:mysql:///test","root","123456");
        //获取预处理对象
        PreparedStatement ps = conn.prepareCall("select * from user");
        //执行sql
        ResultSet rs = ps.executeQuery();
        //封装user
        List<User> users = new ArrayList<User>();
        while(rs.next()){
            User user = new User();
            user.setId(rs.getInt("id"));
            user.setUsername(rs.getString("username"));
            user.setPassword(rs.getString("password"));
            users.add(user);
        }
        //释放资源
        rs.close();
        ps.close();
        conn.close();
        return users;
    }
}

jdbc代码存在的问题分析

  1. 数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。

  2. Sql 语句在代码中硬编码,造成代码不易维护,实际应用 sql 变化的可能较大,sql 变动需要改变 java代码。

  3. 使用 preparedStatement 向占有位符号传参数存在硬编码,因为 sql 语句的 where 条件不一定,可能多也可能少,修改 sql 还要修改代码,系统不易维护。

  4. 对结果集解析存在硬编码(查询列名),sql 变化导致解析代码变化,系统不易维护,如果能将数据库记录封装成,pojo 对象解析比较方便。

案例一:MyBatis入门案例

参看案例:

MyBatis-01入门案例

MyBatis入门案例中的设计模式分析

在这里插入图片描述

图中是mybatis的入门案例实现代码,共有三种设计模式:

  • 构建者模式:隐藏代码细节,让开发者调用方法就能得到对象。
  • 工厂模式:解耦,降低类之间的依赖。
  • 代理模式:扩展程序的功能,对代码进行增强。

案例二:MyBatis注解开发入门

以下步骤在MyBatis-01入门案例的基础上修改。

(1)在IUserDao接口的findAll()方法上方添加:

 @Select("select * from user")

(2)删除resources目录下的com目录。

(3)在SqlMapConfig.xml文件中修改mappers中的内容为:

<mapper class="com.cncs.dao.IUserDao"/>

自定义MyBatis框架

mybatis在使用代理dao进行增删改查时做了哪些事?

  1. 创建代理对象
  2. 在代理对象方法中调用selectList()

在基于mybatis框架开发的应用在执行过程中核心信息包括:连接信息,映射信息。连接信息通过SqlMapConfig.xml获取,同时它定义了Mapper的位置;映射信息通过每张Mapper.xml获取,它通过"全限定类名"+"."+"方法名"定位每个dao接口中的方法,通过返回类型定义查询结果的封装实体类sql语句作为执行目标。

(1)在SqlMapConfig.xml中读取数据库连接信息,创建Connection对象

<configuration>
    <environments default="mysql">
        <environment id="mysql">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis_learn"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <!--        指定每个映射配置文件的位置-->
    <mappers>
        <mapper resource="com/cncs/dao/IUserMapper.xml"/>
    </mappers>
</configuration>

(2)读取IUserMapper.xml映射配置信息,创建PreparedStatement对象,sql语句,返回类型

<mapper namespace="com.cncs.dao.IUserDao">
    <select id="findAll" resultType="com.cncs.domain.User">
        select * from user;
    </select>
</mapper>

(3)执行查询

ResultSet resultSet = preparedStatement.executeQuery(sql);

(4)封装查询结果,当类属性名和数据库表的列名名称相同时,可通过反射生成对象,然后封装数据到对象中。

List<E> list = new ArraryList<E>();
while(resultSet.next()){
	//E e = new E();
    //通过类加载器生成一个实例
    E e = (E)Class.forName("com.cncs.domain.E").newInstance();
    e.setString(resultSet.getString("xx"));
    ...
    list.add(e);
}

以上步骤可参看下图:

在这里插入图片描述

PS:解析xml类型的配置文件,使用dom4j技术。

创建代理对象的分析

IUserDao userDao = sqlSession.getMapper(IUserDao.class);

通过被代理接口的字节码创建代理对象

public <T> T getMapper(<T> daoInterfaceClass){
    /**
    类加载器:被代理对象相同的类加载器
    代理对象要实现的接口:和被代理对象实现相同的接口
    代理内容:增强的方法
    		此处是一个InvocationHandler接口的实现类,需要自己写,在实现类中实现查询所有的功能,即前面分析过的4步。
    */
    Proxy.newProxyInstance(类加载器,代理对象要实现的字节码数组,代理的内容);
}
    

案例三:自定义MyBatis的实现

参看案例三:MyBatis–03自定义MyBatis的实现

案例四:MyBatis完成CRUD操作

参看案例四:MyBatis–04CRUD操作

模糊查询且使用标签内部进行处理的方式时,即"%${value}%",需要遵循OGNL规s范。

OGNL表达式

Object Graphic Navigation Language
对象 图 导航 语言

它是通过对象的取值方法来获取数据。在写法上把get给省略了。

比如:我们获取用户的名称
类中的写法:user.getUsername();
OGNL表达式写法:user.username

mybatis中为什么能直接写username,而不用user.呢?

在parameterType中已经提供了属性所属的类,所以此时不需要写对象名

映射配置文件中的参数使用注意

ParameterType传递参数

该标签可以传递基本类型,写法可以写int,或者写全限定类名java.lang.Integer,简写的原因是mybatis已经把常用简单类型定义了别名。

搜索TypeAliasRegistry,可以看到常用简单类型已经被取了别名,如下。

/**
 * @author Clinton Begin
 */
public class TypeAliasRegistry {

  private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>();

  public TypeAliasRegistry() {
    registerAlias("string", String.class);

    registerAlias("byte", Byte.class);
    registerAlias("long", Long.class);
    registerAlias("short", Short.class);
    registerAlias("int", Integer.class);
    registerAlias("integer", Integer.class);
    registerAlias("double", Double.class);
    registerAlias("float", Float.class);
    registerAlias("boolean", Boolean.class);
    ......

可以传递pojo类型参数,此时只能写全限定类名。

传递pojo包装对象

新建QueryVo类作为查询条件

package com.cncs.domain;

/**
 * 查询条件
 */
public class QueryVo {
    private User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
}

在持久层接口中添加方法

/**
 * 根据queryVo条件查询
 * @param queryVo
 * @return
 */
List<User> findByVo(QueryVo queryVo);

在映射配置文件中添加配置

<!--    根据QueryVo查询-->
<select id="findByVo" parameterType="com.cncs.domain.QueryVo" resultType="com.cncs.domain.User">
    <!--         select * from user where username like #{name};-->
    select * from user where username like #{user.username}
</select>

在测试类中添加测试方法

@Test
public void findByVo() {
    QueryVo queryVo = new QueryVo();
    User user1 = new User();
    user1.setUsername("%王%");
    queryVo.setUser(user1);
    List<User> list = userDao.findByVo(queryVo);
    for (User user : list) {
        System.out.println(user);
    }
}

MyBatis的输出结果封装

resultType配置结果类型

使用resultType标签时,实体类属性名和数据库表列名需要保持一致,否则数据无法完整封装。

基本类型,可以使用简写,也可以使用全限定类名

<delete id="deleteUser" parameterType="int">
    delete from user where id=#{userId};
</delete>

pojo类型

<select id="findAll" resultType="com.cncs.domain.User">
    select * from user;
</select>

实体类属性名和数据库表列名不一致,如何实现数据映射?

java中实体类名称

public class User implements Serializable {
    private Integer userId;
    private String userName;
    private String userAddress;
    private String userSex;
    private Date userBirthday;

数据库中列名

id
username
birthday
sex
address

方法一:为列名取别名

优点:效率高,速度快。

缺点:书写繁琐,如果有多个查询的结果类型需要封装,会书写重复代码。

<select id="findAll" resultType="com.cncs.domain.User">
    select id as userId,username as userName,birthday as userBirthday,address as userAddress,sex as userSex from user
</select>

方法二:使用resultMap标签

优点:关系清晰,一次配置,多处使用。

缺点:效率低。

<!--配置结果集java和数据库之间的映射关系-->
<resultMap id="userMap" type="com.cncs.domain.User">
    <result property="userId" column="id"></result>
    <result property="userName" column="username"></result>
    <result property="userAddress" column="address"></result>
    <result property="userSex" column="sex"></result>
    <result property="userBirthday" column="birthday"></result>
</resultMap>

引用resultMap

<select id="findAll" resultMap="userMap">
    select * from user
</select>

PS1

  • column必须和查询出结果的列名保持一致,如果起了别名,就使用别名
  • property必须和实体类的属性名保持一致。
  • 如果实体类中某个属性是实体类,需要使用标签来进一步封装数据。

在这里插入图片描述

PS2

  • 查询的结果封装到实体类时,该实体类有集合类型的属性时,需要用到标签。

在这里插入图片描述

PS3

  • PS是什么意思?postscript的简写,即留言、备注。

案例五:MyBatis通过实现类完成dao层的开发

参看案例五:MyBatis–05通过实现类完成dao层的开发

SqlMapConfig.xml配置文件常用标签

properties标签

配置properties,可以直接在标签内部配置连接数据库的信息,也可以通过属性引用外部配置文件的信息。

  • resource属性:用于指定配置文件的位置,是按照类路径的写法来写,配置文件必须存在于类路径下。

  • url属性:是按照url的写法来写。

    • URL:全称为uniform resource location,统一资源定位符,它是唯一标识一个资源的位置,是通过全网络来标识一个资源。

      写法举例:

      http://localhost:8080/mybatisserver/mybatisdemo
      协议    主机       端口 URI
      常见协议有,http,ftp,file(本地文件传输协议,用于访问本地计算机中的文件)
      file协议举例:file:///F:/learn_prj/MyBatis_prj/mybatis_04learn_CRUD/src/main/resources/jdbcConfig.properties
      URI:全称为uniform resource identifier,统一资源标识符,是在一个应用中标识一个资源。
      

使用方式一

在configuration标签下直接添加标签

<properties>
        <property name="driver" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis_learn"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </properties>

配置数据源时,直接调用(这种方式没有任何卵用,有用的方式在下面)

<!--配置数据源-->
<dataSource type="POOLED">
    <property name="driver" value="${driver}"/>
    <property name="url" value="${url}"/>
    <property name="username" value="${username}"/>
    <property name="password" value="${password}"/>
</dataSource>

使用方式二

向resources目录下添加文件jdbcConfig.properties

jdbc.driver=com.mysql.jdbc.Driver
#jdbc.url=jdbc:mysql://localhost:3306/mybatis_learn
#jdbc连接数据url简写方式:     jdbc:mysql:///数据库名
jdbc.url=jdbc:mysql:///mybatis_learn
jdbc.username=root
jdbc.password=123456

在configuration标签下添加标签

<!--配置properties-->
<properties resource="jdbcConfig.properties"></properties>

配置数据源时,value要和properties文件中key对应上

<!--配置数据源-->
<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>

使用方式三

除了使用url属性外,其他配置和方式二一致。

<!--配置properties-->
<properties url="file:///F:/learn_prj/MyBatis_prj/mybatis_04learn_CRUD/src/main/resources/jdbcConfig.properties"></properties>

typeAliases标签

配置别名的两种方式,在标签下添加标签

方式一

在标签中添加标签。

通过type属性指定实体类的全限定类名,alias指定的是别名,别名一旦指定后,使用时不区分大小写。

<typeAlias type="com.cncs.domain.User" alias="user"/>

方式二

在标签中添加标签。

通过name属性指定要配置别名的包后,该包下的所有实体类都将注册别名,并且别名就是类名,使用时不区分大小写。

<package name="com.cncs.domain"/>

package标签

package标签用于指定dao接口所在的包,当指定完后,mybatis就可以找到和接口对应的映射配置文件,也就替代了mapper标签。

<mappers>
        <package name="com.cncs.dao"></package>
    </mappers>

MyBatis的连接池及事务

连接池介绍

将多个连接放入一个容器,该容器本质是一个集合,该集合需要实现“线程安全”,不能两个线程拿到同一个连接,且该集合需要符合队列“先进先出”的要求。

连接池的状态分为三种,如图所示。

  1. 没有线程获取连接,连接池所有连接被编号和初始化。
  2. 线程1获取连接1,线程2获取连接2,连接池剩下的连接自动重新编号。
  3. 线程1或者线程2释放连接,连接池重新拿回连接并重新初始化和重新编号。

在这里插入图片描述

MyBatis中的连接池分类

  • POOLED,使用连接池实现的数据源,采用传统的javax.sql.DataSource规范中的连接池,mybatis中有实现类PooledDataSource。

  • UNPOOLED,不使用连接池实现的数据源,采用传统的获取连接的方法,同样实现了DataSource规范,它的实现类UnpooledDataSource。

  • JNDI,使用JNDI技术实现的数据源。

使用UNPOOLED配置连接池的分析

在案例四的基础上修改SqlMapConfig.xml,将标签的属性修改为UNPOOLED,然后运行测试方法findOne(),运行结果如果所示。

<dataSource type="UNPOOLED">

在这里插入图片描述

上图表明了UNPOOLED并未使用连接池技术实现的数据源。

分析UnpooledDataSource类,找到下面的方法

private Connection doGetConnection(Properties properties) throws SQLException {
  initializeDriver();
  Connection connection = DriverManager.getConnection(url, properties);
  configureConnection(connection);
  return connection;
}

分析获取一个连接的执行过程为:初始化驱动 -> 获取连接 -> 配置连接 -> 返回一个连接。

使用POOLED配置连接池的分析

在案例四的基础直接运行测试方法findOne(),运行结果如果所示。

在这里插入图片描述

上图表明了POOLED使用了连接池技术实现的数据源。

分析PooledDataSource类,其中popConnection()方法的部分代码如下

private PooledConnection popConnection(String username, String password) throws SQLException {
  boolean countedWait = false;
  PooledConnection conn = null;
  long t = System.currentTimeMillis();
  int localBadConnectionCount = 0;

  while (conn == null) {
    synchronized (state) {
      if (!state.idleConnections.isEmpty()) {
        // Pool has available connection
        conn = state.idleConnections.remove(0);
        if (log.isDebugEnabled()) {
          log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");
        }
      } else {
        // Pool does not have available connection
        if (state.activeConnections.size() < poolMaximumActiveConnections) {
          // Can create new connection
          conn = new PooledConnection(dataSource.getConnection(), this);
          if (log.isDebugEnabled()) {
            log.debug("Created connection " + conn.getRealHashCode() + ".");
          }
        } else {
          // Cannot create new connection
          PooledConnection oldestActiveConnection = state.activeConnections.get(0);
          long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
          if (longestCheckoutTime > poolMaximumCheckoutTime) {
            // Can claim overdue connection
            state.claimedOverdueConnectionCount++;
            state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;
            state.accumulatedCheckoutTime += longestCheckoutTime;
            state.activeConnections.remove(oldestActiveConnection);
            if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {
              try {
                oldestActiveConnection.getRealConnection().rollback();
              } catch (SQLException e) {

分析代码,代码大致功能,判断连接池中是否存在空闲连接

如果有取出 如果没有,判断活动连接池的连接数量是否小于连接池设置的最大连接数量

​ 如果小于,则创建新的连接 如果不小于,则从活动连接池中找到最先进入活动连接池的连接,取出重新初始化。

在这里插入图片描述

mybatis中的事务

什么是事务

事务就是一个并发控制的单位,是用户定义的一组操作序列。

数据库事务的四大特性(ACID)

原子性(Atomicity),原子性是指事务包含的所有操作要么全部成功然后提交,要么全部失败回滚。

一致性(Consistency),一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态,原子性、隔离性、持久性可以保证一致性。

隔离性(Isolation),隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。

持久性(Durability),持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。

不考虑隔离性会产生的三个问题

脏读、幻读、不可重复读

配置mybatis自动提交事务

打开SqlSessionFactory的实现类DefaultSqlSessionFactory,可以找到下面的方法,调用该方法时通过设置它的参数开启自动提交事务。

@Override
public SqlSession openSession(boolean autoCommit) {
  return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit);
}

动态SQL

这节代码:
https://github.com/canNotCtrlSelf/learn_mybatis/tree/master/mybatis_day03/day03_02dynamicSQL

当查询条件是一个实体类,通过设置不同的属性可以改变查询的条件,在标签内部通过使用一些标签来改变查询的条件,实现动态查询。

if标签

持久层添加方法

/**
 * 根据用户信息,查找用户列表
 * @param user
 * @return
 */
List<User> findByUser(User user);

单元测试添加方法

@Test
public void testFindByUser(){
    User user = new User();
    user.setUsername("老六");
    user.setSex("女");
    List<User> users = userDao.findByUser(user);
    for (User user1 : users) {
        System.out.println(user1);
    }
}

映射配置文件添加

<!--根据用户信息查找用户列表-->
<select id="findByUser" resultType="user" parameterType="user">
    select * from user where 1=1
    <if test="username != null and username !=''">
        and username = #{username}
    </if>
    <if test="sex != null">
        and sex = #{sex}
    </if>
</select>

where标签

在上面的基础上,替换where 1=1

<!--根据用户信息查找用户列表-->
<select id="findByUser" resultType="user" parameterType="user">
    select * from user
    <where>
        <if test="username != null and username !=''">
            and username = #{username}
        </if>
        <if test="sex != null">
            and sex = #{sex}
        </if>
    </where>
</select>

foreach标签

添加实体类QueryVo

/**
 * 查询条件
 */
public class QueryVo {
    private User user;
    private List<Integer> ids;

    public List<Integer> getIds() {
        return ids;
    }

    public void setIds(List<Integer> ids) {
        this.ids = ids;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
}

持久层添加方法

/**
 * 根据queryVo条件查询
 * @param queryVo
 * @return
 */
List<User> findByIds(QueryVo queryVo);

映射配置文件添加

<!--根据ids查询-->
<select id="findByIds" parameterType="queryVo" resultType="user">
    select * from user
    <where>
        <if test="ids != null and ids.size() > 0">
            <foreach collection="ids" open="id in (" close=")" item="id" separator=",">
               #{id}
            </foreach>
        </if>
    </where>
</select>

单元测试添加方法

/**
 * 根据id集合查询
 */
@Test
public void testFindByIds(){
    QueryVo queryVo = new QueryVo();
    ArrayList<Integer> list = new ArrayList<Integer>();
    list.add(53);
    list.add(54);
    list.add(56);
    queryVo.setIds(list);
    List<User> voList = userDao.findByIds(queryVo);
    for (User user : voList) {
        System.out.println(user);
    }
}

简化重复编写sql语句

在映射配置文件中添加代码片段

<!--定义重复使用的sql语句-->
<sql id="defaultSql">
    select * from user
</sql>

引用代码片段

<!--    查询所有-->
<select id="findAll" resultType="uSEr">
    <include refid="defaultSql"></include>
</select>

案例六:MyBatis的多表操作

参看案例:MyBatis–06多表查询操作

案例七:JNDI扩展知识

参看案例:MyBatis–07JNDI扩展知识

MyBatis中的延迟加载

什么是延迟加载?

延迟加载,就是需要使用关联数据的时候才加载关联数据,不使用就不加载,延迟加载也叫懒加载

什么时候需要使用延迟加载?

在多表查询中,有一对一,一对多,多对一,多对多四种关联查询方式,通常情况,一对多和多对多使用延迟加载。在实际开发中,有时候不需要某个数据时,马上得到它的关联数据。例如,在查询用户时,可以不着急查询用户的所有账户信息;反过来,查询账户信息时,往往同时需要查询出账户所属的用户信息。前一种情况需要延迟加载以节约性能,后一种情况则需要立即加载,以方便后面的操作。

延迟加载的坏处

因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。

案例八:MyBatis–08如何使用延迟加载

MyBatis的缓存

什么是缓存?

存在于内存中的临时数据。

为什么使用缓存?

减少和数据库交互的次数,提高操作效率。

什么样的的数据适合使用缓存?什么数据不适合?

适合:

  • 经常查询,且不经常改变的数据。
  • 数据的正确与否对最终结果影响不大。

不适合:

  • 经常改变的数据
  • 数据的正确性对最终结果影响很大,例如:银行汇率,商品库存。

MyBatis一级缓存

指的是SqlSession对象的缓存。MyBatis一级缓存时默认开启的。

当执行查询的时候,查询结果会存入SqlSession提供的一块内存区域。该区域的结构是一个Map,当再次查询时,mybatis会先从SqlSession中拿数据,如果存在直接拿出来用。

当SqlSession对象消失了,缓存就消失了。

案例九:MyBatis–09验证MyBatis一级缓存

MyBatis二级缓存

二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。

二级缓存数据存放的方式

在这里插入图片描述

二级缓存结构图

在这里插入图片描述

开启mybatis 的二级缓存后,sqlSession1 去查询用户信息,查询到用户信息会将查询数据存储到二级缓存中。

如果 SqlSession3 去执行相同 mapper 映射下 sql,执行 commit 提交,将会清空该 mapper 映射下的二级缓存区域的数据。

sqlSession2 去查询与 sqlSession1 相同的用户信息,首先会去缓存中找是否存在数据,如果存在直接从缓存中取出数据。

案例十:MyBatis–10使用MyBatis的二级缓存

MyBatis注解开发

参考案例十一:MyBatis–11注解开发


本文参考博客:

数据库事务的四大特性以及事务的隔离级别

本文视频学习网址:

Mybatis教程IDEA版-4天-2018黑马SSM-01

  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值