Mybatis笔记

it2024-10-30  7

1、简介

1. 什么是Mybatis

持久层框架支持定制化SQL、存储过程以及高级映射避免了所有的JDBC代码和手动设置参数以及获取结果集可使用简单的XML或注解来配置和映射原生类型、接口和Java的POJO(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录

2. 持久化

数据持久化

持久化就是将程序的数据在持久状态和瞬时状态转化的过程内存:断电即失数据库(JDBC),IO文件持久化生活:冷藏、罐头。

为什么需要持久化?

有一些对象,不能让他断电即失内存太贵了

3. 持久层

Dao层、Service层,Controller层…

完成持久化工作的代码块层界限十分明显。

4. 为什么需要Mybatis

帮助程序员将数据存入到数据库中方便传统JDBC代码太复杂用的人多!!!

2、Mybatis程序

1. 搭建环境

搭建数据库

新建项目

新建项目

删除src将项目作为父工程

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>MybatisStudy</artifactId> <packaging>pom</packaging> <version>1.0-SNAPSHOT</version> <modules> <module>mybatis-01</module> </modules> <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.2</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies> </project>

2. 创建一个模块

编写mybatis核心配置文件

<?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://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="password"/> </dataSource> </environment> </environments> <!--<mappers> <mapper resource="org/mybatis/example/BlogMapper.xml"/> </mappers>--> </configuration>

编写mybatis工具类

public class MybatisUtils { private static SqlSessionFactory sqlSessionFactory; static { try { //获取mybatis第一步,获取SqlSessionFactory对象 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } public static SqlSession getSqlSession(){ return sqlSessionFactory.openSession(); } }

3. 编写代码

实体类

Dao接口

public interface UserDao { List<User> getUserList(); }

接口实现类由原来的UserDaoImpl转变为一个Mapper文件

namespace绑定接口id绑定方法resultType绑定类 <?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"> <!--namespace=绑定一个对应的Dao/Mapper接口--> <mapper namespace="org.why.dao.UserDao"> <select id="getUserList" resultType="org.why.pojo.User"> select * from mybatis.user </select> </mapper>

4. 测试

注意点:

org.apache.ibatis.binding.BindingException: Type interface org.why.dao.UserMapper is not known to the MapperRegistry.

MapperRegistry是什么?

核心配置中文注册mappers

当配置文件无法被导出或者生效时,使用以下代码

<!--在build中配置resources,来防止我们资源导出失败的问题--> <build> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> </resources> </build>

Junit测试

@Test public void test(){ //获得sqlSession对象 SqlSession sqlSession = MybatisUtils.getSqlSession(); //方式一:执行SQL UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> userList = mapper.getUserList(); for (User user : userList) { System.out.println(user); } sqlSession.close(); }

可能遇到的问题:

配置文件没有注册绑定接口错误方法名不对返回类型不对Maven导出资源问题

3、CRUD

1. namespace

namespace中的包名要和Dao/mapper接口的包名一致!

2. select

选择,查询语句:

id:就是对应的namespace中的方法名resultType:SQL语句执行的返回值parameterType:参数类型

编写接口

//根据ID查询用户 User getUserById(int id);

编写对应的mapper中的sql语句

<select id="getUserById" parameterType="int" resultType="org.why.pojo.User"> select *from mybatis.user where id = #{id} </select>

测试

@Test public void getUserById(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.getUserById(1); System.out.println(user); sqlSession.close(); }

3. insert、update、delete

<insert id="addUser" parameterType="org.why.pojo.User"> insert into mybatis.user (id,name,pwd) values (#{id},#{name},#{pwd}); </insert> <update id="updateUser" parameterType="org.why.pojo.User"> update mybatis.user set name=#{name},pwd=#{pwd} where id=#{id}; </update> <delete id="deleteUser" parameterType="int"> delete from mybatis.user where id=#{id}; </delete>

​ 注意点:增删改需要提交事务(sqlSession.admit)

4. 分析错误

标签不要匹配错resource绑定mapper,需要使用路径程序配置文件必须符合规范NullPointerException,没有注册到资源输出的xml文件中存在中文乱码maven资源没有导出问题

5. 万能Map

假设我们的实体类或者数据库中的表,字段或者参数过多,我们应当考虑使用Map!

//万能的Map int addUser2(Map<String,Object> map); int updateUser2(Map<String,Object> map); <!--传递map的key,在update中可以只更新想要更新的内容,而不需要全部写出来--> <insert id="addUser2" parameterType="map"> insert into mybatis.user (id,name,pwd) values (#{userId},#{userName},#{password}); </insert> <update id="updateUser2" parameterType="map"> update mybatis.user set pwd=#{password} where id=#{userId} </update> @Test public void addUser2(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); Map<String,Object> map = new HashMap<String, Object>(); map.put("userId",3); map.put("userName","why3"); map.put("passWord","333333");//W为大写,所以密码插入不进去 mapper.addUser2(map); sqlSession.commit(); sqlSession.close(); } public void updateUser2(){ SqlSession sqlSession = MybatisU tils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); Map<String,Object> map = new HashMap<String, Object>(); map.put("userId",3); map.put("password","233333"); mapper.updateUser2(map); sqlSession.commit(); sqlSession.close(); }

Map传递参数,直接在sql中取出key即可!【parameterType=“map”】

对象传递参数,直接在sql中取出对象的属性即可!【parameterType=“Object”】

只有个基本类型参数的情况下,可以直接在sql中取到!

多个参数用Map,或者注解

6. 模糊查询

java代码执行的时候,传递通配符% %

List<User> userList = mapper.getUserLike("%w%");

在sql拼接中使用通配符

select * from mybatis.user where name like concat('%',#{value},'%');

4、配置解析

1.核心配置文件

所有元素都必须按顺序编写!!!

properties(属性) settings(设置) typeAliases(类型别名) typeHandlers(类型处理器) objectFactory(对象工厂) plugins(插件) environments(环境配置) environment(环境变量) transactionManager(事务管理器) {JDBC / MANAGMENT(了解即可)} dataSource(数据源) {UNPOOLED / POOLED / JNDI} (池:用完可以回收) databaseIdProvider(数据库厂商标识) mappers(映射器)

2. environments(环境配置)

Mybatis可以配置成适应多种环境

不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。

学会使用配置多套运行环境

Mybatis默认的事务管理器就是JDBC,连接池:POOLED

3. 属性(properties)

可以通过properties属性来实现引用配置文件

这些属性可以在外部进行配置,并可以进行动态替换。既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。【db.properties】

db.properties文件

driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8 username=root pwd=123456

在核心配置文件中引入

<!--引入外部配置文件--> <properties resource="db.properties"> <!--存在相同元素,优先引入外部文件--> <property name="pwd" value="111111"/> </properties> 可以直接引入外部文件可以在其中增加一些配置属性如果两个文件有同一个字段,优先使用外部配置文件的!

4. 类型别名(typeAliases)

类型别名可为 Java 类型设置一个缩写名字。降低冗余的全限定类名书写。 <typeAliases> <typeAlias type="org.why.pojo.User" alias="User"/> </typeAliases>

也可以指定一个包名,Mybatis会在包名下面搜索需要的Java Bean,比如:

扫描实体类的包,它的默认别名就为这个类的类名,首字母小写!

<typeAliases> <package name="org.why.pojo"/> </typeAliases>

在实体类比较少的时候,使用第一种方式

如果实体类十分多,建议使用第二种

第一种可以DIY别名,第二种要改别名,需要在实体上增加注解

@Alias("user") public class user{...}

5.设置(settings)

注意的几个设置

设置名描述有效值默认值cacheEnabled全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。true | falsetruelazyLoadingEnabled延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。true | falsefalselogImpl指定 MyBatis 所用日志的具体实现,未指定时将自动查找。SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING未设置

6. 其他设置(暂时不需要裂解)

类型处理器(typeHandlers)

对象工厂(objectFactory)

插件(plugins)

mybatis-generator-coremybatis-plus通用mapper

7. 映射器(mappers)

MapperRegistry:注册绑定我们的Mapper文件

方式一:

<mappers> <mapper resource="org/why/dao/UserMapper.xml"/> </mappers>

方式二:使用class文件绑定注册

<mappers> <mapper class="org.why.dao.UserMapper"/> </mappers>

注意点:

接口和他的Mapper配置文件必须同名接口和他的Mapper配置文件必须在同一包下

方式三:使用包扫描进行注入绑定

<mappers> <package name="org.why.dao"/> </mappers>

注意点:

接口和他的Mapper配置文件必须同名接口和他的Mapper配置文件必须在同一包下

8. 作用域(Scope)和生命周期

开始 sqlSessionFactoryBuilder <---- mybatis-config.xml sqlSessionFactory sqlSession sql Mapper 结束

作用域,和生命周期,是至关重要的,因为错误的使用会导致非常严重的并发问题。

sqlSessionFactoryBuilder:

一旦创建了sqlSessionFactoryBuilder,就不再需要作为局部变量来创建

sqlSessionFactory:

可以理解为:数据库连接池一旦运行就一直存在,没有任何理由丢弃它或者重新创建最佳作用域是应用作用域最简单就是使用单例模式或者静态单例模式

sqlSession:

可以理解为:连接到连接池的一个请求不是线程安全,不能被共享最佳作用域是请求或方法作用域用完马上关闭,否则资源占用

一个sqlSessionFactory可以创建 多个sqlSession

一个sqlSession可以引用 多个Mapper

每一个Mapper代表一个具体的业务

5. 解决属性名和字段名不一致问题

sql: pwd java: private String password select * from mybatis.user where id = #{id} 类处理器 select id,name,password from mybatis.user where id = #{id}

当实体类字段和数据库中的字段不一致时,会无法查询出该字段数据

解决方法:

起别名

<select id="getUserById" resultType="user"> select id,name,pwd from mybatis.user where id = #{id} </select>

resultMap

结果映射集

id name pwd id name password <!--结果集映射--> <resultMap id="UserMap" type="user"> <!--column数据库中的字段,property实体类中的属性--> <result column="id" property="id"/> <result column="name" property="name"/> <result column="pwd" property="password"/> </resultMap> <select id="getUserById" resultMap="UserMap"> select * from mybatis.user where id = #{id} </select> resultMap元素是Mybatis中最重要最强大的元素resultMap的设计思想:对于简单语句不需要配置显示的结果映射,对于复杂的语句只需要描述它们的关系就行了。

6. 日志

1. 日志工厂

如果一个数据库操作,出现了异常,我们需要排错。日志就是最好的助手!

曾经:sout、debug

现在:日志工厂

设置名描述有效值默认值logImpl指定 MyBatis 所用日志的具体实现,未指定时将自动查找。SLF4J 、 LOG4J 、 LOG4J2 、 JDK_LOGGING 、 COMMONS_LOGGING 、 STDOUT_LOGGING 、 NO_LOGGING未设置 SLF4JLOG4J 【掌握】LOG4J2JDK_LOGGINGCOMMONS_LOGGINGSTDOUT_LOGGING【掌握】NO_LOGGING

在mybatis中具体使用哪一个日志,在设置中配置

STDOUT_LOGGING标准日志输出:

<settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings> Opening JDBC Connection Created connection 832279283. Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@319b92f3] ==> Preparing: select * from mybatis.user where id = ? ==> Parameters: 1(Integer) <== Columns: id, name, pwd <== Row: 1, why1, 123456 <== Total: 1 User{id=1, name='why1', password='123456'} Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@319b92f3] Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@319b92f3] Returned connection 832279283 to pool.

2. LOG4J

可以控制每一条日志的输出格式通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

先导入LOG4J 的包

<!-- 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/why.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

配置log4j为日志实现

<settings> <setting name="logImpl" value="LOG4J "/> </settings>

Log4j的简单使用

导入包import org.apache.log4j.Logger;

日志对象,参数为当前类的class

static Logger logger= Logger.getLogger(UserMapperTest.class);

日志级别

logger.info("info:进入了testLog4j"); logger.debug("debug:进入了testLog4j"); logger.error("error:进入了testLog4j");

7. 分页

为什么要分页?

减少数据处理量

1. 使用Limit分页

SELECT * from user limit startIndex,pageSize; SELECT * from user limit 0,2; SELECT * from user limit 3; #[0,3]

使用Mybatis实现分页

接口

List<User> getUserByLimit(Map<String,Integer> map);

Mapper.xml

<!--分页--> <select id="getUserByLimit" parameterType="map" resultType="user"> select * from mybatis.user limit #{startIndex},#{pageSize} </select>

测试

@Test public void getUserByLimit(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); HashMap<String, Integer> map = new HashMap<>(); map.put("startIndex",0); map.put("pageSize",2); List<User> userList = mapper.getUserByLimit(map); for (User user : userList) { System.out.println(user); } sqlSession.close(); }

2. RowBounds分页

知道即可

3.分页插件

mybatis分页插件PageHelper

了解即可

8. 注解开发

1. 面向接口编程

根本原因:解耦

2.使用注解开发

注解在接口上实现

@Select("select * from user") List<User> getUsers();

需要在核心配置文件中绑定接口

<!--绑定接口--> <mappers> <mapper class="org.why.dao.UserMapper"/> </mappers>

测试

@Test public void test(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); //底层主要应用反射 UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> users = mapper.getUsers(); for (User user : users) { System.out.println(user); } sqlSession.close(); }

本质:反射机制实现

底层:动态代理

mybatis详细的执行流程!

resources获取加载全局配置文件实例化SqlSessionFactoryBuilder构造器解析文件流XML ConfigBuilderConfiguration所有配置信息sqlSessionFactory实例化transactional事务管理创建**executor**执行器创建sqlSession实现CRUD 无法实现回滚到6查看是否执行成功 无法实现回滚到6提交事务关闭

3. CRUD

可以在工具类创建的时候自动提交事务!

public static SqlSession getSqlSession(){ return sqlSessionFactory.openSession(true); }

【注意:我们必须要将接口注册绑定我们的核心配置文件中】

关于@Param

基本类型和String类型的参数,需要加上引用类型不需要如果只有一个基本类型的话,可以忽略,但建议加上在SQL中引用的就是@Param("")中设定的属性名

#{} ${}区别

9. Lombok

使用步骤:

安装Lombok插件

导入lombok包

<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> </dependency>

在实体类上加注解即可

@Getter and @Setter @FieldNameConstants @ToString @EqualsAndHashCode @AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor @Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog @Data @Builder @SuperBuilder @Singular @Delegate @Value @Accessors @Wither @With @SneakyThrows @Data //无参构造,get,set,tostring,hashcode,equals @AllArgsConstructor //有参构造 @NoArgsConstructor //无参构造

10. 多对一处理

多个学生,对应一个老师对于学生而言, 关联 ==》多个学生关联一个老师【多对一】对于老师而言, 集合 ==》一个老师有很多学生【一对多】

测试环境搭建

导入lombok新建实体类Teacher,Student建立Mapper接口建立Mapper.xml文件核心配置文件中绑定注册我们的Mapper接口或者文件【多种方法】测试查询是否成功!

按照查询嵌套处理

<!-- 1.查询所有学生 2.根据tid查询老师 --> <select id="getStudent" resultMap="StudentTeacher"> select * from mybatis.student </select> <resultMap id="StudentTeacher" type="Student"> <result property="id" column="id"/> <result property="name" column="name"/> <!--复杂属性需要单独处理 对象:association 集合:collection--> <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/> </resultMap> <select id="getTeacher" resultType="Teacher"> select * from mybatis.teacher where id = #{id} </select>

按照结果嵌套处理

<!--按照结果嵌套处理--> <select id="getStudent2" resultMap="StudentTeacher2"> select s.id sid,s.name sname,t.name tname from mybatis.student s,mybatis.teacher t where s.tid=t.id; </select> <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>

回顾MySQL多对一处理方式

子查询联表查询

11. 一对多处理

按照结果嵌套处理

<!--按照结果嵌套查询--> <select id="getTeacher" resultMap="TeacherStudent"> select s.id sid,s.name sname,t.name tname,t.id tid from mybatis.teacher t,mybatis.student s where s.tid = t.id and t.id=#{tid} </select> <resultMap id="TeacherStudent" type="Teacher"> <result property="id" column="tid"/> <result property="name" column="tname"/> <!--复杂属性需要单独处理 对象:association 集合:collection javaType="" 指定属性的类型 集合中的泛型信息,使用ofType获取--> <collection property="students" ofType="Student"> <result property="id" column="sid"/> <result property="name" column="sname"/> <result property="tid" column="tid"/> </collection> </resultMap>

小结

关联 - association【多对一】集合 - collection【一对多】javaType & offType javaType:用来指定实体类中属性的类型offType:用来指定映射到List或集合中的POJO类型,泛型中的约束类型

注意点:

保证SQL的可读性,尽量保持通俗易懂注意一对多和多对一中,属性名和字段的问题如果问题不好排查错误,可以使用日志,建议使用Log4j

面试高频

mysql引擎InnoDB底层原理索引索引优化

12. 动态SQL

动态sq就是根据不同的条件生成不同的SQL语句

ifchoose (when, otherwise)trim (where, set)foreach

搭建环境

导包编写配置文件编写实体类编写实体类对应的Mapper接口和Mapper.xml文件

IF

<select id="queryBlogIF" parameterType="map" resultType="blog"> select * from mybatis.blog where 1=1 <if test="title != null"> and title=#{title} </if> <if test="author != null"> and author=#{author} </if> </select>

choose (when, otherwise)

<select id="queryBlogChoose" parameterType="map" resultType="blog"> select * from mybatis.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>

trim (where, set)

select * from mybatis.blog <where> <if test="title != null"> and title=#{title} </if> <if test="author != null"> and author=#{author} </if> </where> update mybatis.blog <set> <if test="title != null"> title = #{title}, </if> <if test="author != null"> author = #{author} </if> </set> where id = #{id}

所谓的动态SQL,本质还是SQL语句,只是我们可以在SQL层面,去执行一个逻辑代码

Foreach

<select id="queryBlogForeach" parameterType="map" resultType="blog"> select * from mybatis.blog <where> <foreach collection="ids" item="id" open="and (" close=")" separator="or"> id = #{id} </foreach> </where> </select>

SQL片段

有时候,我们会将一些功能提取出来,方便复用

使用SQL标签抽取公共部分

<sql id="if-title-author"> <if test="title != null"> and title=#{title} </if> <if test="author != null"> and author=#{author} </if> </sql>

在需要使用的地方用include标签引用即可

select * from mybatis.blog <where> <include refid="if-title-author"></include> </where>

注意事项:

最好基于单表来定义SQL片段不要存在where标签

动态SQL就是在拼接SQL语句,我们只要保证SQL的正确性,按照SQL的格式,去排列组合即可

【建议:先在MySQL中写出完整的SQL语句,再去修改为动态SQL去实现即可】

13. 缓存

查询 --> 连接数据库 --> 耗资源!

​ 一次查询的结果,暂存在一个可以直接取到的地方!–> 内存:缓存

我们再次查询相同数据的时候,直接走缓存,而不用走数据库了

1.简介

什么是缓存(Cache)? 内存中的临时数据经常查询的数据放在缓存中,提高效率,解决了高并发系统的性能问题。 为什么使用 减少和数据库的交互次数,减少系统开销,提高系统效率 什么样的数据能使用缓存? 经常查询并且不经常改变的数据。【可以使用缓存】

2. Mybatis缓存

mybatis默认定制了两级缓存:一级缓存和二级缓存

默认情况下,只有一级缓存开启;(sqlsession级别的缓存,也称本地缓存)二级缓存需要手动开启和配置;(namespace级别的缓存,全局缓存)未来提高扩展性,mybatis定义了缓存接口Cache。我们可以通过实现Cache接口来定义二级缓存

3.一级缓存

一级缓存也叫本地缓存:SqlSession

与数据库同一次回话期间查询到的数据会放在本地缓存中。以后需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库

测试步骤:

开启日志在一个session中查询两次相同记录查看日志输出

缓存失效的情况:

查询不同对象进行增删改操作(可能会改变原来的数据)查询不同的Mapper.xml手动清理缓存

**小结:**一级缓存默认开启,只在一次SqlSession中有效,也就是拿到连接到关闭连接这一区间段

4. 二级缓存

二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存基于namespace级别的缓存,一个namespace对应一个二级缓存工作机制 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中如果当前会话关闭,一级缓存消失;如果二级缓存开启,一级缓存的数据在会话关闭后,保存到二级缓存中新的会话查询信息,可以从二级缓存获取内容不同mapper查出的数据会放在自己对应的缓存(map)中

步骤:

开启全局缓存

<!--显示的开启全局缓存--> <setting name="cacheEnable" value="true"/>

在要使用二级缓存的Mapper中开启

<!--在当前Mapper。xml中使用二级缓存--> <cache/>

也可以自定义参数

<!--在当前Mapper。xml中使用二级缓存--> <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>

测试

问题:我们需要将实体类序列化!否则报错【readOnly="true"不需要序列化】

Caused by: java.io.NotSerializableException: org.why.pojo.User

小结:

只要开启了二级缓存,在同一个Mapper下就有效所有数据都会先放在一级缓存中只有当会话提交,或者关闭的时候,才会提交到二级缓存

5. 缓存原理

6. 自定义缓存-ehcache

ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存

要在程序中使用ehcache,先要导包!

在mapper中指定使用ehcache缓存实现

<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
最新回复(0)