栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Mybaits生命域、结果集映射、缓存

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Mybaits生命域、结果集映射、缓存

一、生命域和作用周期

理解我们目前已经讨论过的不同作用域和生命周期类是至关重要的,因为错误的使用会导致非常严重的并发问题。

我们可以先画一个流程图,分析一下Mybatis的执行过程!

作用域理解

  • SqlSessionFactoryBuilder 的作用在于创建 SqlSessionFactory,创建成功后,SqlSessionFactoryBuilder 就失去了作用,所以它只能存在于创建 SqlSessionFactory 的方法中,而不要让其长期存在。因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。

  • SqlSessionFactory 可以被认为是一个数据库连接池,它的作用是创建 SqlSession 接口对象。因为 MyBatis 的本质就是 Java 对数据库的操作,所以 SqlSessionFactory 的生命周期存在于整个 MyBatis 的应用之中,所以一旦创建了 SqlSessionFactory,就要长期保存它,直至不再使用 MyBatis 应用,所以可以认为 SqlSessionFactory 的生命周期就等同于 MyBatis 的应用周期。

  • 由于 SqlSessionFactory 是一个对数据库的连接池,所以它占据着数据库的连接资源。如果创建多个 SqlSessionFactory,那么就存在多个数据库连接池,这样不利于对数据库资源的控制,也会导致数据库连接资源被消耗光,出现系统宕机等情况,所以尽量避免发生这样的情况。

  • 因此在一般的应用中我们往往希望 SqlSessionFactory 作为一个单例,让它在应用中被共享。所以说 SqlSessionFactory 的最佳作用域是应用作用域。

  • 如果说 SqlSessionFactory 相当于数据库连接池,那么 SqlSession 就相当于一个数据库连接(Connection 对象),你可以在一个事务里面执行多条 SQL,然后通过它的 commit、rollback 等方法,提交或者回滚事务。所以它应该存活在一个业务请求中,处理完整个请求后,应该关闭这条连接,让它归还给 SqlSessionFactory,否则数据库资源就很快被耗费精光,系统就会瘫痪,所以用 try…catch…finally… 语句来保证其正确关闭。

  • 所以 SqlSession 的最佳的作用域是请求或方法作用域。

二、结果集映射(ResultMap)
  • 假设有两个表,一个Teacher表,一个Student表,一个老师会教多个学生,所以老师跟学生的关系是一对多,学生跟老师的关系是多对一
1、一对一或多对一

实体类

@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接口对应的Mapper.xml文件





   
   
    	select * from student
   
   
   
       
   
   
   
  select * from teacher where id = #{id} and name = #{name}

这种查询方式等价于嵌套查询

select s.*, (select t.name from teacher t where t.id = s.tid) as tname from student s;

当然,也可以通过表连接的方式查询



  select s.id sid, s.name sname , t.name tname
  from student s,teacher t
  where s.tid = t.id



	
	
   
   
   
   
   
       
   

2、一对多

实体类

@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;
}

Mapper接口对应的Mapper.xml配置文件


  select * from student where tid = #{id}



   
   

   
       
       
       
           
           
           
       
   

三、Mybatis缓存

简介

1、什么是缓存 [ Cache ]?

  • 存在内存中的临时数据。

  • 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。

2、为什么使用缓存?

  • 减少和数据库的交互次数,减少系统开销,提高系统效率。

3、什么样的数据能使用缓存?

  • 经常查询并且不经常改变的数据。

Mybaits缓存

  • MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。

  • MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存

    • 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)

    • 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。

  • 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存

一级缓存

一级缓存也叫本地缓存:

  • 与数据库同一次会话期间查询到的数据会放在本地缓存中
    • 一个SqlSession对应一个一级缓存
    • 相同的查询条件,会直接从缓存中获取数据,不会查询数据库
    • SqlSession关闭或提交后,一级缓存会被清除
    • 一级缓存失效的情况
      • SqlSession不同
      • SqlSeesion相同,查询sql不同
      • SqlSeesion相同,两次查询之间执行了增删改操作
      • SqlSeesion相同,手动清除了缓存

二级缓存

  • 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存

  • 基于namespace级别的缓存,一个名称空间,对应一个二级缓存;

  • 工作机制

    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;

    • 如果当前会话关闭或提交了,这个会话对应的一级缓存就没了;如果开启了二级缓存,一级缓存中的数据会被保存到二级缓存中;

    • 同样的,同一个命名空间下

    • 一个命名空间(mapper类)对应一个二级缓存

一级缓存测试

 @Test
    public void test11() {
        // 声明一个SqlSession
        SqlSession sqlSession = MybatisUtil.getSqlSession();

        // 同一个sqlSession下,声明三个mapper
        UserMapper userMapper1 = sqlSession.getMapper(UserMapper.class);
        UserMapper userMapper2 = sqlSession.getMapper(UserMapper.class);
        TeacherMapper teacherMapper = sqlSession.getMapper(TeacherMapper.class);

        // sqlSession中的userMapper1查询
        List users1 = userMapper1.selectUserList();

        // sqlSession2中的userMapper2查询
        List users2 = userMapper2.selectUserList();
        // 结果为true,命中此sqlSession的一级缓存
        System.out.println(users1 == users2);

        // 新增一个teacher
        Teacher teacher = new Teacher();
        teacher.setId(3);
        teacher.setName("hahaha");
        teacherMapper.addTeacher(teacher);

        // sqlSession2中的userMapper2查询
        List users3 = userMapper2.selectUserList();
        // 结果为false,说明此sqlSession的任何增删改操作都会影响此sqlSession的一级缓存
        System.out.println(users1 == users3);
    }

二级缓存测试

		// 声明两个SqlSession
        SqlSession sqlSession1 = MybatisUtil.getSqlSession();
        SqlSession sqlSession2 = MybatisUtil.getSqlSession();

        // 声明三个mapper
        UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
        UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
        TeacherMapper teacherMapper2 = sqlSession2.getMapper(TeacherMapper.class);

        // sqlSession1中的userMapper1查询
        List users1 = userMapper1.selectUserList();
        // 查询后提交,一级缓存清除,保存到UserMapper的二级缓存中
        sqlSession1.commit();

        // sqlSession2中的userMapper2查询
        List users20 = userMapper2.selectUserList();
        // 结果为true,命中UserMapper的二级缓存
        System.out.println(users1 == users20);

        // 通过sqlsession2中的teacherMapper2新增一个user
        User user = new User();
        user.setId(8);
        user.setUsername("hahahaha");
        user.setPassword("123456");
        teacherMapper2.addUser(user);
        sqlSession2.commit();

        // sqlSession2中的userMapper2查询
        List users21 = userMapper2.selectUserList();
        // 结果为true,说明其他命名空间的增删改操作不会影响当前命名空间的缓存,是个需要注意的地方
        System.out.println(users1 == users21);

总结

  • 对于一级缓存,同一个SqlSeesion中,任何增删改操作都会影响此SqlSession的缓存
  • 对于二级缓存,只有同一个命名空间的增删改操作才会影响此命名空间的缓存
  • 当然,MyBatis定义了缓存接口Cache,我们可以通过实现Cache接口来自定义二级缓存
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1006516.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号