springboot第16集:一对多,多对一,SQL缓存

您所在的位置:网站首页 mybatis中的一级缓存 springboot第16集:一对多,多对一,SQL缓存

springboot第16集:一对多,多对一,SQL缓存

2023-05-07 09:36| 来源: 网络整理| 查看: 265

create table teacher ( id int(10) not null, name varchar(30) default null, primary key (id) ) engine = innodb default charset = utf8 insert into teacher (id, name) values (1, 'jeskson'); create table student ( id int(10) not null, name varchar(30) default null, tid int(10) default null, primary key (id) key `fktid` (`tid`), constraint `fktid` foreign key (`tid`) references `teacher` (`id`) ) engine = innodb default charset = utf8 复制代码 org.projectlombok lombok 1.16.10 复制代码 @Data //GET,SET,ToString,有参,无参构造 public class Teacher { private int id; private String name; } 复制代码 @Data public class Student { private int id; private String name; //多个学生可以是同一个老师,即多对一 private Teacher teacher; } 复制代码

Mapper接口

public interface StudentMapper { } 复制代码 public interface TeacherMapper { } 复制代码 复制代码 复制代码 //获取所有学生及对应老师的信息 public List getStudents(); 复制代码 select * from student select * from teacher where id = #{id} 复制代码 select * from teacher where id = #{id} and name = #{name} 复制代码 public List getStudents2(); select s.id sid, s.name sname , t.name tname from student s,teacher t where s.tid = t.id 复制代码 @Test public void testGetStudents2(){ SqlSession session = MybatisUtils.getSession(); StudentMapper mapper = session.getMapper(StudentMapper.class); List students = mapper.getStudents2(); for (Student student : students){ System.out.println( "学生名:"+ student.getName() +"\t老师:"+student.getTeacher().getName()); } } 复制代码

按照查询进行嵌套处理,就像 SQL 中的子查询。MyBatis 提供了 标签中的 和 标签来实现嵌套查询。其中, 标签用于处理集合类型的属性,而 标签用于处理单个对象类型的属性。例如:

SELECT * FROM User WHERE id = #{userId} SELECT * FROM Order WHERE user_id = #{userId} 复制代码

按照结果进行嵌套处理,就像 SQL 中的联表查询。在 MyBatis 中,可以使用 标签来定义映射关系,从而完成对象之间的联表查询。例如:

SELECT * FROM User u LEFT JOIN Order o ON u.id = o.user_id LEFT JOIN Item i ON o.id = i.order_id WHERE u.id = #{userId} 复制代码 @Data public class Student { private int id; private String name; //多个学生可以是同一个老师,即多对一 private Teacher teacher; } @Data public class Student { private int id; private String name; private int tid; } @Data public class Teacher { private int id; private String name; //一个老师多个学生 private List students; } 复制代码 //获取指定老师,及老师下的所有学生 public Teacher getTeacher(int id); 复制代码 select s.id sid, s.name sname , t.name tname, t.id tid from student s,teacher t where s.tid = t.id and t.id=#{id} 复制代码

集合的话,使用collection!

复制代码

JavaType和ofType都是用来指定对象类型的

JavaType是用来指定pojo中属性的类型

ofType指定的是映射到list集合属性中pojo的类型

@Test public void testGetTeacher(){ SqlSession session = MybatisUtils.getSession(); TeacherMapper mapper = session.getMapper(TeacherMapper.class); Teacher teacher = mapper.getTeacher(1); System.out.println(teacher.getName()); System.out.println(teacher.getStudents()); } 复制代码 按查询嵌套处理 public Teacher getTeacher2(int id); 复制代码 select * from teacher where id = #{id} select * from student where tid = #{id} 复制代码 @Test public void testGetTeacher2(){ SqlSession session = MybatisUtils.getSession(); TeacherMapper mapper = session.getMapper(TeacherMapper.class); Teacher teacher = mapper.getTeacher2(1); System.out.println(teacher.getName()); System.out.println(teacher.getStudents()); } 复制代码 关联-association 集合-collection 所以association是用于一对一和多对一,而collection是用于一对多的关系

使用说明: 关联和集合都是数据结构中常用的概念。关联通常用来表示两个元素之间的映射关系,而集合用于存储一组元素。在编程中,我们可以使用不同的数据类型来实现这些概念,如哈希表、数组、链表等。

JavaType和ofType都是用来指定对象类型的

JavaType是用来指定pojo中属性的类型 ofType指定的是映射到list集合属性中pojo的类型。

使用 mybatis 动态SQL,通过 if, choose, when, otherwise, trim, where, set, foreach等标签,可组合成非常灵活的SQL语句,从而在提高 SQL 语句的准确性的同时,也大大提高了开发人员的效率。

MyBatis是一款支持动态SQL的ORM框架,其提供了丰富的标签和功能用于生成不同的SQL语句。

动态SQL指的是根据不同的查询条件动态地生成SQL语句,这个过程通常在Java代码中完成。使用动态SQL可以避免在Java代码中手工拼接SQL语句,从而更加安全、灵活、易于维护。

MyBatis提供了以下几种动态SQL标签:

if标签:用于根据条件判断是否包含某段SQL语句。 choose、when、otherwise标签:用于实现类似于Java中的switch语句的功能。 foreach标签:用于循环遍历某个集合,并将集合中的元素作为参数传递给SQL语句。 where标签:用于判断是否需要在SQL语句中添加WHERE关键字。 set标签:用于生成UPDATE语句中SET子句的内容。 SELECT * FROM users AND name = #{name} AND age = #{age} AND gender = #{gender} ORDER BY name ORDER BY age ORDER BY id 复制代码 if 标签:该标签用于根据指定条件有条件地包含 SQL 语句的一部分。 SELECT * FROM users WHERE name = #{name} 复制代码

在此示例中,如果 "name" 参数不为空,则 WHERE 子句将被添加到 SQL 语句中。

choose、when 和 otherwise 标签:这些标签用于在 SQL 中创建类似于 switch 的语句,其中只有多个条件之一可以满足。 SELECT * FROM users WHERE id = #{id} WHERE email = #{email} WHERE name = 'guest' 复制代码

在此示例中,如果存在 "id" 或 "email" 参数,则仅会执行相应的 WHEN 子句。如果这两个参数都不存在,则会执行 OTHERWISE 子句。

foreach 标签:该标签用于迭代集合并动态生成 SQL 语句。 UPDATE users SET name = #{user.name}, email = #{user.email} WHERE id IN #{id} 复制代码 create table `blog` ( `id` varchar(50) not null comment `博客id`, `title` varchar(100) not null comment `博客标题`, `author` varchar(30) not null comment `博客作者`, `create_time` datatime not null comment `创建时间`, `views` int(30) not null comment `浏览量` ) engine = InnoDB default charset = utf8 复制代码 public class IDUtil { public static String genId(){ return UUID.randomUUID().toString().replaceAll("-",""); } } 复制代码 import java.util.Date; public class Blog { private String id; private String title; private String author; private Date createTime; private int views; //set,get.... } 复制代码 public interface BlogMapper { } 复制代码 复制代码 复制代码 //新增一个博客 int addBlog(Blog blog); 复制代码 insert into blog (id, title, author, create_time, views) values (#{id},#{title},#{author},#{createTime},#{views}); 复制代码 //需求1 List queryBlogIf(Map map); 复制代码 select * from blog where title = #{title} and author = #{author} 复制代码

如果 author 等于 null,那么查询语句为 select from user where title=#{title},但是如果title为空呢?那么查询语句为 select from user where and author=#{author},这是错误的 SQL 语句,如何解决呢?

select * from blog title = #{title} and author = #{author} 复制代码

如果标签返回的内容是以AND 或OR 开头的,则它会剔除掉。

int updateBlog(Map map); 复制代码 update blog title = #{title}, author = #{author} where id = #{id}; 复制代码

choose语句

有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句 List queryBlogChoose(Map map); 复制代码 select * from blog title = #{title} and author = #{author} and views = #{views} 复制代码

提取SQL片段:

title = #{title} and author = #{author} 复制代码 select * from blog 复制代码

最好基于 单表来定义 sql 片段,提高片段的可重用性

在 sql 片段中不要包括 where

Foreach List queryBlogForeach(Map map); 复制代码

collection:指定输入对象中的集合属性

item:每次遍历生成的对象

open:开始遍历时的拼接字符串

close:结束时拼接的字符串

separator:遍历对象之间需要拼接的字符串

select * from blog id=#{id} 复制代码

缓存,也称为Cache,是指存储在计算机内存中的临时数据。它可以将用户经常查询的数据放在内存中,提高查询效率,从而解决高并发系统的性能问题。使用缓存可以减少与数据库的交互次数,降低系统开销,提高系统效率。

通常情况下适合缓存的数据是那些经常被查询但不经常改变的数据。这样的数据可以被缓存到内存中,使得下一次查询时不需要从磁盘上读取,减少了系统IO开销,从而提高了系统性能。

MyBatis拥有强大的查询缓存特性,可以轻松地进行定制和配置。缓存的使用可以显著提高查询效率。

在MyBatis中,默认情况下只开启了一级缓存(SqlSession级别的本地缓存),而二级缓存需要手动开启和配置。二级缓存是基于namespace级别的缓存,在扩展性方面有很多优势。

为了实现更灵活的缓存功能,MyBatis定义了一个Cache接口,用户可以通过实现这个接口来自定义二级缓存。

一级缓存,也称为本地缓存,是指在与数据库进行同一次会话期间查询到的数据会被放置在本地缓存中。如果后续需要获取相同的数据,直接从缓存中取出,无需再次查询数据库,可以显著提高系统性能。

//根据id查询用户 User queryUserById(@Param("id") int id); 复制代码 select * from user where id = #{id} 复制代码 一级缓存失效的四种情况

一级缓存是SqlSession级别的缓存,是一直开启的,我们关闭不了它;

一级缓存失效情况:没有使用到当前的一级缓存,效果就是,还需要再向数据库中发起一次查询请求!

一级缓存可以提高查询效率,但需要注意以下四种情况可能会导致缓存失效:

SqlSession被关闭或清空:一级缓存的生命周期与SqlSession一致。当SqlSession被关闭或清空时,缓存也将被清空。 数据库数据发生变化:如果在同一个会话中更新了数据库中的数据,那么与这些数据相关的缓存也会被清空。 查询不同的数据:如果两次查询参数不同,即使是同样的SQL语句,缓存也会失效。 手动清空缓存:我们可以通过调用SqlSession的clearCache()方法来手动清空缓存。

sqlSession不同

结论:每个sqlSession中的缓存相互独立

sqlSession相同,查询条件不同

结论:当前缓存中,不存在这个数据

sqlSession相同,两次查询之间执行了增删改操作!

//修改用户 int updateUser(Map map); 复制代码 update user set name = #{name} where id = #{id} 复制代码

结论:因为增删改操作可能会对当前数据产生影响

sqlSession相同,手动清除一级缓存

一级缓存就是一个map

SqlSession被关闭或清空:在一个请求中,我们创建了一个SqlSession用于查询数据。如果在查询完成后没有关闭或清空SqlSession,那么下次查询相同的数据时,将会从缓存中获取数据,而不是再次查询数据库。但是,如果SqlSession被关闭或清空,缓存也会被清空,下次查询相同的数据时,将会再次执行查询SQL。 数据库数据发生变化:假设我们有一个缓存了所有用户信息的一级缓存。现在我们创建了一个新用户,并提交到数据库保存。由于此时所有与用户相关的缓存都是无效的,所以下次查询用户信息时需要重新查询数据库。 查询不同的数据:我们创建了一个SqlSession,并使用参数“id=1”查询了一个用户信息。此时缓存中会缓存这个用户信息。接着,我们又创建了一个SqlSession,并使用参数“id=2”查询另一个用户信息,即使这两个查询语句完全相同,但由于传入的参数不同,缓存也会失效,需要重新去数据库查询。 手动清空缓存:我们可以通过调用SqlSession的clearCache()方法来手动清空缓存。当我们调用clearCache()方法时,缓存中的所有数据都会被清空,下次查询需要重新去数据库查询数据。 // 创建SqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); // 第一次查询,数据被缓存 User user1 = sqlSession.selectOne("com.example.mapper.UserMapper.selectById", 1L); System.out.println("第一次查询:" + user1); // 第二次查询,从缓存中获取数据 User user2 = sqlSession.selectOne("com.example.mapper.UserMapper.selectById", 1L); System.out.println("第二次查询:" + user2); // 更新数据库中的数据 User updateUser = new User(); updateUser.setId(1L); updateUser.setName("newName"); sqlSession.update("com.example.mapper.UserMapper.updateById", updateUser); sqlSession.commit(); // 第三次查询,缓存失效,需要查询数据库 User user3 = sqlSession.selectOne("com.example.mapper.UserMapper.selectById", 1L); System.out.println("第三次查询:" + user3); // 手动清空缓存 sqlSession.clearCache(); // 第四次查询,缓存已经被清空,需要查询数据库 User user4 = sqlSession.selectOne("com.example.mapper.UserMapper.selectById", 1L); System.out.println("第四次查询:" + user4); // 关闭SqlSession sqlSession.close(); 复制代码

在配置文件 mybatis-config.xml 中开启二级缓存,并在需要使用二级缓存的 Mapper.xml 文件中添加 标签来配置缓存策略。

在实体类中实现 Serializable 接口,以便能够将数据序列化到缓存中。如果实体类不实现 Serializable 接口,则会报 NotSerializableException 的异常。

在需要使用二级缓存的 Mapper.xml 文件中,使用 标签来引用顶层 Mapper.xml 文件中已经定义好的缓存。

避免在 Mapper.xml 文件中使用动态 SQL,因为动态 SQL 无法被缓存。如果需要使用动态 SQL,可以将它们转换成静态 SQL 来使用。

注意事项:

二级缓存是基于 namespace 级别的缓存,一个 namespace 对应一个缓存。 如果一个语句中含有任何动态标签(例如:where),那么这个语句就不能被缓存。 如果两个相同的语句使用了不同的参数,那么这两次查询的结果都会被缓存。 缓存可能会占用大量内存,因此在使用缓存的时候需要考虑缓存的生命周期、缓存的清理策略等问题。

开启全局缓存 【mybatis-config.xml】

复制代码 复制代码

在MyBatis中,会话(session)指的是一个数据库连接对象。在同一个会话中进行的数据库操作共享同一个数据库连接,这样可以提高数据库的性能。

一级缓存(local cache)是指默认开启的基于PerpetualCache实现的本地缓存,在同一个会话中查询到的数据会被缓存在该缓存中,以便后续的查询可以直接从缓存中获取数据,而不需要再次去查询数据库。

二级缓存(second level cache)是指基于Cache接口实现的全局缓存。它可以在多个会话之间共享缓存数据,从而避免了重复查询和浪费资源。只要开启了二级缓存,在同一个Mapper中的查询会先从二级缓存中获取数据,如果缓存中没有,则会从数据库中查询,并将查询结果放入缓存中。当会话提交或关闭时,一级缓存中的数据会被转移到二级缓存中。

因此,这里所说的会话并不是指浏览器的会话,而是指MyBatis中的数据库连接对象。

Ehcache是一种广泛使用的java分布式缓存,用于通用缓存; org.mybatis.caches mybatis-ehcache 1.1.0 复制代码 复制代码

加群联系作者vx:xiaoda0423

仓库地址:github.com/webVueBlog/…



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3