Mybatis从浅入深-Plus(IDEA版通俗易懂)
每一个成功者都有一个开始。勇于开始,才能找到成功的路。你好,我是梦阳辰!期待与你相遇! Mybatis从浅入深(IDEA版通俗易懂) 01.使用注解开发 引入:大家之前都学过面向对象编程,也学习过接口,但在真正的开发中,很多时候我们会选择面向接口编程 根本原因∶解耦,可拓展,提高复用,分层开发中,上层不用管具体的实现,大家都遵守共同的标准,使得开发变得容易,规范性更好 在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,各个对象内部是如何实现自己的,对系统设计人员来讲就不那么重要了; 而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容。面向接口编程就是指按照这种思想来编程。关于接口的理解 接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离。-接口的本身反映了系统设计人员对系统的抽象理解。 接口应有两类: 第一类是对一个个体的抽象,它可对应为一个抽象体(abstract class); 第二类是对一个个体某一方面的抽象,即形成一个抽象面(interface) ; 一个体有可能有多个抽象面。抽象体与抽象面是有区别的。 三个面向的区别: 面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性及方法. 面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现. 接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题.更多的体现就是对系统整体的架构 使用注解开发:它们映射的语句可以不用 XML 来配置,而可以使用 Java 注解来配置。比如,上面的 XML 示例可以被替换成如下的配置: packageorg.mybatis.example;publicinterfaceBlogMapper{ @Select("SELECT*FROMblogWHEREid=#{id}") BlogselectBlog(intid);} 使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪。 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。 选择何种方式来配置映射,以及认为是否应该要统一映射语句定义的形式,完全取决于你和你的团队。 换句话说,永远不要拘泥于一种方式,你可以很轻松的在基于注解和 XML 的语句映射方式间自由移植和切换。 1.注解在接口上实现 ** *接口操作user表*/publicinterfaceUserMapper{ @Select("select*fromuser") List<User>selectUser();} 2.需要在核心配置文件中绑定接口。 <!--绑定接口--> <mappers> <mapperclass="com.mengyangchen.dao.UserMapper"/> </mappers> 测试: @Test publicvoidtest(){ //1.获取SqlSession对象 SqlSessionsqlSession=MybatisUtils.getSqlSession(); //方式一:getMapper //2.执行sql,获取sql,面向接口编程,获取UserMapper接口(以前是实现接口的实现类,现在是配置文件) UserMappermapper=sqlSession.getMapper(UserMapper.class); List<User>userList=mapper.selectUser(); /*StringsqlId="com.mengyangchen.dao.UserDao"+"."+"selectUser"; List<User>userList=sqlSession.selectList(sqlId);*/ for(Useruser:userList){ System.out.println(user); } //3.关闭SqlSession sqlSession.close(); } 本质:反射机制实现 底层:动态代理。 Mybatis详细的执行流程 多debug,多看底层源码。 注解完成增删改查 接口: //方法存在多个参数,所有的参数前面必须加上@Param("id")注解,引用对象不需要写 @Select("select*fromuserwhereid=#{id}") UserselectUserById(@Param("id")intid); @Insert("insertintouser(id,name,pwd)values(#{id},#{name},#{password})") intinsertUser(Useruser); 测试: @Test publicvoidselectUserById(){ //1.获取SqlSession对象 SqlSessionsqlSession=MybatisUtils.getSqlSession(); //方式一:getMapper //2.执行sql,获取sql,面向接口编程,获取UserMapper接口(以前是实现接口的实现类,现在是配置文件) UserMappermapper=sqlSession.getMapper(UserMapper.class); Useruser=mapper.selectUserById(1); System.out.println(user); sqlSession.close(); } @Test publicvoidinsertUser(){ //1.获取SqlSession对象 SqlSessionsqlSession=MybatisUtils.getSqlSession(); //方式一:getMapper //2.执行sql,获取sql,面向接口编程,获取UserMapper接口(以前是实现接口的实现类,现在是配置文件) UserMappermapper=sqlSession.getMapper(UserMapper.class); intnum=mapper.insertUser(newUser(3,"meng","123456")); if(num>0){ System.out.println("插入成功!"); } sqlSession.commit(); sqlSession.close(); } #{},${}的区别#{}可以防止sql注入。 02.Lombok Lombok项目是一个Java库,它会自动插入您的编辑器和构建工具中,从而使您的Java更加生动有趣。永远不要再写另一个getter或equals方法,带有一个注释的您的类有一个功能全面的生成器,自动化您的日志记录变量等等。 Lombok是一款Java开发插件,使得Java开发者可以通过其定义的一些注解来消除业务工程中冗长和繁琐的代码,尤其对于简单的Java模型对象(POJO)。在开发环境中使用Lombok插件后,Java开发人员可以节省出重复构建,诸如hashCode和equals这样的方法以及各种业务对象模型的accessor和ToString等方法的大量时间。对于这些方法,它能够在编译源代码期间自动帮我们生成这些方法,并没有如反射那样降低程序的性能。 使用步骤:1.在idea中安装Lombok插件 2.在项目中导入Lombokjar包 <!--https://mvnrepository.com/artifact/org.projectlombok/lombok--><dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> <scope>provided</scope></dependency> Lombok含有的注解: @Getterand@Setter@FieldNameConstants@ToString@EqualsAndHashCode@AllArgsConstructor,@RequiredArgsConstructorand@NoArgsConstructor@Log,@Log4j,@Log4j2,@Slf4j,@XSlf4j,@CommonsLog,@JBossLog,@Flogger,@CustomLog@Data@Builder@SuperBuilder@Singular@Delegate@Value@Accessors@Wither@With@SneakyThrows@val@var 结果: importlombok.Data;@DatapublicclassUser{ privateIntegerid; privateStringname; privateStringpassword; } 自动帮我们写了getter或equals等方法。 @Data:无参构造,get,set,toString,hashcode,equals 无参和所有参数:注意:了解即可。 03.多对一处理 多个学生,对应一个老师。对于学生而言:关联…多个学生关联一个老师(多对一) 对于老师而言:集合 一个老师,有很多学生(一对多) 多对一:实体类 publicclassStudent{ privateIntegerid; privateStringname; //学生需要关联一个老师 privateTeacherteacher; publicIntegergetId(){ returnid; } publicvoidsetId(Integerid){ this.id=id; } publicStringgetName(){ returnname; } publicvoidsetName(Stringname){ this.name=name; } publicTeachergetTeacher(){ returnteacher; } publicvoidsetTeacher(Teacherteacher){ this.teacher=teacher; } @Override publicStringtoString(){ return"Student{"+ "id="+id+ ",name='"+name+'\''+ ",teacher="+teacher+ '}'; }} publicclassTeacher{ privateIntegerid; privateStringname; publicIntegergetId(){ returnid; } publicvoidsetId(Integerid){ this.id=id; } publicStringgetName(){ returnname; } publicvoidsetName(Stringname){ this.name=name; } @Override publicStringtoString(){ return"Teacher{"+ "id="+id+ ",name='"+name+'\''+ '}'; }} 环境搭建:1.新建实体类Teacher,Student2.建立Mapper接口3.建立Mapper.xml文件4.在核心配置文件中绑定注册我们的mapper接口或者文件。5.测试是否成功。 publicinterfaceTeacherMapper{ @Select("select*fromteacherwhereid=#{tid}") TeacherselectTeacherById(@Param("tid")intid);} <mappers> <mapperclass="com.mengyangchen.dao.TeacherMapper"/> <mapperclass="com.mengyangchen.dao.StudentMapper"/></mappers> publicclassMyTest{ @Test publicvoidtest(){ SqlSessionsqlSession=MybatisUtils.getSqlSession(); TeacherMappermapper=sqlSession.getMapper(TeacherMapper.class); Teacherteacher=mapper.selectTeacherById(1); System.out.println(teacher); sqlSession.close(); }} 嵌套查询(按照查询嵌套处理)(子查询):select语句的嵌套。这里测试的时候出现异常:org.apache.ibatis.binding.bindingexception: invalid bound statement (not found) 问题出在:我在创建包的时候采用的是: com.mengyangchen.dao 改为一个一个的创建即可。 <?xmlversion="1.0"encoding="UTF-8"?><!DOCTYPEmapper PUBLIC"-//mybatis.org//DTDMapper3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!--绑定一个对应的Dao/Mapper接口--><!--以前是写一个类实现Dao接口,现在绑定即可--><mappernamespace="com.mengyangchen.dao.StudentMapper"><!--sql语句嵌套思路:1.查询所有的学生信息2.根据查询出来的学生的tid,寻找对应的老师--> <resultMapid="studentTeacher"type="student"> <resultproperty="id"column="id"/> <resultproperty="name"column="name"/> <!--复杂的属性,我们需要单独处理对象association集合collection--> <associationproperty="teacher"column="tid"javaType="Teacher"select="selectTeacherById"/> </resultMap><selectid="selectStudentInfo"resultMap="studentTeacher"> select*fromstudent</select> <selectid="selectTeacherById"resultType="teacher"> select*fromteacherwhereid=#{id} </select></mapper> 结果: 联表查询(按照结果嵌套处理): <?xmlversion="1.0"encoding="UTF-8"?><!DOCTYPEmapper PUBLIC"-//mybatis.org//DTDMapper3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!--绑定一个对应的Dao/Mapper接口--><!--以前是写一个类实现Dao接口,现在绑定即可--><mappernamespace="com.mengyangchen.dao.StudentMapper"><!--sql语句嵌套思路:1.查询所有的学生信息2.根据查询出来的学生的tid,寻找对应的老师--> <resultMapid="studentTeacher"type="student"> <resultproperty="id"column="id"/> <resultproperty="name"column="name"/> <!--复杂的属性,我们需要单独处理对象association集合collection--> <associationproperty="teacher"column="tid"javaType="Teacher"> <resultproperty="name"column="teacherName"/> </association> </resultMap><selectid="selectStudentInfo"resultMap="studentTeacher"> selects.idid,s.namename,t.nameteacherNamefromstudentsinnerjointeachertons.tid=t.id</select></mapper> 测试: @Test publicvoidstudentTest(){ SqlSessionsqlSession=MybatisUtils.getSqlSession(); StudentMappermapper=sqlSession.getMapper(StudentMapper.class); List<Student>list=mapper.selectStudentInfo(); for(Studentstudent:list){ System.out.println(student); } sqlSession.close(); } 04.一对多处理 一个老师对应多个学生对于老师而言就是一对多。实体类: publicclassTeacher{ privateIntegerid; privateStringname; //一个老师有多个学生 privateList<Student>student; publicIntegergetId(){ returnid; } publicvoidsetId(Integerid){ this.id=id; } publicStringgetName(){ returnname; } publicvoidsetName(Stringname){ this.name=name; } publicList<Student>getStudent(){ returnstudent; } publicvoidsetStudent(List<Student>student){ this.student=student; } @Override publicStringtoString(){ return"Teacher{"+ "id="+id+ ",name='"+name+'\''+ ",student="+student+ '}'; }} publicclassStudent{ privateIntegerid; privateStringname; privateIntegertid; publicIntegergetId(){ returnid; } publicvoidsetId(Integerid){ this.id=id; } publicStringgetName(){ returnname; } publicvoidsetName(Stringname){ this.name=name; } publicIntegergetTid(){ returntid; } publicvoidsetTid(Integertid){ this.tid=tid; } @Override publicStringtoString(){ return"Student{"+ "id="+id+ ",name='"+name+'\''+ ",tid="+tid+ '}'; }} 接口: publicinterfaceTeacherMapper{ TeacherselectTeacherById(@Param("tid")intid);} 配置文件: <?xmlversion="1.0"encoding="UTF-8"?><!DOCTYPEmapper PUBLIC"-//mybatis.org//DTDMapper3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!--绑定一个对应的Dao/Mapper接口--><!--以前是写一个类实现Dao接口,现在绑定即可--><mappernamespace="com.mengyangchen.dao.TeacherMapper"> <!--查询语句--> <!--id相对于以前的书写,表示重写的方法名(以前在方法中执行连接数据库等操作,现在不需要了,只需要执行sql--> <!--resultType返回结果--> <!--按照结果嵌套查询--><selectid="selectTeacherById"resultMap="teacherStudent"> selects.idid,s.namename,t.nameteacherNamefromstudentsinnerjointeachertons.tid=t.id</select> <resultMapid="teacherStudent"type="teacher"> <resultproperty="name"column="teacherName"/> <collectionproperty="student"ofType="Student"> <resultproperty="id"column="id"/> <resultproperty="name"column="name"/> </collection> </resultMap></mapper> 05.动态sql 动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。 如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。 ifchoose(when,otherwise)trim(where,set)foreach 使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分。比如: <selectid="findActiveBlogWithTitleLike" resultType="Blog"> SELECT*FROMBLOG WHEREstate=‘ACTIVE’<iftest="title!=null"> ANDtitlelike#{title} </if></select> 动态sql之if 实现根据不同条件查询数据! 实体类: packagecom.mengyangchen.pojo;importjava.util.Date;publicclassBlog{ privateStringid; privateStringtitle; privateStringauthor; privateDatecreateTime;//属性名和字段名不一致 privateIntegerviews; publicBlog(){ } publicStringgetId(){ returnid; } publicvoidsetId(Stringid){ this.id=id; } publicStringgetTitle(){ returntitle; } publicvoidsetTitle(Stringtitle){ this.title=title; } publicStringgetAuthor(){ returnauthor; } publicvoidsetAuthor(Stringauthor){ this.author=author; } publicDategetCreateTime(){ returncreateTime; } publicvoidsetCreateTime(DatecreateTime){ this.createTime=createTime; } publicIntegergetViews(){ returnviews; } publicvoidsetViews(Integerviews){ this.views=views; } @Override publicStringtoString(){ return"Blog{"+ "id="+id+ ",title='"+title+'\''+ ",author='"+author+'\''+ ",createTime="+createTime+ ",views="+views+ '}'; }} 接口: packagecom.mengyangchen.dao;importcom.mengyangchen.pojo.Blog;importjava.util.List;importjava.util.Map;publicinterfaceBlogMapper{ /*插入数据*/ intinsertBlog(Blogblog); /*查询博客*/ List<Blog>queryBlogIf(Mapmap);} 配置文件: <?xmlversion="1.0"encoding="UTF-8"?><!DOCTYPEmapper PUBLIC"-//mybatis.org//DTDMapper3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mappernamespace="com.mengyangchen.dao.BlogMapper"> <insertid="insertBlog"parameterType="blog"> insertintomybatis.blog(id,title,author,create_time,views)values(#{id},#{title},#{author},#{createTime},#{views}); </insert> <selectid="queryBlogIf"parameterType="map"resultType="blog"> select*frommybatis.blogwhere1=1 <iftest="title!=null"> andtitlelike"%"#{title}"%" </if> <iftest="author!=null"> andauthorlike"%"#{author}"%" </if> </select></mapper> 属性名和字段名不一致,一个是下划线,一个为驼峰。 <?xmlversion="1.0"encoding="UTF-8"?><!DOCTYPEconfiguration PUBLIC"-//mybatis.org//DTDConfig3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"><!--核心配置文件--><configuration><!--引入外部配置文件--> <propertiesresource="db.properties"> <propertyname="username"value="root"/><!--一配置文件中为准--> <propertyname="password"value="123456"/> </properties> <!--标准日志实现--> <settings> <settingname="logImpl"value="STDOUT_LOGGING"/> <!--是否开启自动驼峰命名规则映射--> <settingname="mapUnderscoreToCamelCase"value="true"/> </settings> <!--别名--> <typeAliases> <packagename="com.mengyangchen.pojo"/> </typeAliases> <environmentsdefault="development"> <environmentid="development"> <!--事务管理--> <transactionManagertype="JDBC"/> <dataSourcetype="POOLED"> <propertyname="driver"value="${driver}"/><!--&是xml的转义字符(即为&)--> <propertyname="url"value="${url}"/> <propertyname="username"value="${username}"/> <propertyname="password"value="${password}"/> </dataSource> </environment> </environments><mappers> <mapperclass="com.mengyangchen.dao.BlogMapper"/></mappers></configuration> 动态sql之trim (where, set) 前面几个例子已经方便地解决了一个臭名昭著的动态 SQL 问题。现在回到之前的 “if” 示例,这次我们将 “state = ‘ACTIVE’” 设置成动态条件,看看会发生什么。 <selectid="findActiveBlogLike" resultType="Blog"> SELECT*FROMBLOG WHERE<iftest="state!=null"> state=#{state} </if> <iftest="title!=null"> ANDtitlelike#{title} </if> <iftest="author!=nullandauthor.name!=null"> ANDauthor_namelike#{author.name} </if></select> 如果没有匹配的条件会怎么样?最终这条 SQL 会变成这样: SELECT*FROMBLOG WHERE 这会导致查询失败。如果匹配的只是第二个条件又会怎样?这条 SQL 会是这样: SELECT*FROMBLOG WHERE ANDtitlelike‘someTitle’ 这个查询也会失败。这个问题不能简单地用条件元素来解决。这个问题是如此的难以解决,以至于解决过的人不会再想碰到这种问题。 MyBatis 有一个简单且适合大多数场景的解决办法。而在其他场景中,可以对其进行自定义以符合需求。而这,只需要一处简单的改动: <selectid="findActiveBlogLike" resultType="Blog"> SELECT*FROMBLOG<where> <iftest="state!=null"> state=#{state} </if> <iftest="title!=null"> ANDtitlelike#{title} </if> <iftest="author!=nullandauthor.name!=null"> ANDauthor_namelike#{author.name} </if> </where></select> where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。上个例子改完后: <?xmlversion="1.0"encoding="UTF-8"?><!DOCTYPEmapper PUBLIC"-//mybatis.org//DTDMapper3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mappernamespace="com.mengyangchen.dao.BlogMapper"> <insertid="insertBlog"parameterType="blog"> insertintomybatis.blog(id,title,author,create_time,views)values(#{id},#{title},#{author},#{createTime},#{views}); </insert> <selectid="queryBlogIf"parameterType="map"resultType="blog"> select*frommybatis.blog<where> <iftest="title!=null"> titlelike"%"#{title}"%" </if> <iftest="author!=null"> andauthorlike"%"#{author}"%" </if> </where> </select> </mapper> 如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为: <trimprefix="WHERE"prefixOverrides="AND|OR"> ...</trim> prefixOverrides 属性会忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容。 用于动态更新语句的类似解决方案叫做 set。set 元素可以用于动态包含需要更新的列,忽略其它不更新的列。比如: 解决了最后一个条件不需要,的问题。 <updateid="updateAuthorIfNecessary"> updateAuthor<set> <iftest="username!=null">username=#{username},</if> <iftest="password!=null">password=#{password},</if> <iftest="email!=null">email=#{email},</if> <iftest="bio!=null">bio=#{bio}</if> </set> whereid=#{id}</update> 这个例子中,set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。 来看看与 set 元素等价的自定义 trim 元素吧: <trimprefix="SET"suffixOverrides=","> ...</trim> 动态sql之choose (when, otherwise) 有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。还是上面的例子,但是策略变为:传入了 “title” 就按 “title” 查找,传入了 “author” 就按 “author” 查找的情形。若两者都没有传入,就返回标记为 featured 的 BLOG <selectid="findActiveBlogLike" resultType="Blog"> SELECT*FROMBLOGWHEREstate=‘ACTIVE’<choose> <whentest="title!=null"> ANDtitlelike#{title} </when> <whentest="author!=nullandauthor.name!=null"> ANDauthor_namelike#{author.name} </when> <otherwise> ANDfeatured=1 </otherwise> </choose></select> 与if的区别只会执行一个条件。练习: <selectid="queryBlogChoose"parameterType="map"resultType="blog"> select*fromblog<where> <choose> <whentest="title!=null"> title=#{title} </when> <whentest="author!=null"> andauthor=#{author} </when> <otherwise> andviews=#{views} </otherwise> </choose> </where> </select> @Test publicvoidqueryBlogChoose(){ SqlSessionsqlSession=MybatisUtils.getSqlSession(); BlogMappermapper=sqlSession.getMapper(BlogMapper.class); Mapmap=newHashMap(); map.put("title","Mybatis你好!"); map.put("author","梦阳辰"); List<Blog>list=mapper.queryBlogChoose(map); for(Blogblog:list){ System.out.println(blog); } } 动态sql之foreach 06.sql片段 将一些通用的sql片段提取出来。实现sql代码的复用。 07.缓存 1.什么是缓存?存在内存中的临时数据。将用户经常查询的数据放在缓存((内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。 2.为什么使用缓存?减少和数据库的交互次数,减少系统开销,提高系统效率。 3.什么样的数据能使用缓存?经常查询并且不经常改变的数据。Mybatis缓存 MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。 MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)。 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存 一级缓存 MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。 为了使它更加强大而且易于配置,我们对 MyBatis 3 中的缓存实现进行了许多改进。默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 一级缓存也叫本地缓存:SqlSession 与数据库同一次会话期间查询到的数据会放在本地缓存中。以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库; 测试步骤:1.开启日志! 2.测试在一个Sesion中查询两次相同记录 3.查看日志输出 映射语句文件中的所有 select 语句的结果将会被缓存。映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。缓存不会定时进行刷新(也就是说,没有刷新间隔)。缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。 缓存失效的情况:1.查询不同的内容 2.增删改操作,可能会改变原来的数据,所以会刷新缓存。 3.查询不同的Mapper.xml 4.手动清除缓存。 sqlSession.clearCache();//手动清理缓存 一级缓存存在于sqlsession获得到关闭,默认开启(有效期在获得连接到关闭连接)。 二级缓存 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存· 基于namespace级别的缓存,一个名称空间,对应一个二级缓存; 工作机制一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中; 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中; 新的会话查询信息,就可以从二级缓存中获取内容; 不同的mapper查出的数据会放在自己对应的缓存(map)中; 默认情况下,只启用了本地的会话缓存(一级缓存),它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行: <cache/> 缓存只作用于 cache 标签所在的映射文件中的语句。如果你混合使用 Java API 和 XML 映射文件,在共用接口中的语句将不会被默认缓存。你需要使用 @CacheNamespaceRef 注解指定缓存作用域。 这些属性可以通过 cache 元素的属性来修改。比如: <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/> 这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。 可用的清除策略有: LRU – 最近最少使用:移除最长时间不被使用的对象。FIFO – 先进先出:按对象进入缓存的顺序来移除它们。SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。默认的清除策略是 LRU。 步骤:1.开启全局缓存(默认开启)。2.在要使用二级缓存的Mapper中开启3.问题:我们需要将实体类序列化!否则会报错。 implementsSerializable 只要开启了二级缓存,在同一个Mapper下就有效。 所有的数据都会先放在一级缓存中。 只有当会话提交,或者关闭的时候,才会提交到二级缓存中! 缓存的原理 自定义缓存 EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider。1.导包 第三方缓存<!--https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache--><dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-ehcache</artifactId> <version>1.1.0</version></dependency> 除了上述自定义缓存的方式,你也可以通过实现你自己的缓存,或为其他第三方缓存方案创建适配器,来完全覆盖缓存行为。 <cachetype="com.domain.something.MyCustomCache"/> 这个示例展示了如何使用一个自定义的缓存实现。type 属性指定的类必须实现 org.apache.ibatis.cache.Cache 接口,且提供一个接受 String 参数作为 id 的构造器。 这个接口是 MyBatis 框架中许多复杂的接口之一,但是行为却非常简单。 publicinterfaceCache{ StringgetId(); intgetSize(); voidputObject(Objectkey,Objectvalue); ObjectgetObject(Objectkey); booleanhasKey(Objectkey); ObjectremoveObject(Objectkey); voidclear();} 放假回家已经好几天啦,感觉要学习的东西还很多。加油,下一篇spring见!