Java EE数据持久化框架 • 【第5章 MyBatis代码生成器和缓存配置】

时间:2023-12-16 08:24:14

全部章节   >>>>


本章目录

5.1 配置MyBatis Generator

5.1.1 MyBatis Generator介绍

5.1.2 MyBatis Generator XML文件示例

5.1.3 MyBatis Generator XML详解

5.1.3 <'context>标签属性

5.1.3 <'property>标签

5.1.3 <'plugin>和<'commentGenerator>标签

5.1.3 <'jdbcConnection>标签

5.1.3 <'javaModelGenerator>标签

5.1.3 <'javaClientGenerator>标签

5.1.4 实践练习

5.2 运行MyBatis Generator和Example

5.2.1 编写Java代码运行MyBatis Generator

5.2.2 生成Example

5.2.3 实践练习

5.3 MyBatis缓存

5.3.1 MyBatis一级缓存

5.3.2 MyBatis二级缓存

5.3.3 使用MyBatis二级缓存

5.3.4 实践练习

5.4 MyBatis脏数据

5.4.1 MyBatis脏数据的产生

5.4.2 MyBatis脏数据的解决方案

5.4.3 实践练习

总结:


5.1 配置MyBatis Generator

5.1.1 MyBatis Generator介绍

MyBatis在很大程度上大大降低了以前繁琐的JDBC代码,并且提供了大量的标签对SQL语句的支持,但是在操作不同数据表方面,仍然存在大量重复工作。

MyBatis的开发团队提供了一个很强大的代码生成器——MyBatis Generator(MyBatis生成器,简称MBG)

MBG自动产生包含了数据库表对应的实体类、Mapper接口类、Mapper XML文件和Example对象等,这些代码文件中几乎包含了全部的单表操作方法。

示例:要在MyBatis中使用MBG,首先需要在pom.xml中添加坐标依赖,如下:

<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.7</version>
</dependency>

版本号和mybatis关系不大

5.1.2 MyBatis Generator XML文件示例

在项目的src/main/resources中创建一个generator目录,在该目录下创建一个generatorConfig.xml文件,该配置文件主要针对MBG生成代码时的一系列配置,如数据库信息、代码位置配置等,该文件内容核心结构如下:

示例:

Java EE数据持久化框架 • 【第5章  MyBatis代码生成器和缓存配置】

<!-- 配置生成器 -->
<generatorConfiguration>
<!--
targetRuntime="MyBatis3Simple": 不会生成与Example相关的方法
targetRuntime="MyBatis3":默认值,生成基于MyBatis3.x以上版本的内容,包括XXXBySample
defaultModelType="flat":所有内容(主键,BLOB)等全部生成在一个对象中
defaultModelType="hierarchical":主键生成一个XXKey对象,BLOB等单独生成一个对象,其他简单属性在一个对象中
defaultModelType="conditional"(默认值)表主键只有一列,不为该字段生成单独实体类,将该字段合并到基本实体类中
-->
<context id="MySQLContext" targetRuntime="MyBatis3Simple" defaultModelType="flat">
<!-- 配置前置分隔符和后置分隔符 -->
<property name="beginningDelimiter" value="`"/>
<property name="endingDelimiter" value="`"/>
<!-- 配置注释信息 -->
<commentGenerator>
<!-- 不生成注释 -->
<property name="suppressAllComments" value="true"/>
<property name="suppressDate" value="true"/>
<property name="addRemarkComments" value="true"/>
</commentGenerator>
<!-- 数据库连接配置 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/dbauthority"
userId="root" password="root"></jdbcConnection>
<!-- targetPackage:生成实体类存放的包名, targetProject:指定目标项目路径,可以使用相对路径或绝对路径 -->
<javaModelGenerator targetPackage="jack.mybatis.authority.model" targetProject="src\main\java">
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<!-- 配置SQL映射器Mapper.xml文件的属性 -->
<sqlMapGenerator targetPackage="jack.mybatis.authority.mapper" targetProject="src\main\resources"/>
<!-- type="XMLMAPPER":所有的方法都在XML中,接口调用依赖XML文件 -->
<javaClientGenerator targetPackage="jack.mybatis.authority.mapper" type="XMLMAPPER"
targetProject="src\main\java"/>
<!-- 生成所有表的映射 -->
<table tableName="%">
<!-- 配置自增主键 -->
<generatedKey column="id" sqlStatement="MySQL" />
</table>
</context>
</generatorConfiguration>

5.1.3 MyBatis Generator XML详解

<context>标签

在MBG XML中至少配置1个<context>标签,该标签用于指定生成一组对象的环境,可配置多个,但是具体运行时选择其中一个。

该标签下的子标签顺序必须如下,否则报错:

property(0个或多个)。

plugin(0个或多个)。

commentGenerator(0个或1个)。

jdbcConnection(1个)。

javaTypeResolver(0个或1个)。

javaModelGenerator(1个)。

sqlMapGenerator(0个或1个)。

javaClientGenerator(0个或1个)。

table(1个或多个)。

5.1.3 <'context>标签属性

<context id=“自定义" targetRuntime="MyBatis3Simple" defaultModelType="flat">)

属性名

取值

作用

id

自定义

唯一确定该标签,运行时可以设置

defaultModelType

定义MBG如何生成实体类

conditional(默认值)

表主键只有一列,不为该字段生成单独实体类,将该字段合并到基本实体类中

flat(推荐)

只为每张表生成一个实体类,这个实体类包含表中的所有字段

hierarchical

如果表有主键,那么该模型会产生一个单独的主键实体类

targetRuntime

生成代码的运行时环境

MyBatis3

默认值

MyBatis3Simple

这种情况不会生成与Example相关的方法

5.1.3 <'property>标签

<property>标签

<property>标签为属性标签,它定义一个全局意义的属性及值。

属性名

取值

autoDelimitKeywords

自动给关键字添加分隔符的属性

beginningDelimiter和endingDelimiter

配置前置分隔符和后置分隔符的属性。如果设置了前置分隔符和后置分隔符,那么,当数据库的表名、字段名中出现空格或关键字时,MyBatis将为这些名称加上分隔符。

5.1.3 <'plugin>和<'commentGenerator>标签

<plugin>标签

  • <plugin>标签用来定义一个插件,用于扩展或修改通过MBG生成的代码。<plugin>标签可以配置0个或者多个,个数不受限制。

<commentGenerator>标签

  • 用来配置如何生成注释信息,最多可以配置一个。常用的子标签property属性如下:
<commentGenerator>
<!--阻止生成注释,默认为false-->
<property name="suppressAllComments" value="true" />
<!--阻止生成的注释,包含时间戳,默认为false-->
<property name="suppressDate" value="true" />
<!--注释是否添加数据库表的备注信息,默认为false-->
<property name="addRemarkComments" value="true" />
</commentGenerator>

5.1.3 <'jdbcConnection>标签

 <jdbcConnection>标签

  • 该标签用于指定MBG要连接的数据库信息,该标签必选,并且只能有一个。
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/db_authority"
userId="root" password="abc123">
</jdbcConnection>

<javaTypeResolver>标签

  • 该标签的配置用来指定JDBC类型和Java类型如何转换,最多可以配置一个,一般不配置,  默认即可。

5.1.3 <'javaModelGenerator>标签

<javaModelGenerator>标签

  • 该标签用来控制生成的实体类,targetPackage生成实体类存放的包名,targetProject指定目标项目路径。
<javaModelGenerator  targetPackage="jack.mybatis.authority.model“  targetProject="src\main\java">
<property name="trimStrings" value="true" />
</javaModelGenerator>

<sqlMapGenerator>标签

  • 该标签用于配置SQL映射器Mapper.xml文件的属性,该标签可选,最多配置一个,如不配置则只产生实体类,不产生对应的Mapper.xml映射文件。
<sqlMapGenerator  targetPackage="jack.mybatis.authority.dao“  targetProject="src\main\resources" />

5.1.3 <'javaClientGenerator>标签

 <javaClientGenerator>标签

  • 该标签用于配置Java客户端生成器(Mapper接口)的属性,该标签可选,最多配置一个。如果不配置该标签,就不会生成Mapper接口。
  • 配置中的type属性,推荐使用XMLMAPPER
<!--type="XMLMAPPER":所有的方法都在XML中,接口调用依赖XML文件 -->
<!--如果type= ANNOTATEDMAPPER,则SQL生成在Mapper接口的注解中-->
<javaClientGenerator targetPackage="jack.mybatis.authority.dao" type="XMLMAPPER"
targetProject="src\main\java" />

<table>标签

  • <table>标签用于配置需要通过映射的数据库表,只有在<table>标签中配置过的表,才能经过上述其他配置生成最终的代码。<table>标签至少要配置一个,可以配置多个。
  • <table>标签有一个必选属性tableName,该属性指定要生成的表名,可以使用SQL通配符匹配多个表。
例如,要生成全部的表,可以作如下配置:
<table tableName="%">
例如,要生成有关customer的表,可以作如下配置:
<table tableName="%customer%">

 <generatedKey>标签是<table>的子标签

  • 用来指定自动生成主键的属性(identity字段或者sequences序列)。如果指定这个标签,MBG将在生成insert的SQL映射文件中插入一个<selectKey>标签。这个标签非常重要,而且只能配置一个。
  • (1)column属性:生成列的列名。
  • (2)sqlStatement属性:根据不同数据库定义,mysql则定义为MySQL。
<table tableName= “%" >
<generatedKey column ="id " sqlStatement= "MySQL" />
</table>

5.1.4 实践练习

5.2 运行MyBatis Generator和Example

MBG提供了很多种运行方式,常用的有以下几种:

  • 使用Java编写代码运行。
  • 从命令提示符运行。
  • 使用Maven Plugin运行。
  • 使用Eclipse插件运行。

使用Java编码方式运行的好处是,generatorConfig.xml中配置的一些特殊的类(如<commentGenerator>标签中type属性配置的定制化的实现类)只要在当前项目中,或者在当前项目的classpath中,就可以直接使用。

5.2.1 编写Java代码运行MyBatis Generator

如何编写代码运行MyBatis Generator生成实体类、接口和Mapper映射文件

保证项目中已经添加了mybatis-generator-core.jar依赖

在项目的src/main/java目录下新建jack.mybatis.blog.generator包(包名可自定)

在改包下新建Generator.java类,包含main方法(注:该类代码无需掌握)

public class Generator {
// targetRuntime="MyBatis3Simple", 不生成Example
public void generateMyBatis() {
List<String> warnings = new ArrayList<String>();//该集合记录MBG执行过程中的警告信息
boolean overwrite = true ; //当生成的代码重复时,覆盖原代码
String generatorFile = “/generator/generatorConfig.xml”; //指定配置文件路径
InputStream is = Generator.class.getResourceAsStream(generatorFile);//读取MBG配置文件
ConfigurationParser cp = new ConfigurationParser(warnings);
try {
Configuration config = cp.parseConfiguration(is);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings) myBatisGenerator.generate(null);//执行生成代码
}
catch (Exception e) {
e.printStackTrace();
}
//打印出执行过程中的警告信息,以便于修改
for (String warning : warnings) {
System.out.println(warning);
}
}
//main方法为java普通类运行入口
public static void main(String[] args) {
Generator generator = new Generator();
generator.generateMyBatis();
}
}

5.2.2 生成Example

  • 在MBG的配置文件中,如果将targetRuntime属性值设置为MyBatis3,则在生成MyBatis实体类的同时,还生成与之对应的带Example的辅助查询工具类。
  • Example类包含了增加、修改、删除,以及查询时动态构建的where子句,表中的每个非BLOB列可以被包括在where子句中,Example类可以用来生成一个几乎无限的where子句,方便测试和后期使用。
  • 比如,与实体类SysUser对应的Example类为SysUserExample,它包含了常见的CRUD等方法的代码示例。

实现一个用户管理高级查询功能,根据输入的条件去检索用户信息。这个功能还需要支持以下四种情况:

  • 当只输入用户名关键字时,需要根据用户名关键字进行模糊查询。
  • 当只一个日期时,需要获取创建时间在该日期(含)之后的用户信息。
  • 当同时输入用户名关键字和日期时,用这两个条件去查询匹配的用户。
  • 如果用户名关键字和日期都没有输入,则查询全部用户信息。

通过MGB已逆向生成了所有表所对应的实体类、Mapper接口,并且还生成Example类,现在只需定义一个测试方法来实现上述需求,代码如下:

SysUserMapper userMapper = sqlSession.getMapper(SysUserMapper.class);
SysUserExample userExample = new SysUserExample(); // 创建Example对象
SysUserExample.Criteria userCriteria = userExample.createCriteria(); // 创建查询条件
if(!keyUserName.isEmpty())
// 设置根据用户名关键字检索的查询条件
userCriteria.andUserNameLike('%'+keyUserName+'%');
if(!createTimeStr.isEmpty()) {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
Date createTime = formatter.parse(createTimeStr);
// 设置根据时间检索的查询条件
userCriteria.andCreateTimeGreaterThanOrEqualTo(createTime);
}
// 执行查询
List<SysUser> users = userMapper.selectByExample(userExample);
System.out.println("用户数量:"+users.size());

updateByExample()方法和updateByExampleSelective()方法都可以实现更新,区别是当对象的属性为空时,第一个方法会将值更新为null,第二个方法不会更新null属性的字段。

更新id在3至7范围的用户的密码为999999,采用UserMapper的updateByExampleSelective()方法实现以上需求:

	SysUserMapper userMapper = sqlSession.getMapper(SysUserMapper.class);
SysUserExample userExample = new SysUserExample();
SysUserExample.Criteria userCriteria = userExample.createCriteria();
// 更新条件:用户id在3~7的范围
userCriteria.andIdBetween(3L, 7L);
// 创建一个要设置的对象
SysUser user = new SysUser();
// 将密码设置为999999
user.setUserPassword("999999");
// 执行查询
int result = userMapper.updateByExampleSelective(user, userExample);
sqlSession.commit();
System.out.println("更新了"+result+"个用户");

相信大家对Example应该己经有所了解,使用Example查询能解决大部分复杂的单表操作,从一定程度上能减少工作量。但是建议在条件很多并且判断很多的情况下,避免使用Example查询,因为生成的代码毕竟不是特别人性化,在这种情况下,根据需要自己去完善Mapper XML方式会更有效。

5.2.3 实践练习

5.3 MyBatis缓存

使用缓存可以使应用更快地获取数据,避免频繁的数据库交互,尤其是在查询越多、缓存命中率越高的情况下,使用缓存的作用就越明显。

缓存的应用在企业级开发中随处可见,合理的使用缓存能大大提高项目的性能,因为缓存的数据一般是在内存中,可以降低对数据库的访问次数。

MyBatis作为持久化框架,提供了非常强大的查询缓存特性,可以非常方便地配置和定制使用。MyBatis提供了一级缓存和二级缓存,其中一级缓存默认启用,不能控制,所以一般提到的缓存,是指二级缓存(可配置)。

5.3.1 MyBatis一级缓存

一级缓存是MyBatis默认开启且无法控制的缓存,是属于SqlSession级别,目的是提高查询结果的利用率,在同一个SqlSession实例下进行查询,会使用到一级缓存,而不同的SqlSession实例则不会使用。

	SqlSession sqlSession = getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 查询id为1L的用户
SysUser user1 = userMapper.selectUserById(1L);
// 更新之前userName的值为admin
System.out.println("更新之前,user1的userName="+user1.getUserName());
// 更新user1的userName
user1.setUserName("new_admin");
// 再次获取id为1L的用户
SysUser user2 = userMapper.selectUserById(1L);
// 虽然没有更新数据库,但是user2的userName的值和user1重新赋值的名字相同,
// 它们都是new_admin
System.out.println("user2的userName="+user2.getUserName());
// 由于从一级缓存中存在键值selectUserById(1L),因此user1和user2是同一个实例
System.out.println("user1==user2:"+(user1==user2));

Java EE数据持久化框架 • 【第5章  MyBatis代码生成器和缓存配置】

示例:

重新开启一个新的SqlSession实例,再次查询该数据:

	System.out.println("开启新的SqlSession");
sqlSession = getSqlSession(); // 开启一个新的SqlSession
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 查询id为1L的用户
SysUser user2 = userMapper.selectUserById(1L);
// 第二个SqlSession获取的的userName的值是admin
System.out.println("user2的userName="+user2.getUserName());
// 这里的user2和前一个SqlSession查询的结果是两个不同的实例
System.out.println("user1==user2:"+(user1==user2));
// 执行删除操作(或者新增、更新操作),将清空一级缓存
userMapper.deleteUserById(10L);
sqlSession.commit();
// 再次查询id为1L的用户
SysUser user3 = userMapper.selectUserById(1L);
System.out.println("user3的userName="+user3.getUserName());
// 由于将清空了一级缓存,因此user2和user3不是同一个实例
System.out.println("user2==user3:"+(user2==user3));

User2和刚才的user1虽然数据一样,但是不是同一个对象,打印false

执行删除操作后,一级缓存会被清除,所以查询的user3和user2也不是同一个对象,打印false

1)在同一个SqlSession下进行的查询操作,查询的结果会默认放入一级缓存中,再次查询时会到一级缓存中去寻找是否存在该数据,如果存在则取出,而不查询数据库;如果不存在,则再去查找数据库,并且放入一级缓存中。当然在SqlSession下进行任何的新增、更新和删除操作都会清空一级缓存,以保证数据的准确性。

2)如果希望某个查询操作不使用一级缓存,则可以在配置时进行如下设置:

<!--清空当前一级缓存-->
<select id="selectUserById" resultMap="userMap" flushCache="true">
select * from sys_user where id=#{id}
</select>

配置为true后,会在查询数据前清空当前的一级缓存,一般不建议设置

5.3.2 MyBatis二级缓存

MyBatis二级缓存是Mapper级别的缓存,每一个Mapper都有一个二级缓存区域(按namespace区分)

如果两个Mapper的namespace相同,则这两个Mapper执行SQL查询所得到的数据将存在相同的二级缓存区域中,即多个SqlSession可能共用二级缓存。

MyBatis的全局配置settings中有一个参数cacheEnabled,该参数是二级缓存的全局开关,在mybatis核心主配置中可以添加配置,如下:

<!--mybatis全局核心配置中-->
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>

在保证二级缓存的全局配置开启的情况下,不同Mapper下的二级缓存需要设置开启,有两种不同的方式进行Mapper命名空间缓存启用方式。

使用Mapper.xml开启二级缓存只需要在接口对应的Mapper.xml中添加<cache/>元素即可,如下:

<!--UserMapper.xml配置文件-->
<mapper namespace="jack.mybatis.authority.mapper.UserMapper">
<cache eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="false“ />
<!--其他配置-->
</mapper>

namespace为命名空间区域

可通过属性配置二级缓存特性

使用接口中注解方式配置映射时,开启该命名空间下二级缓存的方式,如下:

import org.apache.ibatis.annotations.CacheNamespace;

@CacheNamespace(
eviction=FifoCache.class,
flushInterval=60000,
size=512,
readWrite=true
)
public interface UserMapper{
// 接口方法
}

二级缓存配置属性参数说明:

属性名

取值

作用

eviction

(回收策略)

LRU(默认值)

移除最长时间不被使用的对象,这是默认值

FIFO

按对象进入缓存的顺序来移除它们

SOFT

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

WEAK

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

flushinterval

(刷新间隔)

可以被设置为任意的正整数,而且它们代表一个合理的毫秒形式的时间段。默认情况不设置,即没有刷新间隔,缓存仅仅在调用语句时刷新

size(引用数目)

可以被设置为任意正整数,默认值是1024

readOnly(只读)

可设置为true或false。只读的缓存所有调用者返回缓存对象的相同实例,这些对象不能被修改;可读写缓存会通过序列化返回缓存对象的拷贝,这种方式慢一些,但是安全,因此默认值是false

默认的二级缓存会有如下效果:

映射语句文件中的所有select语句将会被缓存。

映射语句文件中的所有insert、update和delete语句会刷新缓存。

缓存会使用Least Recently Used(LRU,最近最少使用)算法来收回。

如果没有设置刷新时间间隔,缓存不会以任何时间顺序来刷新。

默认情况下不能同时使用Mapper.xml和接口两种方式都开启二级缓存,会出现配置冲突,所以一般使用一种配置方式即可,最好不要混用。

5.3.3 使用MyBatis二级缓存

如对UserMapper配置二级缓存后,当调用UserMapper所有的select查询方法时,二级缓存就己经开始起作用,在使用可读写缓存时,要求缓存的实体类需要实现序列化接口,如SysUser实体类:

public class SysUser implements Serializable{
// 其他属性和getter()方法、setter()方法
}

调用UserMapper中的查询方法测试缓存:

sqlSession = getSqlSession();   // 开启一个新的SqlSession
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 第二个SqlSession获取的的userName的值是从二级缓存取得,因此是new_admin
SysUser user2 = userMapper.selectUserById(1L);
// 由于二级缓存配置为读写缓存,user1和user2是两个不同的实例
System.out.println("user1==user2:"+(user1==user2));
SysUser user3 = userMapper.selectUserById(1L);// 这里仍然从二级缓存中取出
// user2和user3都是2级别缓存读取出来的结果,但是为false
System.out.println("user2==user3:"+(user2==user3));

因为二级读写缓存是反序列化的结果,所以为false

5.3.4 实践练习

5.4 MyBatis脏数据

5.4.1 MyBatis脏数据的产生

二级缓存虽然能提高应用效率,减轻数据库服务器的压力,但是如果使用不当,很容易产生脏数据。

MyBatis的二级缓存和命名空间绑定的,每个Mapper命名空间下的二级缓存互不影响,但是难免遇到一个命名空间所缓存的数据,在数据库中已被其他命名空间修改,缓存的数据则不是数据库最真实的,即脏数据。

通过案例来演示脏数据的产生:

示例:在UserMapper中创建了selectUserAndRoleById()方法,该方法获取指定用户id的用户信息,用户信息要求包含该用户所对应的角色信息

<!—添加开启当前UserMapper空间下二级缓存配置-->
<cache eviction="FIFO“ flushInterval="60000“ size="512“ readOnly="false"/> <select id="selectUserAndRoleById" resultType="SysUser">
select u.id, u.user_name, u.user_email, u.user_info, u.head_img, u.create_time,
r.id "role.id", r.role_name "role.roleName", r.enabled "role.enabled",
r.create_by "role.createBy", r.create_time "role.createTime"
from sys_user u join sys_user_role ur on u.id=ur.user_id join sys_role r
on r.id=ur.role_id where u.id=#{userId}
</select>

该方法查询的数据会被缓存到UserMapper空间下的二级缓存中

示例:在RoleMapper.xml添加二级缓存配置,增加<cache/>元素,代码如下

<cache
eviction="LRU"
flushInterval="10000"
size="1024"
readOnly="false"
/>

让SysUser类实现Serializable接口

public class SysUser implements Serializable  {
//相关属性略
}

通过案例来演示脏数据的产生:

示例:在RoleMapper中创建了selectRoleById()方法,该方法获取指定角色id的角色,另外,再创建updateRole()方法,该方法更新指定角色id的角色信息

<resultMap id="roleMap2" type="SysRole">
<result property="id" column="id" />
<result property="roleName" column="role_name" />
<result property="enabled" column="enabled" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" jdbcType="TIMESTAMP" />
</resultMap>
<select id="selectRoleById" resultMap="roleMap2">
select * from sys_role where id=#{id}
</select>
<update id="updateRole">
update sys_role set
role_name=#{roleName},enabled=#{enabled},create_by=#{createBy},
create_time=#{createTime,jdbcType=TIMESTAMP} where id=#{id}
</update>

RoleMapper下更新角色的信息不会影响到UserMapper的二级缓存

1、UserMapper空间下定义开启二级缓存,并且查询方法会把用户及角色数据缓存至二级缓存中。

2、RoleMapper中的修改方法调用后,不会影响到UserMapper空间下的二级缓存,因为二级缓存只在各自空间下有效。

3、所以UserMapper中二级缓存中的数据并非数据库中最新的数据,会造成数据不合理的脏数据。

5.4.2 MyBatis脏数据的解决方案

如何避免脏数据的出现?需要用到参照缓存了。当某几个表可以作为一个业务整体的查询来源时,通常是让几个会关联的数据表同时使用同一个二级缓存,这样就能解决脏数据问题。

将UserMapper.xml中的缓存配置修改如下:

<mapper namespace="jack.mybatis.authority.mapper.UserMapper">
<cache-ref namespace="jack.mybatis.authority.mapper.RoleMapper"/>
<!—其他配置-->
</mapper>

参照缓存可以解决关联较少的情况,如果有几十个表且关联复杂时,使用参照缓存意义不大。因此使用二级缓存需要满足一定的条件:

  • 以查询为主的应用中,只有尽可能少的新增、删除和更新操作。
  • 查询业务绝大多数都是对单表进行操作,由于很少存在互相关联的情况,因此不会出现脏数据。
  • 可以按业务划分对表进行分组时,如果关联的表比较少,可以通过参照缓存进行配置。

5.4.3 实践练习

总结:

  • 利用MyBatisGenerator可以快速生成针对数据库表的实体类、接口、Mapper.xml映射配置文件,以及Example辅助查询工具类。
  • 缓存是一种将数据库查询的数据进行临时存储,一般是存储在内存中,后续查询时减少和数据库的交互次数,提高程序性能的技术。
  • MyBatis中提供了一级缓存和二级缓存,其中一级缓存默认存在,不可控制,同一SqlSession范围内的操作共享该缓存,增、删、改后将清除缓存。
  • 二级缓存需要配置才能生效,属于各自命名空间范围的缓存,在同一命名空间范围内,即使不同SqlSession也可共用,使用时需要注意脏数据的产生。