MyBatis高级
1.多参数传递问题
- 多个参数封装成一个对象
- 如果现有对象可以接收使用现有对象
- 如果现有对象无法接收使用DTO(data transfer object)
- 将多个参数封装成一个map(不推荐)
- 在接口中给参数添加
@param
注解List<Person> getPerson(@Param("name") String name, @Param("pwd") String pwd);
2.resultMap(核心)
思考:如果实体类的属性跟数据库的字段不一样,怎么处理
- sql语句设置别名
- 使用resultMap
2.1 解决属性名和字段名不一致的问题
将Person的pwd字段修改为password
<resultMap id="person_result" type="cn.blb.mybatis02.pojo.Person">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="pwd" property="password"/>
</resultMap>
<select id="getPerson" resultMap="person_result">
select * from person where name = #{name} and pwd = #{pwd}
</select>
2.2 一对一
2.2.1 级联属性
-
dto
@Data public class StudentDto { private int id; private String name; private String city; private Teacher teacher; }
-
mapper
Student getStudentAndTeacher(int id);
<resultMap id="studentMap" type="cn.blb.mybatis02.pojo.dto.StudentDto"> <id property="id" column="id"/> <result property="name" column="name"/> <result property="city" column="city"/> <result property="teacher.id" column="tid"/> <result property="teacher.name" column="tname"/> </resultMap> <select id="getStudentAndTeacher" resultMap="studentMap"> select s.*, t.id tid, t.name tname from student s, teacher t where s.tid = t.id and s.id = #{id} </select>
2.2.2 association
-
实体类
StudentDto getStudentAndTeacher(int id);
-
mapper
<resultMap id="studentMap" type="cn.blb.mybatis02.pojo.dto.StudentDto"> <id property="id" column="id"/> <result property="name" column="name"/> <result property="city" column="city"/> <association property="teacher" javaType="cn.blb.mybatis02.pojo.Teacher"> <id property="id" column="tid" /> <result property="name" column="tname"/> </association> </resultMap> <select id="getStudentAndTeacher" resultMap="studentMap"> select s.*, t.id tid, t.name tname from student s, teacher t where s.tid = t.id and s.id = #{id} </select>
2.2.3 分步查询
-
mapper
StudentDto getStudentAndTeacher(int id);
<resultMap id="studentMap" type="cn.blb.mybatis02.pojo.dto.StudentDto"> <id property="id" column="id"/> <result property="name" column="name"/> <result property="city" column="city"/> <association property="teacher" javaType="cn.blb.mybatis02.pojo.Teacher" select="cn.blb.mybatis02.dao.TeacherMapper.getTeacherById" column="tid" /> </resultMap> <select id="getStudentAndTeacher" resultMap="studentMap"> select * from student where id = #{id} </select>
-
teacher
-
mapper
Teacher getTeacherById(int id);
<select id="getTeacherById" resultType="cn.blb.mybatis02.pojo.Teacher"> select * from teacher where id = #{id} </select>
-
2.3 一对多
关键字:connection
- 需求:一个老师对应多个学生
- 根据老师的id去查询老师信息,并且查询出这个老师所带的所有学生
实现:
-
dto
@Data public class TeacherDto { private int id; private String name; private List<Student> students; }
-
mapper
TeacherDto getTeacherAndStudentById(int id);
<resultMap id="teacherMap" type="cn.blb.mybatis02.pojo.dto.TeacherDto"> <id property="id" column="id"/> <result property="name" column="name"/> <!-- ofType:返回值的对象的类型 javaType:集合的类型,因为给我们封装好了,所以不用写全量名称 --> <collection property="students" ofType="cn.blb.mybatis02.pojo.Student" javaType="list"> <id property="id" column="sid"/> <result property="name" column="sname"/> <result property="city" column="city"/> <result property="tid" column="tid"/> </collection> </resultMap> <select id="getTeacherAndStudentById" resultMap="teacherMap"> select t.*, s.id as sid, s.name as sname, s.city as city, s.tid as tid from teacher t, student s where t.id = s.tid and t.id = #{id} </select>
3.动态SQL(核心)
动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦
为什么能够实现动态SQL: 基于 OGNL 的表达式,跟 JSTL 非常的相似
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
4.延迟加载
前置条件起码要查询两次
第一次查询的是学生的数据,第二次查询的是老师的数据,
但是在有的时候,只要学生的数据,有的时候需要学生+老师的数据,如果是第一种情况,第二次查询就没有必要去执行了
懒加载的核心说明:按需加载
需求表现的形式:有没有使用teacher这个对象
使用:两处
1.核心配置文件中添加一个配置
<!-- 开启懒加载 ,默认值是false,true表示开启-->
<setting name="lazyLoadingEnabled" value="true"/>
2.resultMap中添加一个属性
association fetchType=“lazy”
测试:不打印teacher的数据,观察sql语句
打印teacher的数据,观察sql语句
5.核心对象的生命周期
SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域,
用完之后需要赶紧关闭,否则资源被占用
-
SqlSessionFactoryBuilder:
- 一旦创建了SqlSessionFactory,就不再需要它了
- 局部变量、
-
SqlSessionFactory:
- 说白了就可以想象为:数据库连接池
- SqlSessionFactory一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建一个实例
- 因此SqlSessionFactory的最佳作用域是应用作用域(ApplocationContext)因此SqlSessionFactory的最佳作用域是应用作用域(ApplocationContext)
- 最简单的就是使用单例模式或静态单例模式
-
SqlSession:
- 连接到连接池的一个请求
- SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域
- 用完之后需要赶紧关闭,否则资源被占用!
6.缓存
6.1 什么是缓存
- 什么是缓存[Cache]?
- 存在内存中的临时数据
- 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题
- 为什么使用缓存?
- 减少和数据库的交互次数,减少系统开销,提高系统效率
- 什么样的数据可以使用缓存?
- 经常查询并且不经常改变的数据 【可以使用缓存】
6.2 MyBatis缓存
- MyBatis包含一个非常强大的查询缓存特性,它可以非常方便的定制和配置缓存,缓存可以极大的提高查询效率
- MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存
- 默认情况下,只有一级缓存开启(SqlSession级别的缓存,也称为本地缓存)
- 二级缓存需要手动开启和配置,他是基于namespace级别的缓存
- 为了提高可扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来定义二级缓存
6.2.1 一级缓存
-
一级缓存也叫本地缓存:SqlSession
- 与数据库同一次会话期间查询到的数据会放在本地缓存中
- 以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库
@Test public void test1() { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.getUserById(1); System.out.println(user); System.out.println("====================================="); User user2 = mapper.getUserById(1); System.out.println(user2 == user); }
缓存失效的情况:
- 查询不同的东西
- 增删改操作,可能会改变原来的数据,所以必定会刷新缓存
- 查询不同的Mapper.xml
- 手动清理缓存
6.2.2 二级缓存
-
二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
-
基于namespace级别的缓存,一个名称空间,对应一个二级缓存
工作机制- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中
- 如果会话关闭了,这个会员对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的
- 数据被保存到二级缓存中
新的会话查询信息,就可以从二级缓存中获取内容 - 不同的mapper查询出的数据会放在自己对应的缓存(map)中
一级缓存开启(SqlSession级别的缓存,也称为本地缓存)
-
二级缓存需要手动开启和配置,他是基于namespace级别的缓存
-
为了提高可扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来定义二级缓存