【Java框架】Mybatis教程(二)——SQL映射及缓存

ops/2024/9/25 8:25:44/
webkit-tap-highlight-color: rgba(0, 0, 0, 0);">

目录

  • SQL传参
    • 1.单个简单参数
      • 使用1
      • 使用2
    • 2.多个简单参数
      • 2.1使用索引【不推荐】
      • 2.2使用@Param
    • 3.复杂参数
      • 3.1对象
      • 3.2集合(Map)
  • Mybatis中的批量操作
    • 1.批量查询
      • 1.1数组入参
      • 1.2List入参
      • 1.3Map中有list入参
  • SQL映射的XML文件
    • resultMap
      • resultMap属性
      • 方式1:在查询时,给字段取别名,与实体类属性名保持一致
      • 方式2:resultMap手动映射不一致的两个名字
      • 补充
    • resultMap子元素
      • 面试题:MyBatis实现一对一和一对多有几种方式?具体怎么操作的?
      • association
        • 代码示例:一对一联合查询
      • collection
        • 代码示例:一对多联合查询
      • resultMap自动映射(autoMappingBehavior)的三个匹配级别
    • 面试题:resultType与resultMap的区别
    • select属性小结
  • Mybatis缓存
    • 一级缓存:默认开启
    • 二级缓存
      • 开启二级缓存
      • 代码示例
    • 面试题:说一下 MyBatis 的一级缓存和二级缓存
    • 面试题:简述Mybatis都有哪些执行器?他们之间的区别是什么?
      • simpleExecutor执行器
      • reuseExecutor执行器
      • batchExecutor执行器

SQL传参

1.单个简单参数

  • 单个参数的传参比较简单,可以是任意形式的,比如#{a} 、#{b} 或者#{param1} ,但是为了开发规范,尽量使用和入参时一样。
  • 传入单个简单参数时,xml中parameterType可写可不写,但是写的话一定保证值与参数类型/别名一致

mapper接口

java">User getUserById(int id);

使用1

xml

    <select id="getUserById" parameterType="int" resultType="User">SELECT * FROM SMBMS_USER WHERE ID=#{id}</select>

这种情况#{}中可以是任意名称,但是不规范,规范写法还是和mapper接口中的形参名保持一致

使用2

xml

    <select id="getUserById" parameterType="int" resultType="User">SELECT * FROM SMBMS_USER WHERE ID=#{uid}</select>

这种情况#{}中只能是uid了,@Param注解相当于是绑定了uid这个名称入参,在使用时就只能使用uid了

2.多个简单参数

mapper接口

java">List<User> getUserList(String userName,int userRole);

2.1使用索引【不推荐】

  • 多个参数可以使用类似于索引的方式传值,比如: #{param1} 对应第一个参数, #{param2} 对应第二个参数…
  • 此时xml中的parametType不写
    xml
java">	<select id="getUserList" resultType="User">SELECT * FROM SMBMS_USER WHERE USERNAME=#{param1} AND USERROLE=#{param2}</select>

2.2使用@Param

  • @Param 这个注解用于指定key,一旦指定了key,在SQL中即可对应的key入参。
  • 此时xml中的parametType不写
    mapper
java">List<User> getUserList(@Param("userName")String userName,@Param("userRole")int userRole);

xml

    <select id="getUserList" resultType="User">SELECT * FROM SMBMS_USER WHERE USERNAME=#{userName} AND USERROLE=#{userRole}</select>

3.复杂参数

3.1对象

mapper

java">   int insertUser(User user);

xml

	<insert id="insertUser" parameterType="com.zjl.pojo.User">INSERT INTO SMBMS_USER(id,userCode,userName,birthday)VALUES(default,#{userCode},#{userName},#{birthday})</insert>

3.2集合(Map)

Mybatis底层就是将入参转换成Map ,入参传Map当然也行,此时#{key} 中的key 就 对 应 Map 中 的 key 。
mapper

java">   List<User> getUserListByMap(Map<String,Object> paramMap);

xml

java">    <select id="getUserListByMap" parameterType="map" resultType="User">SELECT * FROM SMBMS_USER WHERE USERNAME=#{queryName} AND USERROLE=#{queryRole}</select>

测试

java">    @Testpublic void getUserListByMap(){SqlSession session = factory.openSession();UserMapper userMapper = session.getMapper(UserMapper.class);Map<String,Object> paramMap = new HashMap<String, Object>();paramMap.put("queryName","系统管理员");paramMap.put("queryRole",1);List<User> userList = userMapper.getUserListByMap(paramMap);System.out.println(userList);session.close();}

List集合和数据这种情况是需要结合批量操作一起使用的

Mybatis中的批量操作

1.批量查询

批量查询相当于SQL中的select xx from table where colum in (v1,v2,v3)

1.1数组入参

要求:foreach 标签中的collection属性值等于array
mapper

java">    List<User> getUserListByids(int[] ids);

xml

    <select id="getUserListByids" resultType="User" parameterType="int">SELECT * FROM SMBMS_USER WHERE ID IN<foreach collection="array" item="id" open="(" separator="," close=")">#{id}</foreach></select>

1.2List入参

要求:foreach 标签中的collection属性值等于list

java">    List<User> getUserListByids(List<Integer> idList);

xml

    <select id="getUserListByids" resultType="User" parameterType="int">SELECT * FROM SMBMS_USER WHERE ID IN<foreach collection="list" item="id" open="(" separator="," close=")">#{id}</foreach></select>

1.3Map中有list入参

要求:foreach 标签中的collection属性值等于Map中List集合的key
mapper

java">    List<User> getUserListByMap(Map<String,Object> paramMap);

xml

    <select id="getUserListByMap" parameterType="map" resultType="User">SELECT * FROM SMBMS_USER WHERE GENDER = #{gender}AND USERROLE IN<foreach collection="idList" item="userRole" open="(" separator="," close=")">#{userRole}</foreach></select>

测试

java">    @Testpublic void getUserListByMap(){SqlSession session = factory.openSession();UserMapper userMapper = session.getMapper(UserMapper.class);List<Integer> roleList = new ArrayList<Integer>();Collections.addAll(roleList,2,4,6);Map<String,Object> paramMap = new HashMap<String, Object>();paramMap.put("idList",roleList);paramMap.put("gender",1);List<User> userList = userMapper.getUserListByMap(paramMap);System.out.println(userList);session.close();}

SQL映射的XML文件

MyBatis 真正的强大在于SQL映射语句,也是它的魅力所在,专注于SQL,功能强大,SQL映射的配置却是相当简单

SQL映射文件的几个顶级元素(按照定义的顺序)

  • mapper – namespace
  • cache – 配置给定命名空间的缓存
  • cache-ref – 从其他命名空间引用缓存配置
  • resultMap –用来描述数据库结果集和对象的对应关系
  • sql – 可以重用的SQL块,也可以被其他语句引用
  • insert – 映射插入语句
  • update – 映射更新语句
  • delete – 映射删除语句
  • select – 映射查询语句
    select、insert、update、delete这四个标签的基本使用上一张已经讲过了,这里就不再将基本使用了

resultMap

resultMap属性

  • id:resultMap的唯一标识
  • type:Java实体类

修改实体类

java">private String password;//数据库名称为userPassword

思考:当数据库的字段名与实体类中的属性名不一致时,应该怎么办

方式1:在查询时,给字段取别名,与实体类属性名保持一致

    <select id="selectUserByUserCode" parameterType="string" resultType="User">SELECT ID,USERNAME,USERPASSWORD AS PASSWORD WHERE USERCODE=#{userCode}</select>

方式2:resultMap手动映射不一致的两个名字

注意!!!

  • column的值不是数据库表字段名称,而是查询结果的字段名称。
  • 也就是说手动映射不是将实体类属性名与数据库表字段名映射,而是将实体类属性名与查询结果的字段名映射。
  • 其次,select中如果使用手动映射返回结果,则不能使用resultType,而是使用resultMap。
    <resultMap id="userMap" type="User"><id column="id" property="id"/><result column="userCode" property="userCode"/><result column="userName" property="userName"/><result column="userPassword" property="password"/><result column="gender" property="gender"/><result column="birthday" property="birthday"/><result column="phone" property="phone"/></resultMap><select id="selectUserByUserCode" parameterType="string" resultMap="userMap">SELECT ID,USERNAME,USERPASSWORD WHERE USERCODE=#{userCode}</select>

补充

  • 其实mybatis默认是自动映射的,也就是说查询结果的字段名如果跟实体类属性名一致了的话,就不用手动映射
  • 也就是说在默认配置情况下,resultMap也只需要配置不同字段名的映射即可
java">    <resultMap id="userMap" type="User"><!-- 一致的属性和字段名就不用写了 --><result column="phone" property="phone"/></resultMap>
  • resultMap的自动映射级别-autoMappingBehavior,具体参考上一章的settings标签中的属性

resultMap子元素

面试题:MyBatis实现一对一和一对多有几种方式?具体怎么操作的?

答:

  • 有联合查询和嵌套查询,联合查询是几个表联合查询,只查询一次, 通过在resultMap里面配置association节点配置一对一的类就可以完成;

  • 嵌套查询是先查一个表,根据这个表里面的结果的外键id,去再另外一个表里面查询数据,也是通过association/collection配置,但另外一个表的查询通过select属性配置。

resultMap属性

  • id:一般对应数据库中该行的主键id,设置此项可提高MyBatis性能
  • result:映射到JavaBean的某个“简单类型”属性
  • association:映射到JavaBean的某个“复杂类型”属性,比如JavaBean类
  • collection:映射到JavaBean的某个“复杂类型”属性,比如集合

association

  • 复杂的类型关联,一对一
  • 内部嵌套
    • 映射一个嵌套JavaBean属性
  • 属性
    • property:映射数据库列的实体对象的属性
    • javaType:完整Java类名或者别名
    • resultMap:引用外部resultMap
  • 子元素
    • id
    • result
代码示例:一对一联合查询

实体类

java">@Data
public class Role {private int id;private String roleName;
}@Data
@ToString
public class User implements Serializable {private long id;private String userCode;private String userName;private String password;//数据库名称为userPasswordprivate int gender;private Date birthday;private String phone;private String address;private int userRole;private Role role;
}

mapper

java">   User getUserAndRoleByUserId(int uid);

xml:注意看注释!!!!

    <resultMap id="userMap" type="User" autoMapping="true"><id column="id" property="id"/><result column="userPassword" property="password"/><!--  autoMapping="true"必须要加,否则不会自动映射 --><association property="role" javaType="Role"><!-- 这个role是User实体类中的role属性 --><id column="roleId" property="id"/><!-- 这个roleId是查询结果中的roleId,id是role实体类中的id --></association></resultMap><select id="getUserAndRoleByUserId" parameterType="int" resultMap="userMap">SELECT u.id,u.userCode,u.userName,u.userPassword,u.gender,u.birthday,u.phone,u.address,r.id AS roleId,r.roleNameFROM SMBMS_USER u,SMBMS_ROLE rWHERE u.userRole=r.id AND u.id=#{uid}</select>

collection

  • 复杂类型集合,一对多
  • 内部嵌套
    • 映射一个嵌套结果集到一个列表
  • 属性
    • property:映射数据库列的实体对象的属性
    • ofType:完整Java类名或者别名(集合所包括的类型)
    • resultMap:引用外部resultMap
  • 子元素
    • id
    • result
代码示例:一对多联合查询

实体类

java">@Data
public class Address {private int id;private String addressDesc;private int postCode;private String tel;private String contact;
}@Data
@ToString
public class User implements Serializable {private long id;private String userCode;private String userName;private String password;//数据库名称为userPasswordprivate int gender;private Date birthday;private String phone;private String address;private int userRole;private Role role;private List<Address> addressList;
}

mapper

java">   User getUserAndAddressListByUserId(int uid);

xml

	<resultMap id="userMap" type="User" autoMapping="true"><id column="id" property="id"/><result column="userPassword" property="password"/><!--  autoMapping="true"必须要加,否则不会自动映射 --><association property="role" javaType="Role" autoMapping="true"><!-- 这个role是User实体类中的role属性 --><id column="roleId" property="id"/><!-- 这个roleId是查询结果中的roleId,id是role实体类中的id --></association><!--  autoMapping="true"必须要加,否则不会自动映射 --><collection property="addressList" ofType="Address" autoMapping="true"><!-- 这个addressList是User实体类中的addressList属性 --><id column="addressId" property="id"/><!-- 这个addressId是查询结果中的addressId,id是address实体类中的id --></collection></resultMap><select id="getUserAndAddressListByUserId" parameterType="int" resultMap="userMap">SELECT u.id,u.userCode,u.userName,u.userPassword,u.gender,u.birthday,u.phone,u.address,a.id AS addressId,a.contact,a.addressDesc,a.postCode,a.telFROM SMBMS_USER u,SMBMS_ADDRESS aWHERE u.id=a.userId AND u.id=#{uid}</select>

resultMap自动映射(autoMappingBehavior)的三个匹配级别

  • NONE:禁止自动匹配
  • PARTIAL(默认):自动匹配所有属性,内部嵌套除外
  • FULL:自动匹配所有

面试题:resultType与resultMap的区别

对象不同描述不同类型适用不同
resultMapresultMap如果查询出来的列名和pojo的属性名不一致,通过定义一个resultMap对列名和pojo属性名之间作一个映射关系。resultMap对于一对一表连接的处理方式通常为在主表的pojo中添加嵌套另一个表的pojo,然后在mapper.xml中采用association节点元素进行对另一个表的连接处理。mybatis中在查询进行select映射的时候,返回类型可以用resultType,也可以用resultMap。
resultTyperesultType使用resultType进行输出映射,只有查询出来的列名和pojo中的属性名一致,该列才可以映射成功。resultType无法查询结果映射到pojo对象的pojo属性中,根据对结构集查询遍历的需要选择使用resultType还是resultMap。适用于单表查询。resultType是直接表示返回类型的,而resultMap则是对外部ResultMap的引用,但是resultType跟resultMap不能同时存在。

select属性小结

属性描述
id在命名空间中唯一的标识符,可以被用来引用这条语句
parameterType将会传入这条语句的参数类的完全限定名或别名
resultType从这条语句中返回的期望类型的类的完全限定名或别名。注意集合情形,那应该是集合可以包含的类型,而不能是集合本身。使用resultType或resultMap,但不能同时使用
resultMap命名引用外部的resultMap
flushCache将其设置为true,不论语句什么时候被调用,都会导致缓存被清空。默认值:false
useCache将其设置为true,将会导致本条语句的结果被缓存。默认值:true
timeout这个设置驱动程序等待数据库返回请求结果,并抛出异常时间的最大等待值。默认不设置(驱动自行处理)
fetchSize这是暗示驱动程序每次批量返回的结果行数
statementTypeSTATEMENT,PREPARED或CALLABLE的一种。让MyBatis选择使用Statement,PreparedStatement或CallableStatement。默认值:PREPARED
resultSetTypeFORWARD_ONLY

Mybatis缓存

  • 一级缓存
  • 二级缓存
  • 二级缓存的配置
    • MyBatis的全局cache配置
    • 在Mapper XML文件中设置缓存,默认情况下是没有开启缓存
    • 在Mapper XML文件配置支持cache后,如果需要对个别查询进行调整,可以单独设置cache

一级缓存:默认开启

代码测试

java">    @Testpublic void getUserCountForMapper(){SqlSession session = factory.openSession();UserMapper userMapper = session.getMapper(UserMapper.class);userMapper.getUserCount();//第一次查询System.out.println("==================================");userMapper.getUserCount();//一模一样的第二次查询session.close();}

日志

java">[DEBUG] 2024-04-15 22:30:26,026 org.apache.ibatis.transaction.jdbc.JdbcTransaction - Opening JDBC Connection
[DEBUG] 2024-04-15 22:30:26,289 org.apache.ibatis.datasource.pooled.PooledDataSource - Created connection 1896552614.
[DEBUG] 2024-04-15 22:30:26,289 org.apache.ibatis.transaction.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@710b18a6]
[DEBUG] 2024-04-15 22:30:26,291 com.zjl.mapper.UserMapper.getUserCount - ==>  Preparing: SELECT COUNT(1) FROM SMBMS_USER 
[DEBUG] 2024-04-15 22:30:26,314 com.zjl.mapper.UserMapper.getUserCount - ==> Parameters: 
[DEBUG] 2024-04-15 22:30:26,325 com.zjl.mapper.UserMapper.getUserCount - <==      Total: 1
==================================
[DEBUG] 2024-04-15 22:30:26,326 org.apache.ibatis.transaction.jdbc.JdbcTransaction - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@710b18a6]
[DEBUG] 2024-04-15 22:30:26,326 org.apache.ibatis.transaction.jdbc.JdbcTransaction - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@710b18a6]
[DEBUG] 2024-04-15 22:30:26,326 org.apache.ibatis.datasource.pooled.PooledDataSource - Returned connection 1896552614 to pool.
Disconnected from the target VM, address: '127.0.0.1:1653', transport: 'socket'Process finished with exit code 0

可以看到两次一模一样的SQL,只会执行一次,其实从第二次开始,就是从一级缓存中直接去拿结果的。

当然,这是在同一个SqlSession的情况下,那如果是两个sqlSession对象呢?

java">    @Testpublic void getUserCountForMapper(){SqlSession session = factory.openSession();UserMapper userMapper = session.getMapper(UserMapper.class);userMapper.getUserCount();//第一次查询session.close();System.out.println("==================================");session = factory.openSession();userMapper = session.getMapper(UserMapper.class);userMapper.getUserCount();//一模一样的第二次查询session.close();}
java">[DEBUG] 2024-04-15 22:36:31,332 org.apache.ibatis.transaction.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@710b18a6]
[DEBUG] 2024-04-15 22:36:31,334 com.zjl.mapper.UserMapper.getUserCount - ==>  Preparing: SELECT COUNT(1) FROM SMBMS_USER 
[DEBUG] 2024-04-15 22:36:31,372 com.zjl.mapper.UserMapper.getUserCount - ==> Parameters: 
[DEBUG] 2024-04-15 22:36:31,406 com.zjl.mapper.UserMapper.getUserCount - <==      Total: 1
==================================
[DEBUG] 2024-04-15 22:36:31,406 org.apache.ibatis.transaction.jdbc.JdbcTransaction - Opening JDBC Connection
[DEBUG] 2024-04-15 22:36:31,424 org.apache.ibatis.datasource.pooled.PooledDataSource - Created connection 1544614339.
[DEBUG] 2024-04-15 22:36:31,424 org.apache.ibatis.transaction.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5c10f1c3]
[DEBUG] 2024-04-15 22:36:31,425 com.zjl.mapper.UserMapper.getUserCount - ==>  Preparing: SELECT COUNT(1) FROM SMBMS_USER 
[DEBUG] 2024-04-15 22:36:31,425 com.zjl.mapper.UserMapper.getUserCount - ==> Parameters: 
[DEBUG] 2024-04-15 22:36:31,425 com.zjl.mapper.UserMapper.getUserCount - <==      Total: 1
[DEBUG] 2024-04-15 22:36:31,426 org.apache.ibatis.transaction.jdbc.JdbcTransaction - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5c10f1c3]
[DEBUG] 2024-04-15 22:36:31,426 org.apache.ibatis.transaction.jdbc.JdbcTransaction - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@5c10f1c3]
[DEBUG] 2024-04-15 22:36:31,426 org.apache.ibatis.datasource.pooled.PooledDataSource - Returned connection 1544614339 to pool.
Disconnected from the target VM, address: '127.0.0.1:1776', transport: 'socket'Process finished with exit code 0

可以看到,执行了两次SQL查询,因此缓存失效了
那么在同一个SqlSession对象的前提下如果第一次查询之后对这个表做了一次更新操作(增删改)呢?

java">    public void getUserCountForMapper(){SqlSession session = factory.openSession();UserMapper userMapper = session.getMapper(UserMapper.class);userMapper.getUserCount();//第一次查询System.out.println("==================================");User user = new User(66,"zjl","在家里",new Date());userMapper.updateUser(user);userMapper.getUserCount();//一模一样的第二次查询session.close();}
java">[DEBUG] 2024-04-15 22:39:59,234 org.apache.ibatis.transaction.jdbc.JdbcTransaction - Opening JDBC Connection
[DEBUG] 2024-04-15 22:39:59,475 org.apache.ibatis.datasource.pooled.PooledDataSource - Created connection 1896552614.
[DEBUG] 2024-04-15 22:39:59,475 org.apache.ibatis.transaction.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@710b18a6]
[DEBUG] 2024-04-15 22:39:59,479 com.zjl.mapper.UserMapper.getUserCount - ==>  Preparing: SELECT COUNT(1) FROM SMBMS_USER 
[DEBUG] 2024-04-15 22:39:59,511 com.zjl.mapper.UserMapper.getUserCount - ==> Parameters: 
[DEBUG] 2024-04-15 22:39:59,520 com.zjl.mapper.UserMapper.getUserCount - <==      Total: 1
==================================
[DEBUG] 2024-04-15 22:39:59,522 com.zjl.mapper.UserMapper.updateUser - ==>  Preparing: UPDATE SMBMS_USER SET userCode=?, userName=?, birthday=? WHERE id=? 
[DEBUG] 2024-04-15 22:39:59,526 com.zjl.mapper.UserMapper.updateUser - ==> Parameters: zjl(String), 在家里(String), 2024-04-15 22:39:59.521(Timestamp), 66(Long)
[DEBUG] 2024-04-15 22:39:59,555 com.zjl.mapper.UserMapper.updateUser - <==    Updates: 1
[DEBUG] 2024-04-15 22:39:59,555 com.zjl.mapper.UserMapper.getUserCount - ==>  Preparing: SELECT COUNT(1) FROM SMBMS_USER 
[DEBUG] 2024-04-15 22:39:59,555 com.zjl.mapper.UserMapper.getUserCount - ==> Parameters: 
[DEBUG] 2024-04-15 22:39:59,556 com.zjl.mapper.UserMapper.getUserCount - <==      Total: 1
[DEBUG] 2024-04-15 22:39:59,556 org.apache.ibatis.transaction.jdbc.JdbcTransaction - Rolling back JDBC Connection [com.mysql.jdbc.JDBC4Connection@710b18a6]
[DEBUG] 2024-04-15 22:39:59,662 org.apache.ibatis.transaction.jdbc.JdbcTransaction - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@710b18a6]
[DEBUG] 2024-04-15 22:39:59,662 org.apache.ibatis.transaction.jdbc.JdbcTransaction - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@710b18a6]
[DEBUG] 2024-04-15 22:39:59,663 org.apache.ibatis.datasource.pooled.PooledDataSource - Returned connection 1896552614 to pool.
Disconnected from the target VM, address: '127.0.0.1:1857', transport: 'socket'Process finished with exit code 0

可以看到一共三次SQL,两次查询,因此 缓存失效了

二级缓存

开启二级缓存

  1. mybatis-config.xml中

    <settings><setting name="cacheEnabled" value="true"/>
    </settings>
    
  2. mapper.xml中

    java"><cache/>
    
  3. 查询sql

<select id="selectAll" resultType="Emp"  useCache="true">

代码示例

    <cache/><!-- 开启二级缓存 --><select id="getUserCount" resultType="int">SELECT COUNT(1) FROM SMBMS_USER</select>

一级缓存生效的情况就不在演示了,现在试一下同一个sqlsessionFactory,不同sqlSession走不走缓存

java">    @Testpublic void getUserCountForMapper(){SqlSession session = factory.openSession();UserMapper userMapper = session.getMapper(UserMapper.class);userMapper.getUserCount();//第一次查询session.close();//测试时一定记得关闭上一个sessionSystem.out.println("==================================");session = factory.openSession();userMapper = session.getMapper(UserMapper.class);userMapper.getUserCount();//一模一样的第二次查询session.close();}

日志

java">[DEBUG] 2024-04-15 22:54:00,214 org.apache.ibatis.datasource.pooled.PooledDataSource - Created connection 896138248.
[DEBUG] 2024-04-15 22:54:00,215 org.apache.ibatis.transaction.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@3569fc08]
[DEBUG] 2024-04-15 22:54:00,217 com.zjl.mapper.UserMapper.getUserCount - ==>  Preparing: SELECT COUNT(1) FROM SMBMS_USER 
[DEBUG] 2024-04-15 22:54:00,244 com.zjl.mapper.UserMapper.getUserCount - ==> Parameters: 
[DEBUG] 2024-04-15 22:54:00,254 com.zjl.mapper.UserMapper.getUserCount - <==      Total: 1
[DEBUG] 2024-04-15 22:54:00,257 org.apache.ibatis.transaction.jdbc.JdbcTransaction - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@3569fc08]
[DEBUG] 2024-04-15 22:54:00,257 org.apache.ibatis.transaction.jdbc.JdbcTransaction - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@3569fc08]
[DEBUG] 2024-04-15 22:54:00,257 org.apache.ibatis.datasource.pooled.PooledDataSource - Returned connection 896138248 to pool.
==================================
[DEBUG] 2024-04-15 22:54:00,260 com.zjl.mapper.UserMapper - Cache Hit Ratio [com.zjl.mapper.UserMapper]: 0.5
Disconnected from the target VM, address: '127.0.0.1:2202', transport: 'socket'Process finished with exit code 0

可以看到只有一次查询日志,因此二级缓存生效

失效情况就不再一一列举了:

  • 换了sqlSessionfactory一定会二级失效。
  • 两次查询之间对表更新数据也会二级失效。
  • 两次查询之间手动清空缓存也会二级失效。

面试题:说一下 MyBatis 的一级缓存和二级缓存

一级缓存

  • 基于 PerpetualCache 的 HashMap 本地缓存,它的生命期是和 SQLSession 一致的,有多个 SQLSession 或者分布式的环境中数据库操作,可能会出现脏数据。当 Session flush 或 close 之后,该 Session 中的所有 Cache 就将清空;
  • 一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问,一级缓存是默认开启的。

二级缓存

  • 也是基于 PerpetualCache 的 HashMap 本地缓存,不同在于其存储作用域为 Mapper 级别的,如果多个SQLSession之间需要共享缓存,则需要使用到二级缓存,并且二级缓存可自定义存储源,如 Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现 Serializable 序列化接口(可用来保存对象的状态)。
  • 二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存,此后若再次执行相同的查询语句,结果就会从缓存中获取。

开启二级缓存数据查询流程:二级缓存 -> 一级缓存 -> 数据库

缓存更新机制

当某一个作用域(一级缓存 Session/二级缓存 Mapper)进行了C/U/D 操作后,默认该作用域下所有 select 中的缓存将被 clear。

面试题:简述Mybatis都有哪些执行器?他们之间的区别是什么?

mybatis有三种executor执行器,分别为simpleexecutor、reuseexecutor、batchexecutor。

simpleExecutor执行器

在每执行一次update或select,就开启一个statement对象,用完后就关闭。

reuseExecutor执行器

在执行update或select时以sql作为key去查找statement,有就直接使用,没有就创建,使用完毕后不关闭,放入Map<String,Statement>中,供下次使用。重复使用statement。

batchExecutor执行器

执行update(jdbc批处理不支持select),会把所有sql添加到批处理中addbatch();等待统一批处理executorbatch();它缓存了·多个statement,每一个statement都是addbatch(),后等待进行executorbatch()批处理。

作用范围:统一限制在sqlsession生命周期范围内。


http://www.ppmy.cn/ops/8435.html

相关文章

高效便捷!解锁阿里云跨账号专线互联的全新实施方案

作者&#xff1a;小丫、琉璃 背景 为持续提升金融云环境的合规标准以及可用区内产品服务的性能和稳定性&#xff0c;阿里云将对杭州地域BCD三个金融云可用区进行基础设施架构升级与改造&#xff0c;对应可用区云产品将于 2024 年后停止服务&#xff0c;需要将业务迁移到新可用…

互连芯片浪潮席卷AI服务器:突破瓶颈,再创辉煌

改变AI服务器&#xff1a;互连芯片技术创新和突破 AI服务器崛起&#xff0c;引领未来创新根据TrendForce数据&#xff0c;AI服务器出货量达130,000台&#xff0c;占服务器总出货量的1%。主要制造商推出生成式AI产品&#xff0c;推动订单激增。ChatGPT等应用的需求持续增长&…

php单文件实现文件批量预览——图片,音频,视频

有一天&#xff0c;无意中发现了一个在线文件预览地址。即那种暴露目录的地址。该目录下清一色的图片。觉得一个个点击进去查看太麻烦了&#xff0c;因此特意写了这个文件预览代码。单php文件&#xff0c;放到站点下运行即可。 1.实用场景 比如一个在线站点文件目录如下&#…

【原创】springboot+mysql疫苗预约管理系统设计与实现

个人主页&#xff1a;程序猿小小杨 个人简介&#xff1a;从事开发多年&#xff0c;Java、Php、Python、前端开发均有涉猎 博客内容&#xff1a;Java项目实战、项目演示、技术分享 文末有作者名片&#xff0c;希望和大家一起共同进步&#xff0c;你只管努力&#xff0c;剩下的交…

【学习】人工智能在软件测试中的作用有哪些

随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;已经渗透到各个领域&#xff0c;并发挥着不可或缺的作用。其中&#xff0c;在软件测试方面&#xff0c;AI的应用也愈发普遍。本文将探讨人工智能在软件测试领域的关键作用以及其带来的积极影响。 一、提升测…

开源项目实现简单实用的股票回测

1 引言 之前&#xff0c;尝试做股票工具一直想做的大而全&#xff0c;试图抓取长期的各个维度数据&#xff0c;然后统计或者训练模型。想把每个细节做到完美&#xff0c;结果却陷入了细节之中&#xff0c;最后烂尾了。 最近&#xff0c;听到大家分享了一些关于深度学习、时序…

每日一题:地下城游戏

恶魔们抓住了公主并将她关在了地下城 dungeon 的 右下角 。地下城是由 m x n 个房间组成的二维网格。我们英勇的骑士最初被安置在 左上角 的房间里&#xff0c;他必须穿过地下城并通过对抗恶魔来拯救公主。 骑士的初始健康点数为一个正整数。如果他的健康点数在某一时刻降至 0…

Module外贸主题开心版下载-v5.7.0版本WordPress企业模板

主题下载地址&#xff1a;Module外贸主题开心版下载-v5.7.0版本 Module主题介绍&#xff1a;采用全新模块化开发&#xff0c;首页模块可视化拖拽自由组合&#xff0c;可自定义搭建出不同行业适用的企业网站。同时主题全面支持WPML多语言切换&#xff0c;可轻松搭建外贸网站。W…