1、简介
什么是MyBatis:
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
maven仓库:
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency>复制代码
2、基础配置
2.1、导入依赖
<dependencies> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.49</version> </dependency> <!-- https://mvnrepository.com/artifact/junit/junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.1</version> <scope>test</scope> </dependency> </dependencies> <build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> </resources> </build>复制代码
2.2、核心配置文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <!--核心配置文件--> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://192.168.1.101:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <!--每一个Mapper.xml都需要在Mbatis核心配置文件中注册--> <mappers> <mapper resource="com/luke/dao/UserMapper.xml"/> </mappers> </configuration>复制代码
3、增删改查实现
3.1、UserMapper接口
public interface UserMapper { List<User> getUser();//查询所有用户 User getUserById(int id);//根据Id查询用户 int addUser(User user);//增加用户 int updateUser(User user);//修改用户 int deleteUserById(int id);//删除用户 }复制代码
3.2、UserMappper.xml
<?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.luke.dao.UserMapper"> <select id="getUser" resultType="com.luke.pojo.User"> select * from user; </select> <select id="getUserById" resultType="com.luke.pojo.User" parameterType="int"> select * from user where id = #{id}; </select> <insert id="addUser" parameterType="com.luke.pojo.User"> insert into user (id,name,pwd) values (#{id},#{name},#{pwd}); </insert> <update id="updateUser" parameterType="com.luke.pojo.User"> update user set name = #{name},pwd = #{pwd} where id = #{id}; </update> <delete id="deleteUserById" parameterType="int"> delete from user where id = #{id}; </delete> </mapper>复制代码
3.3、Util工具类
public class MybatisUtils { private static SqlSessionFactory sqlSessionFactory; static { try { String resource = "mybatis-config.xml"; InputStream inputStream; inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } public static SqlSession getSqlSession(){ return sqlSessionFactory.openSession(); } }复制代码
3.4、测试类
public class MyTest { //查询所有用户 @Test public void testGetUser(){ SqlSession sqlSession = null; try { sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> users = mapper.getUser(); for (User user : users) { System.out.println(user.toString()); } } catch (Exception e) { e.printStackTrace(); } finally { if (sqlSession != null) sqlSession.close(); } } //根据Id查询用户 @Test public void testGetUserById(){ SqlSession sqlSession = null; try { sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.getUserById(1); System.out.println(user); } catch (Exception e) { e.printStackTrace(); } finally { if (sqlSession != null) sqlSession.close(); } } //增加用户信息 @Test public void testInsert(){ SqlSession sqlSession = null; sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); int i = mapper.addUser(new User(4,"帅帅","123456")); System.out.println(i); sqlSession.commit();//增删改需要提交事务 sqlSession.close(); } //修改用户信息 @Test public void testUpdate(){ SqlSession sqlSession = null; sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); int i = mapper.updateUser(new User(4,"帅帅啊","1234567")); System.out.println(i); sqlSession.commit();//增删改需要提交事务 sqlSession.close(); } //删除用户 @Test public void testDelete(){ SqlSession sqlSession = null; sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); int i = mapper.deleteUserById(4); System.out.println(i); sqlSession.commit();//增删改需要提交事务 sqlSession.close(); } }复制代码
3.5、使用Map作为入参
Map传递参数,直接在sql中取出key即可。
4、配置解析
一般命名为mybatis-config.xml
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。
4.1、环境配置(environments)
MyBatis 可以配置成适应多种环境,由environment id 区分。
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
<environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment>复制代码
Mybatis默认的事务管理器就是:JDBC连接池是:POOLED
4.2、属性(properties)
可以通过properties属性来实现引用配置文件
这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。
db.properties
driver=com.mysql.jdbc.Driver url=jdbc:mysql://192.168.1.101:3306/mybatis?useSSL=false&characterEncoding=UTF-8 username=root password=root复制代码
核心配置文件引入:
<properties resource="db.properties"> <property name="username" value="root"/> <property name="password" value="123456"/> </properties>复制代码
- 可以直接引入外部文件,
- 可以在其中添加一些属性
- 在外部文件和属性添加了相同的字段时,优先使用外部文件的
4.3、类型别名(typeAliases)
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。
<typeAliases> <typeAlias type="com.luke.pojo.User" alias="user"/> </typeAliases>复制代码
当这样配置时,user可以用在任何使用 com.luke.pojo.User 的地方。
<typeAliases> <package name="com.luke.pojo"/> </typeAliases>复制代码
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。
也可以使用注解添加别名
@Alias("user") public class User { private int id; private String name; private String pwd; }复制代码
4.4、设置(settings)
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。
cacheEnabled:全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。
lazyLoadingEnabled:延迟加载的全局开关。
multipleResultSetsEnabled:是否允许单个语句返回多结果集
useColumnLabel:使用列标签代替列名。实际表现依赖于数据库驱动。
useGeneratedKeys:允许 JDBC 支持自动生成主键,需要数据库驱动支持。
autoMappingBehavior:指定 MyBatis 应如何自动映射列到字段或属性。
NONE 表示关闭自动映射;
PARTIAL 只会自动映射没有定义嵌套结果映射的字段。
FULL 会自动映射任何复杂的结果集(无论是否嵌套)。
autoMappingUnknownColumnBehavior:指定发现自动映射目标未知列(或未知属性类型)的行为。
NONE: 不做任何反应
WARNING: 输出警告日志('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' 的日志等级必须设置为 WARN)
FAILING: 映射失败 (抛出 SqlSessionException)
defaultExecutorType:配置默认的执行器。
SIMPLE 就是普通的执行器;
REUSE 执行器会重用预处理语句(PreparedStatement);
BATCH 执行器不仅重用语句还会执行批量更新。
defaultStatementTimeout:设置超时时间,它决定数据库驱动等待数据库响应的秒数。
defaultFetchSize:为驱动的结果集获取数量(fetchSize)设置一个建议值。此参数只可以在查询设置中被覆盖。
safeRowBoundsEnabled:是否允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false。
mapUnderscoreToCamelCase:是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。
localCacheScope:MyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。
默认值为 SESSION,会缓存一个会话中执行的所有查询。
若设置值为 STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存。
jdbcTypeForNull:当没有为参数指定特定的 JDBC 类型时,空值的默认 JDBC 类型。
lazyLoadTriggerMethods:指定对象的哪些方法触发一次延迟加载。
logImpl:指定 MyBatis 所用日志的具体实现,未指定时将自动查找。
4.5、其他配置
类型处理器(typeHandlers)
MyBatis 在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时, 都会用类型处理器将获取到的值以合适的方式转换成 Java 类型。
对象工厂(objectFactory)
每次 MyBatis 创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成实例化工作。
插件(plugins)
- mybatis-generator-core
- mybatis-plus
- 通用mapper
4.6、映射器(mappers)
就是告诉 MyBatis 到哪里去找到sql语句.
使用相对于类路径的资源引用
使用完全限定资源定位符(URL)
使用映射器接口实现类的完全限定类名
注意点:接口和它的Mapper配置文件必须同名且在同一包下。
将包内的映射器接口实现全部注册为映射器
注意点:接口和它的Mapper配置文件必须同名且在同一包下。
4.7、作用域(Scope)和生命周期
- SqlSessionFactoryBuilder
SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。
- SqlSessionFactory
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
SqlSessionFactory 的最佳作用域是应用作用域。
有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。
- SqlSession
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
用完之后需要被关闭。
5、解决属性名和字段名不一致的问题:ResultMapper
resultMap 元素是 MyBatis 中最重要最强大的元素。
ResultMapper结果映射:
<resultMap id="UserMap" type="user"> <result column="id" property="id"/> <result column="name" property="name"/> <result column="pwd" property="password"/> </resultMap> <select id="getUser" resultMap="UserMap"> select * from user; </select>复制代码
6、日志
6.1、日志工厂
在数据库出现异常的时候,需要打出日志帮助排错。
Mybatis 通过使用内置的日志工厂提供日志功能。内置日志工厂将会把日志工作委托给下面的实现之一:
- SLF4J、
- LOG4J、【常用】
- LOG4J2、
- JDK_LOGGING、
- COMMONS_LOGGING、
- STDOUT_LOGGING、【常用】
- NO_LOGGING
需要使用哪个在设置中设定
<settings> <!--标准的日志工厂实现--> <setting name="logImpl" value="STDOUT_LOGGING"/></settings>复制代码
6.2、LOG4J
Log4j简介:
- Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等
- 我们也可以控制每一条日志的输出格式
- 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程
- 这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码
导入依赖
<!-- https://mvnrepository.com/artifact/log4j/log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>复制代码
配置文件:log4j.properties
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码 log4j.rootLogger=DEBUG,console,file #控制台输出的相关设置 log4j.appender.console = org.apache.log4j.ConsoleAppender log4j.appender.console.Target = System.out log4j.appender.console.Threshold=DEBUG log4j.appender.console.layout = org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=[%c]-%m%n #文件输出的相关设置 log4j.appender.file = org.apache.log4j.RollingFileAppender log4j.appender.file.File=./log/luke.log log4j.appender.file.MaxFileSize=10mb log4j.appender.file.Threshold=DEBUG log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n #日志输出级别 log4j.logger.org.mybatis=DEBUG log4j.logger.java.sql=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.ResultSet=DEBUG log4j.logger.java.sql.PreparedStatement=DEBUG复制代码
配置
<settings> <setting name="logImpl" value="LOG4J"/></settings>复制代码
Java中使用时:
static Logger logger = Logger.getLogger(MyTest.class); @Test public void testLog4j(){ logger.info("info:进入了testLog4j"); logger.debug("debug:进入了testLog4j"); logger.error("error:进入了testLog4j"); }复制代码
7、分页
作用:
- 减少数据的处理量
7.1、使用Limit分页
select * from user limit startIndex,pageSize;复制代码
使用Mybatis实现
接口:
List<User> getUserByLimit(Map<String,Object> map);复制代码
sql:
<select id="getUserByLimit" resultMap="UserMap" parameterType="map"> select * from user limit #{startIndex},#{pageSize} </select>复制代码
测试类:
@Test public void getUserByLimit(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); HashMap<String, Object> map = new HashMap<String, Object>(); map.put("startIndex",1); map.put("pageSize",2); List<User> userList = mapper.getUserByLimit(map); for (User user : userList) { System.out.println(user); } }复制代码
7.2、RowBounds
接口
List<User> getUserByRowBounds();复制代码
sql:
<select id="getUserByRowBounds" resultMap="UserMap" > select * from user; </select>复制代码
测试类
@Test public void getUserByRowBounds(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); RowBounds rowBounds = new RowBounds(2,2); List<User> userList = sqlSession.selectList("com.luke.dao.UserMapper.getUserByRowBounds", null, rowBounds); for (User user : userList) { System.out.println(user); } sqlSession.close(); }复制代码
8、使用注解
8.1、面向接口编程
大家之前都学过面向对象编程,也学习过接口,但在真正的开发中,很多时候我们会选择面向接口编程
根本原因 : 解耦 , 可拓展 , 提高复用 , 分层开发中 , 上层不用管具体的实现 , 大家都遵守共同的标准 , 使得开发变得容易 , 规范性更好
在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,各个对象内部是如何实现自己的,对系统设计人员来讲就不那么重要了;
而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容。面向接口编程就是指按照这种思想来编程。
关于接口的理解
接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离。
接口的本身反映了系统设计人员对系统的抽象理解。
接口应有两类:
第一类是对一个个体的抽象,它可对应为一个抽象体(abstract class);
第二类是对一个个体某一方面的抽象,即形成一个抽象面(interface);
一个体有可能有多个抽象面。抽象体与抽象面是有区别的。
三个面向区别
面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性及方法 .
面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现 .
接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题.更多的体现就是对系统整体的架构
8.2、使用注解开发
本质是由反射机制实现
底层是动态代理
1.注解在接口上实现
@Select("select * from user") List<User> getUser();复制代码
2.在核心配置文件中绑定接口
<mappers> <mapper class="com.luke.dao.UserMapper"/> </mappers>复制代码
3.测试类
@Test public void testGetUser(){ SqlSession sqlSession = null; try { sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> users = mapper.getUser(); for (User user : users) { System.out.println(user.toString()); } } catch (Exception e) { e.printStackTrace(); } finally { if (sqlSession != null) sqlSession.close(); } }复制代码
8.3、CRUD
自动提交事务:
public static SqlSession getSqlSession(){ return sqlSessionFactory.openSession(true); }复制代码
查询:
//根据id查询用户 @Select("select * from user where id = #{id}") User selectUserById(@Param("id") int id);复制代码
新增:
//添加一个用户 @Insert("insert into user (id,name,pwd) values (#{id},#{name},#{pwd})") int addUser(User user);复制代码
修改:
//修改一个用户 @Update("update user set name=#{name},pwd=#{pwd} where id = #{id}") int updateUser(User user);复制代码
删除:
//根据id删除用户 @Delete("delete from user where id = #{id}") int deleteUser(@Param("id")int id);复制代码
@Param注解用于给方法参数起一个名字。以下是总结的使用原则:
在方法只接受一个参数的情况下,可以不使用@Param。
在方法接受多个参数的情况下,建议一定要使用@Param注解给参数命名。
如果参数是 JavaBean , 则不能使用@Param。
不使用@Param注解时,参数只能有一个,并且是Javabean。
9、复杂查询(多对一)
9.1、按照查询嵌套处理
查询语句:
<?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.luke.dao.StudentMapper"> <resultMap id="studentTeacher" type="student"> <result property="id" column="id"/> <result property="name" column="name"/> <association property="teacher" column="tid" javaType="teacher" select="getTeacher"/> </resultMap> <select id="getAllStudent" resultMap="studentTeacher"> select * from student; </select> <select id="getTeacher" resultType="teacher"> select * from teacher where id = #{tid}; </select> </mapper>复制代码
9.2、按照结果嵌套处理
<resultMap id="studentTeacher2" type="student"> <result property="id" column="sid"/> <result property="name" column="sname"/> <association property="teacher" javaType="teacher"> <result property="name" column="tname"/> </association> </resultMap> <select id="getAllStudent2" resultMap="studentTeacher2"> select s.id sid, s.name sname, t.name tname from student s,teacher t where s.tid = t.id; </select>复制代码
10、复杂查询(一对多)
10.1、按照结果嵌套
<?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.luke.dao.TeacherMapper"> <resultMap id="teacherStudent" type="teacher"> <result property="id" column="tid"/> <result property="name" column="tname"/> <collection property="students" ofType="student"> <result property="id" column="sid"/> <result property="name" column="sname"/> <result property="tid" column="tid"/> </collection> </resultMap> <select id="getTeacherById" resultMap="teacherStudent"> select s.id sid, s.name sname, t.name tname, t.id tid from student s,teacher t where s.tid = t.id and tid = #{id}; </select> </mapper>复制代码
10.2、按照查询嵌套
<resultMap id="teacherStudent2" type="teacher"> <result property="id" column="id"/> <collection property="students" javaType="ArrayList" ofType="student" column="id" select="getStudengByTeacherId"/> </resultMap> <select id="getTeacherById2" resultMap="teacherStudent2"> select * from teacher where id = #{tid} </select> <select id="getStudengByTeacherId" resultType="student"> select * from student where tid = #{tid} </select>复制代码
11、动态SQL
动态 SQL 是 MyBatis 的强大特性之一。
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
11.1、if
<select id="queryBlogIf" parameterType="map" resultType="blog"> select * from blog <where> <if test="title != null"> and title = #{title} </if> <if test="author != null"> and author = #{author} </if> </where> </select>复制代码
11.2、choose (when, otherwise)
<select id="queryBlogChoose" parameterType="map" resultType="blog"> select * from blog <where> <choose> <when test="title != null"> title = #{title} </when> <when test="author != null"> and author = #{author} </when> <otherwise> and views = #{views} </otherwise> </choose> </where> </select>复制代码
11.3、set
<!--注意set是用的逗号隔开--> <update id="updateBlog" parameterType="map"> update blog <set> <if test="title != null"> title = #{title}, </if> <if test="author != null"> author = #{author} </if> </set> where id = #{id}; </update>复制代码
11.4、Foreach
<select id="queryBlogForeach" parameterType="map" resultType="blog"> select * from blog <where> <!-- collection:指定输入对象中的集合属性 item:每次遍历生成的对象 open:开始遍历时的拼接字符串 close:结束时拼接的字符串 separator:遍历对象之间需要拼接的字符串 select * from blog where 1=1 and (id=1 or id=2 or id=3) --> <foreach collection="ids" item="id" open="and (" close=")" separator="or"> id=#{id} </foreach> </where> </select>复制代码
11.5、SQL片段
提取:
<sql id="if-title-author"> <if test="title != null"> title = #{title} </if> <if test="author != null"> and author = #{author} </if> </sql>复制代码
引入:
<select id="queryBlogIf" parameterType="map" resultType="blog"> select * from blog <where> <include refid="if-title-author"></include> </where> </select>复制代码
12、缓存
MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存
默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
12.1、一级缓存
一级缓存也叫本地缓存:
与数据库同一次会话期间查询到的数据会放在本地缓存中。
以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;
也就是同一个session中查询同样的查询语句,第二次及之后的将读取缓存中的数据
SqlSession session = MybatisUtils.getSession();复制代码
缓存失效的四种情况:
sqlSession不同
sqlSession相同,查询条件不同
sqlSession相同,两次查询之间执行了增删改操作!
sqlSession相同,手动清除一级缓存
12.2、二级缓存
二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
基于namespace级别的缓存,一个名称空间,对应一个二级缓存;
工作机制
一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
新的会话查询信息,就可以从二级缓存中获取内容;
不同的mapper查出的数据会放在自己对应的缓存(map)中;
开启全局缓存:
<setting name="cacheEnabled" value="true"/>复制代码
配置使用:
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>复制代码
这个更配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的
只要开启了二级缓存,我们在同一个Mapper中的查询,可以在二级缓存中拿到数据。查出的数据都会被默认先放在一级缓存中。只有会话提交或者关闭以后,一级缓存中的数据才会转到二级缓存中。