Mybatis Study
Mybatis Study
配置 pom 文件
<dependencies> <!--mysql驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.28</version> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.2</version> </dependency> <!--junit 单元测试依赖--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies>
编写 mybatis 核心配置文件 mybatis-config.xml
<?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"> <!--mybatis核心配置文件--> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <!-- jdbc 链接驱动 由于 com.mysql.jdbc.Driver 提示弃用,所以我们这边导入 com.mysql.cj.jdbc.Driver --> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <!--数据库连接路径--> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8"/> <!--用户名--> <property name="username" value="root"/> <!--密码--> <property name="password" value="Root123."/> </dataSource> </environment> </environments> <!--每一个 mapper.xml 都需要在 mybatis 核心文件中注册--> <mappers> <mapper resource="com/niuma/dao/UserMapper.xml"/> </mappers> </configuration>
编写工具类
public class MybatisUtils { private static SqlSessionFactory sqlSessionFactory; static { try { // 获取 sqlSessionFactory 对象 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } public static SqlSession getSqlSession() { // sqlSession 中包涵了数据库执行 sql 命令的所有方法 return sqlSessionFactory.openSession(); } }
编写一个实体类 User
public class User { private int id; private String name; private String passwd; public User() { } public User(int id, String name, String passwd) { this.id = id; this.name = name; this.passwd = passwd; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPasswd() { return passwd; } public void setPasswd(String passwd) { this.passwd = passwd; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", passwd='" + passwd + '\'' + '}'; } }
编写持久层接口 UserMapper
public interface UserMapper { /** * 放回 User LIst 集合 * @return UserList */ List<User> getUserList(); /** * 根据用户的 id 查询这个用户的信息 * @param id 用户数据库中的 id * @return A User Info */ User getUserById(int id); /** * 向数据库中插入一个用户信息 * @param user 用户的信息 * @return 受影响的行数 */ int addUser(User user); /** * 修改用户 * @param user 用户的信息 * @return 受影响的行数 */ int updateUser(User user); /** * 删除用户 * @param id user is id * @return 影响的行数 */ int deleteUser(int id); }
编写 Mapper.xml 文件 UserMapper.xml
查询所有的 sql
根据这个模板,我们向里面添加即可
namespace: 命名空间,绑定一个对应的 Mapper 接口
select 标签中的 id 是对应接口中的方法
sql 语句写在 select 标签中
<?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绑定一个对应的mapper接口--> <mapper namespace="com.niuma.dao.UserMapper"> <select id="getUserList" resultType="com.niuma.pojo.User"> select * from mybatis.user; </select> </mapper>
使用 id 查询用户
<select id="getUserById" parameterType="int" resultType="com.niuma.pojo.User"> select * from mybatis.user where id = #{id}; </select>
插入数据
在这里可以直接使用 User 类中的属性名
<insert id="addUser" parameterType="com.niuma.pojo.User"> insert into mybatis.user (id, name, passwd) values (#{id}, #{name}, #{passwd}) </insert>
更新数据
<update id="updateUser" parameterType="com.niuma.pojo.User"> update mybatis.user set name=#{name}, passwd=#{passwd} where id=#{id}; </update>
删除一条数据
<delete id="deleteUser" parameterType="int"> delete from mybatis.user where id=#{id}; </delete>
编写单元测试
public class UserMapperTest { /** * 查询所有用户的信息 */ @Test public void test() { // get sqlSession Object SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List<User> userList = userMapper.getUserList(); for (User user : userList) { System.out.println(user); } sqlSession.close(); } /** * 根据用户的id查询用户的信息 */ @Test public void getUserById() { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User userInfo = mapper.getUserById(1); System.out.println(userInfo); sqlSession.close(); } @Test public void addUser() { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); int resultNumber = mapper.addUser(new User(4, "嘿嘿", "password")); if (resultNumber > 0) { System.out.println("数据插入成功"); } sqlSession.commit(); sqlSession.close(); } /** * update user info */ @Test public void updateUser() { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); int resultNumber = mapper.updateUser(new User(4, "嘻嘻", "336699")); sqlSession.commit(); sqlSession.close(); } @Test public void deleteUser() { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); int resultNumber = mapper.deleteUser(4); sqlSession.commit(); sqlSession.close(); } }
资源导出问题
在我们测试的时候会遇到一个问题,那就是资源在构建的时候没有导出,这里的 UserMapper.xml 是放在 java 类中的,所以他不会导出
所以我们需要在 pom 文件中将其配置一下
<build> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> </resource> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> </resource> </resources> </build>
万能的 Map
我们在更新一条数据的时候,其实是可以不用到像上面那样创建 User 类的,现在定义的 User 类才三个元素,但是如果一个 User 类有很多条呢?
我们不可能更新一个密码或什么就都将 User 类中的所有元素写进去,众所周知使用 sql 更改一个数据,我们将 id 和要更改的字段写进去就好了,这里我们就可以用 map 集合来解决这个问题
例如
update mybatis.user set passwd=123456 where id=1;
实现接口
int updateUser2(Map<String, Object> map);
编写 mapper.xml
<update id="updateUser2" parameterType="map"> update mybatis.user set passwd=#{userPasswd} where id=#{id} </update>
然后写个单元测试
@Test public void updateUser2() { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); Map<String, Object> map = new HashMap<String, Object>(); map.put("userPasswd", "passwd"); map.put("id", 1); int i = mapper.updateUser2(map); sqlSession.commit(); sqlSession.close(); }
环境配置
MyBatis 可以配置适应成多种环境
不过要记住,尽管可以配置多个环境,但每个 SqlSessionFactory 实例只会选择一种环境
MyBatis 的事物管理器默认是 JDBC, 链接池是: POOLED
属性 properties
我们可以通过 properties 属性来实现引用配置文件
这些属性都是外部配置且可动态替换的,既可以在典型的 java 属性文件中配置,亦可以通过 properties 元素的子元素来传递。 db.properties
编写一个配置文件
properties
driver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&;useUnicode=true&;characterEncoding=utf8 username=root password=Root123.
在核心配置文件中引入
<properties resource="org/mybatis/example/config.properties"> <property name="username" value="dev_user"/> <property name="password" value="F2Fa3!33TYyg"/> </properties>
<?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"> <!--mybatis核心配置文件--> <configuration> <!--引入外部配置文件--> <properties resource="db.properties"/> <environments default="development"> <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> </environments> <!--每一个 mapper.xml 都需要在 mybatis 核心文件中注册--> <mappers> <mapper resource="com/niuma/dao/UserMapper.xml"/> </mappers> </configuration>
-
可以直接引入外部文件
-
可以在其中增加一些属性配置
-
如果两个文件使用同一个字段,会优先使用外部配置文件。
类型别名 (typeAliases)
-
Java 类型设置一个缩写名字。
-
它仅用于 XML 配置,意在降低冗余的全限定类名书写。
写在 MyBatis 的核心配置文件中 mybatis-config.xml
<typeAliases> <typeAlias alias="User" type="com.niuma.pojo.User"/> </typeAliases>
这样我们就可以在 Mapper.xml 中使用这个别名了
<select id="getUserList" resultType="User"> select * from mybatis.user; </select>
也可以通过包扫描来起别名
- 在没有注解的情况下,会使用的首字母小写的非限定类名来作为它的别名。
<typeAliases> <package name="com.niuma.pojo"/> </typeAliases>
<select id="getUserList" resultType="user"> select * from mybatis.user; </select>
设置(settings)
参考文档 https://mybatis.org/mybatis-3/zh/configuration.html#settings
映射器(mappers)
mapperRegistry: 注册我们的 Mapper 文件 URL: https://mybatis.org/mybatis-3/zh/configuration.html#mappers
方式一:
<mappers> <mapper resource="com/niuma/dao/UserMapper.xml"/> </mappers>
方式二: 使用 Class 文件绑定注册
<mappers> <mapper class="com.niuma.dao.UserMapper"/> </mappers>
注意点:
-
接口和它的 Mapper 配置文件必须同名!
-
接口和它的 Mapper 配置文件必须在同一个包下!
日志
日志工厂
如果一个数据库操作出现了异常,我们需要排错。日志就是最好的助手!
曾经的我们,年少无知,我们使用 sout 和 debug 来进行
现在的我们,长大了,学会使用日志了。
首先我们在 Mybatis 核心文件中添加 Settings
标准的日志输出
<!--addSettings--> <settings> <!--标准的日志输出--> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>
Log4j
编写配置文件 log4j.properties
log4j.rootLogger=DEBUG,Console,Stdout #Console log4j.appender.Console=org.apache.log4j.ConsoleAppender log4j.appender.Console.layout=org.apache.log4j.PatternLayout log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n log4j.logger.java.sql.ResultSet=INFO log4j.logger.org.apache=INFO log4j.logger.java.sql.Connection=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.PreparedStatement=DEBUG log4j.appender.Stdout = org.apache.log4j.DailyRollingFileAppender log4j.appender.Stdout.File = ./log/niuma.log log4j.appender.Stdout.Append = true log4j.appender.Stdout.Threshold = DEBUG log4j.appender.Stdout.layout = org.apache.log4j.PatternLayout log4j.appender.Stdout.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
配置 log4j 的实现
<!-- https://mvnrepository.com/artifact/log4j/log4j --> <dependencies> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> </dependencies>
log4j 的使用
public class UserMapperTest { static Logger logger = Logger.getLogger(UserMapperTest.class); @Test public void Log4jTest() { logger.info("info: 进入了 log4j"); logger.debug("debug: 进入了 log4j"); logger.warn("warn: 进入了 log4j"); logger.error("error: 进入了 log4j"); } }
执行后的效果
2022-04-03 01:01:38,670 [main] INFO [com.niuma.dao.UserMapperTest] - info: 进入了 log4j 2022-04-03 01:01:38,671 [main] DEBUG [com.niuma.dao.UserMapperTest] - debug: 进入了 log4j 2022-04-03 01:01:38,671 [main] WARN [com.niuma.dao.UserMapperTest] - warn: 进入了 log4j 2022-04-03 01:01:38,671 [main] ERROR [com.niuma.dao.UserMapperTest] - error: 进入了 log4j
使用 limit 分页
编写接口
List<User> getUserByLimit(Map<String, Integer> map);
Mapper.xml
<select id="getUserById" parameterType="int" resultType="com.niuma.pojo.User"> select * from mybatis.user where id = #{id}; </select>
test
@Test public void getUserByLimit() { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); HashMap<String, Integer> map = new HashMap<>(); map.put("startIndex", 1); map.put("pageSize", 2); List<User> userList = mapper.getUserByLimit(map); for (User user : userList) { System.out.println(user); } sqlSession.close(); }
console
User{id=1, name='niuma', passwd='passwd'} User{id=2, name='maniu', passwd='654321'}
使用注解开发
1.注解在接口上实现
@Select("select * from user") List<User> getUsers();
2.在核心文件中绑定绑定接口
<!--绑定接口--> <mappers> <mapper class="com.niuma.dao.UserMapper"/> </mappers>
3.测试
@Test public void getUsers() { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> userList = mapper.getUsers(); for (User user : userList) { System.out.println(user); } sqlSession.close(); }
-
本质:使用反射机制
-
底层:使用动态绑定机制
CRUD
使用 id 查询用户
// 如果参数存在多个,那么就在每个参数之前加上 @Param("") 注解 @Select("select * from user where id = #{id}") User getUserById(@Param("id") int id);
在定义工具类的时候我们可以设置自动提交
return sqlSessionFactory.openSession(true);
增加用户
@Insert("insert into user(id, name, passwd) values (#{id},#{name},#{passwd})") int addUser(User user);
修改用户信息
@Update("update user set name=#{name}, passwd=#{passwd} where id = #{id}") int updateUser(User user);
删除用户
@Delete("delete from user where id = #{id}") int deleteUser(@Param("id") int id);
查询 一对多
这里有个子查询,我用的是官方的案例做的,但是有点问题,上面显示解析不了我的查询,然后我又学会了第二个查询的方式,就是 SQL 中的连表查询
使用联表查询
先写一个接口
// 检索所有学生和老师的的信息 List<Student> getStudent();
编写 mapper.xml
<resultMap id="StudentTeacher" type="Student"> <result property="id" column="sid"/> <result property="name" column="sname"/> <association property="teacher" javaType="Teacher"> <result property="id" column="tid"/> <result property="name" column="tname"/> </association> </resultMap> <select id="getStudent" resultMap="StudentTeacher"> select s.id as sid, s.name as sname, t.id as tid, t.name as tname from mybatis.student s join mybatis.teacher t on s.tid = t.id; </select>
这样我们就可以检索出我们想要的信息了
但是还有个问题就是,我按照官网上面那样写,然后我看教学视频上面也是这样,网上很多人也是这样写的,但是写出来就会有问题,目前我还不知道问题出在哪里,这个用的就是和子查询一样的思路。
<resultMap id="student" type="Student"> <association property="teacher" column="tid" javaType="Teacher" select="selectTeacher"/> </resultMap> <select id="getStudent" resultType="Student"> select * from mybatis.student; </select> <select id="selectTeacher" resultType="Teacher"> select * from mybatis.teacher where id = #{id}; </select>
我们使用上面第一个的方法,然后测试一下:
@Test public void test() { SqlSession sqlSession = MybatisUtils.getSqlSession(); StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); List<Student> studentList = mapper.getStudent(); for (Student student : studentList) { System.out.println(student); } sqlSession.close(); }
我这样写出来做测试是没有问题的
一对多处理
实体类
Student
public class Student { private int id; private String name; private int tid; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getTid() { return tid; } public void setTid(int tid) { this.tid = tid; } @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + ", tid=" + tid + '}'; } }
Teacher
public class Teacher { private int id; private String name; private List<Student> students; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public List<Student> getStudents() { return students; } public void setStudents(List<Student> students) { this.students = students; } @Override public String toString() { return "Teacher{" + "id=" + id + ", name='" + name + '\'' + ", students=" + students + '}'; } }
接口
Teacher getTeacher(@Param("tid") int id);
Mapper.xml
<select id="getTeacher" resultMap="TeacherStudent"> select s.id as sid, s.name as sname, t.name as tname, t.id as 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"/> <!-- 复杂的属性我们需要单独处理 集合使用 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>
动态 SQL
什么是动态 sql ? 动态 SQL 就是指跟据不同的条件生成不同的 SQL 语句。
if 语句
在 Mapper.xml 中,在 SQL 语句中添加一些简单的逻辑判断语句,用来完善我们 sql
先编写一个接口:
List<Blog> queryBlogIf(Map<String, Object> map);
Mapper.xml
<select id="queryBlogIf" parameterType="map" resultType="Blog"> select * from mybatis.blog <where> <if test="title != null"> title = #{title} </if> <if test="author != null"> and author = #{author} </if> </where> </select>
我们知道在子查询中会有一个 where 语句,这条 SQL 的作用是,如果用户没有传递参数,就查询所有,如果用户传递了 title 的变量,那么就会添加 title 的模糊查询,如果传入了 author 变量,就会添加作者的模糊查询。
这个和 java 中的 if 语句是一样的,满足条件就会将 sql 添加到后面,在这条语句中,有个 where 标签,这个 where 标签的作用就是当后面的 if 语句生效之后,就会添加 where 这个关键词,如果没生效就不会添加。
在这其中还有一个作用就是,我们在第一条语句中没有加 and 关键字,然后在第二个 if 语句的时候增加了 and 关键字,可能你会说,如果第一个条件没有成立,但是第二个条件成立了,那么这条 SQL 岂不是不规范的,是错误的。
但是在这里,他会有一个机制,就是,如果在有 and 关键字的那条 SQL 满足了条件,在前面的 and 和 or 就会被忽略/去除。
测试
@Test public void queryBlogIf() { SqlSession sqlSession = MybatisUtils.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); HashMap<String, Object> map = new HashMap<>(); //map.put("title", "牛马成长日记"); map.put("author", "牛马"); List<Blog> blogs = mapper.queryBlogIf(map); System.out.println(blogs); sqlSession.close(); }
choose、when、otherwise
choose、when、otherwise 和 java 中的 Switch 语句是一样的。
when 是选择,otherwise 是始终都会执行的
如果在 when 标签中满足了条件,就会添加这个这个标签中的 SQL 语句
例如下面,先写个接口
List<Blog> queryBlogChoose(Map<String, Object> map);
现在来写 Mapper.xml 的代码
<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>
这个代码的意思就是,如果传来了 title,那么就会安装 title 来进行模糊查询,如果没有 传来 title 而是传来了 author ,就 按照 author 来进行模糊查询。
后面的 otherwise 就是不管前面是否传入数据,但是都得传入 id 来满足这个 SQL 的正常执行,也就是这一条 SQL (and views = #{views}) 都是要被拼接到里面的。所以我们要传入 id 来保证这 SQL 正常执行。
Set 更新数据
我们在更新数据的时候可以使用 Set 标签来完成我们的需求
int updateBlog(Map<String, Object> map);
在更新数据的时候,我们会使用到 Set 这个关键字,现在我们来写动态 SQL 的时候,也可以用到 Set 的这个标签
这个标签的作用就是,他会自动在 sql 语句前面为我们添加 Set 关键字,然后也会为我们自动去除后面多余的逗号
下面的代码一看便知其意思,就不多作赘述了。
<update id="updateBlog" parameterType="map"> update mybatis.blog <set> <if test="title != null"> title = #{title}, </if> <if test="author != null"> author = #{author} </if> </set> where id = #{id} </update>
SQL 片段
我们可以将我们重复的 SQL 语句将它提取出来进行复用。例如下面的例子:
<select id="queryBlogIf" parameterType="map" resultType="Blog"> select * from mybatis.blog <where> <include refid="blogIf"/> </where> </select> <sql id="blogIf"> <if test="title != null"> title = #{title} </if> <if test="author != null"> and author = #{author} </if> </sql>
现在就是将原本在 where 中的 SQL 片段用 sql 标签提取出来,然后用 include 标签进行引用
forEach
我们在查询的时候,有时候会遇到要查询某些范围内的事物,我们这里就可以用到 forEach 来做
接口
List<Blog> getBlogForEach(Map<String, ArrayList<Integer>> map);
-
collection: 要遍历的集合
-
item: 取出来的元素
-
open: 以什么开头
-
close: 以什么结尾
-
separator: 以什么分割
如果传来的集合是: (1, 2, 3, 4)
那个这个 SQL 就是:
select * from mybatis.blog where (id=1,id=2,id=3,id=4);
<select id="getBlogForEach" parameterType="map" resultType="Blog"> select * from mybatis.blog <where> <foreach collection="ids" item="id" open="(" close=")" separator="or"> id = #{id} </foreach> </where> </select>
测试:
@Test public void getBlogForEach() { SqlSession sqlSession = MybatisUtils.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); HashMap<String, ArrayList<Integer>> map = new HashMap<>(); ArrayList<Integer> ids = new ArrayList<>(); ids.add(1); ids.add(2); ids.add(3); ids.add(4); map.put("ids", ids); List<Blog> blogs = mapper.getBlogForEach(map); for (Blog blog : blogs) { System.out.println(blog); } sqlSession.close(); }
动态 SQL 就是在拼接 SQL 语句,我们只要保证 SQL 的正确性,安装 SQL 的格式,去排列组合就可以了。
缓存
1.什么是缓存?
-
存在内存中的临时数据
-
将用户经常访问的数据存在缓存中,用户去查询数据就不用去磁盘上(关系型数据库文件)查询,从缓存中查询,从而提高查询效率,解决了高并发性能的问题。
2.为什么要使用缓存?
- 减少数据库的交互次数,减少系统开销,提高系统效率。
3.什么样的数据能使用缓存?
- 经常查询且经常不改变的数据。
mybatis 缓存
-
MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便的定制和配置缓存。缓存可以极大地提升查询效率。
-
MyBatis 系统默认定义了两级缓存--一级缓存和二级缓存
-
默认情况下,只有一级缓存开启。(sqlSession 级别缓存,也就是本地缓存)
-
二级缓存需要手动开启和配置,他是基于 namespace 级别的缓存。
-
为了提高拓展性,MyBatis 定义了缓存接口 Cache。我们可以通过 Cache 来定义二级缓存。
一级缓存
-
与数据库用一次会话期间查询到的数据会保存到本地。
-
如果需要获取相同的数据,直接从缓存中拿,没必要再去查数据库。
二级缓存
二级缓存也叫全局缓存,因为一级缓存的作用域太低了,所以诞生了二级缓存。
-
基于 namespace 级别的缓存,一个名称空间对应一个二级缓存。
-
工作机制
-
一个会话查询一条数据,这个数据就会被放在当前的一级缓存中。
-
如果会话关闭了,这个会话对应的一级缓存就消失了;但我们想要的是,会话关闭了,一级缓存中的数据保存到二级缓存中。
-
新的会话查询信息,就可以从二级缓存中获取。
-
不同的 Mapper 查出来的信息会放在自己对应的缓存中。
-
开启全局缓存
在核心配置文件中的 Settings 标签中添加
<!--显示的开启全局缓存--> <setting name="cacheEnabled" value="true"/>
如果你不需要别的配置,那么就可以在 Mapper.xml 中人添加一个标签即可
<!--在当前 Mapper.xml 中开启二级缓存--> <cache/>
除此之外,我们还可以为其添加一些参数
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。
我们开启缓存之后,会遇到问题,例如没有序列化的问题 : caused by: java.io.NotSerializableException
所以我们需要进行序列化,让实体类继承 Serializable 这个类
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix