1.懒加载
一对多懒加载
1.mybatis配置中开启懒加载
<!-- 开启全局懒加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
2.查询所有学生
/**
* 查找学生 并查看学生的成绩
* 一对多懒加载
* @return
*/
List<Student> findAllStudentWithScoreLazy();
<!--
一对多懒加载
1.查询所有的学生 select * from student_tb
2.根据学生的 id 查找到所有相关学生的成绩 列表
<collection property="scoreList" column="id" ofType="Score"
select="com.qfedu.dao.ScoreDao.findAllScoreByStudentId">
property="scoreList" Student 类中的 成绩列表属性
ofType="Score" 集合中放置的类型
column="id" 学生id 数据表列名、
select="com.qfedu.dao.ScoreDao.findAllScoreByStudentId" 调用ScoreDao 中对应的方法并且 将 student表中列名 id 学生id对应的值给到这个方法
-->
<resultMap id="studentWithScoreLazyMap" type="Student">
<id column="id" property="id"></id>
<result property="name" column="name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="height" column="height"></result>
<result property="sAddress" column="s_address"></result>
<collection property="scoreList" column="id" ofType="Score" select="com.qfedu.dao.ScoreDao.findAllScoreByStudentId">
</collection>
</resultMap>
<select id="findAllStudentWithScoreLazy" resultMap="studentWithScoreLazyMap">
select * from student_tb
</select>
3.当需要查询学生成绩时调用
public interface ScoreDao {
/**
* 根据学生的id 去查询该学生的列表
* @param id
* @return
*/
List<Score> findAllScoreByStudentId(int id);
}
<?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">
<mapper namespace="com.qfedu.dao.ScoreDao">
<select id="findAllScoreByStudentId" resultType="Score">
select * from score_tb where studentid = #{id}
</select>
</mapper>
测试
@Test // 一对多懒加载
public void findAllStudentWithScoreLazyTest(){
List<Student> studentList = studentDao.findAllStudentWithScoreLazy();
for (Student student:studentList){
System.out.println("student-name:"+student.getName());
// 懒加载 只有当调用时 student.getScoreList() 才去请求查询学生成绩
System.out.println("student-scoreList:"+student.getScoreList());
// 一旦调用 student的 toString 就会触发懒加载
//mybatis 底层机制只要 触发equals,clone,hashCode,toString 就会懒加载
// 可以再setting 里面关闭功能
System.out.println("student:"+student);
}
}
一对一懒加载
查看所有成绩,并在需要时查看改成绩对应的学生信息
1.先查出来所有的成绩列表
/**
* 查询所有的成绩 并再需要时包含 该成绩对应的学生
* @return
*/
List<Score> findAllScoreWithStudentByLazy();
<!--
一对一 懒加载
1.查询所有成绩 select * from score_tb
2.当需要成绩对应的学生信息时
执行 一对一
<association property="student" column="studentid"
javaType="Student" select="com.qfedu.dao.StudentDao.findStudentById">
property="student" Score 类中的student 属性
column="studentid" 成绩表中 学生id 的列名
javaType="Student" 返回的类型
select="com.qfedu.dao.StudentDao.findStudentById" 当需要改学生的信息时调用此方法 并且经对应studentid 的值传给他
-->
<resultMap id="scoreWithStudentByLazyMap" type="Score">
<id column="scoreid" property="scoreid"></id>
<result column="couresename" property="couresename"></result>
<result column="score" property="score"></result>
<result column="studentid" property="studentid"></result>
<association property="student" column="studentid"
javaType="Student" select="com.qfedu.dao.StudentDao.findStudentById">
</association>
</resultMap>
<select id="findAllScoreWithStudentByLazy" resultMap="scoreWithStudentByLazyMap">
select * from score_tb
</select>
2.再需要时 差选该成绩对应的学生信息
/**
* 根据id 查找学生
* @param id
* @return
*/
Student findStudentById(int id);
<select id="findStudentById" resultType="Student">
select * from student_tb where id = #{id}
</select>
3.测试
@Test //测试方法必须时 public void
public void findAllScoreWithStudentByLazyTest(){
List<Score> scoreList = scoreDao.findAllScoreWithStudentByLazy();
for (Score score:scoreList){
// 打印对象没有触发懒加载是因为再setting 已经关闭
System.out.println("score:"+score);
// 获取student 相关信息 触发懒加载
System.out.println("student:"+score.getStudent());
}
}
局部控制懒加载
fetchType="lazy" 默认情况就是懒加载 因为你开启了全局懒加载
eager 立即加载 如果配置为 eager 则会覆盖全局的开启的懒加载功能
2.mybatis的缓存
1缓存
暂时的存放常用小批量数据
特点:换窜数据不可靠(可能会丢失),存放热点数据(经常使用的数据)
优点:缓存大部分存在内存中,查询速度快
mybatis缓存
一级缓存:基于sqlSession,
二级缓存基于namespace,同一个namespace下所有数据可以共享
一个mapper xml 用自己的namespace,另外也可以多个mapper(dao接口)共享同一个二级缓存namespace
一级缓存
一级缓存:基于sqlSession,
当前sqlSession 发生 修改,增加,删除 动作,就会把当前缓存的所有 数据清空
@Test
public void firstLevelCacheTest(){
//查询所有时 可以缓存数据,但是一旦发生增加 或者删除 修改 就要清空
studentDao.findAllStudentWithScoreLazy();
// 查询第一次 有执行sqlselect * from student_tb where id = ?
Student student1 = studentDao.findStudentById(4);
System.out.println("student1:"+student1);
// 查询第二次 没有执行sql 没有去数据库查数据,从一级缓存sqlsession 获得数据
Student student2 = studentDao.findStudentById(4);
System.out.println("student2:"+student2);
student2.setName("志恒哥1");
int num = studentDao.updateStudent(student2);
System.out.println("num:"+num);
// 再次查询数据 需要去数据库查数据 为什么?
// 应为 当前sqlSession 发生 修改,增加,删除 动作,就会把当前缓存的所有 数据清空
Student student3 = studentDao.findStudentById(4);
System.out.println("student3:"+student3);
studentDao.findAllStudentWithScoreLazy();
}
二级缓存
在同一namespace下,共享一块缓存空间,如果多个mapper (dao.xml)共享同一namesapce 则也共享一块缓存
二级缓存是跨sqlsession,多个sqlsession可以去二级缓存获取数据
sqlsession 获取数据先去二级缓存获取,如果有得到数据,并写入自己的一级缓存
二级缓存:只要发生增删改,就会将同一命名空间(namespace)下的缓存块清空
使用二级缓存的实体类必须实现序列化,否则报错
实现
1.开启二级缓存
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
2.在mapper的命名空间下配置缓存
<!--配置缓存
1.开启 <cache></cache>
2.配置参数 flushInterval="10000" 配置缓存刷新时间 清空
eviction="LRU" 最少使用的先删除 就是使用 LruCache 类实现
size="1000" 缓存最多 1000 条数据 1000 sql
readOnly="true" 只读不可修改 只读性能差,但是安全 获取的拷贝 可以修改 获取的缓存对象就是直接的应用
blocking="false" 配置是否阻塞
type 用来配自定义二级缓存
-->
<cache flushInterval="10000" eviction="LRU" size="1000" readOnly="false" blocking="false" ></cache>
3.使用
<!--
useCache="true" 使用缓存 一般不需要配置 默认就开启
-->
<select id="findStudentById" resultType="Student" useCache="true" >
select * from student_tb where id = #{id}
</select>
4.测试
@Test // 二级缓存
public void secondLevelCacheTest(){
// 查询第一次 有执行sqlselect * from student_tb where id = ?
Student student1 = studentDao.findStudentById(4);
System.out.println("student1:"+student1);
// 查询第二次 没有执行sql 没有去数据库查数据,从一级缓存sqlsession 获得数据
Student student2 = studentDao.findStudentById(4);
System.out.println("student2:"+student2);
// 只有sqlSession 调用close(),commit() 方法 才会将数据提交到二级换存,其他的sqlSession才能拿到
// sqlSession.close();
sqlSession.commit();
// studentDao2 studentDao 来自于不同的sqlSession
studentDao2 = sqlSessionFactory.openSession(true).getMapper(StudentDao.class);
Student student3 = studentDao2.findStudentById(4);
System.out.println("student3:"+student3);
}
Mybatis使用一级缓存二级缓存时的几种情况(串数据的情况及解决办法)
情景一:从java1中查sid ,先去二级缓存找,再去一级缓存找,都没有再去数据库找,
从数据库中取出来,存放在二级缓存,在存放在一级缓存,在返回给java。
(现在二级缓存中有java1的数据,一级缓存中也存有java1的数据)
当java2查询的时候,从二级查询中取出id,没错误。
情景二:在java2中更改(增删改),再在Java2中查询,数据从Java2,二级缓存一级缓存没 找到再去MySQL中查出,传回二级缓存~一级缓存,这时候java1在查,查的是二级 缓存中,Java2更改过的数据,不会造成误差,没错误。
情景三:在java2中更改(增删改),再在Java1中查询,这时候java1查询id,查的是二级缓存 中没有更改的数据,但是id被Java2更改过了,java1取得是没有更改的id,会造成误 差
串数据的解决办法
1、不用缓存,把缓存关了再用
2、只用二级缓存,不用一级缓存
3.注解
开启二级缓存
@CacheNamespace(blocking = true) // 开启二级缓存
多参数方法的参数的使用 @Param
// 当mybatis 方法参数为多个参数 可以使用 以下几种方式
// 1.传对象 传map
// 2. 使用 arg0 arg1 或者 param1 param2
// 3. 先 将参数声明,再使用 @Param("name") String name,@Param("sex") String sex
// @Select("select * from student_tb where name like #{arg0} and sex = #{arg1}")
// @Select("select * from student_tb where name like #{param1} and sex = #{param2}")
@Select("select * from student_tb where name like #{name} and sex = #{sex}")
List<Student> findStudentByNameAndSex(@Param("name") String name,@Param("sex") String sex);
4.pageHelper
1.引入依赖
<!--配置分页 放在最下面-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.1.4</version>
</dependency>
2.再mybatis配置文件声明插件,
注意在 environments之前声明
<plugins>
<!-- 让mybatis 加载PageHelper -->
<!-- com.github.pagehelper为PageHelper类所在包名 -->
<plugin interceptor="com.github.pagehelper.PageHelper">
<!-- 使用MySQL方言的分页 -->
<property name="helperDialect" value="mysql"/><!--如果使用mysql,这里value为mysql-->
<property name="pageSizeZero" value="true"/>
</plugin>
</plugins>
3.使用
// 以前 分页 select * from student_tb limit (pageIndex -1)* pageSize,pageSize
// 现在 1.select * from student_tb ,其他的分页交给插件pageHelper
@Test
public void pageHelpTest(){
// 开启分页 // 查询第一页 每页连个数据
PageHelper.startPage(2,2);
List<Student> studentList = studentDao2.findAllStudent();
for (Student student:studentList){
System.out.println("student:"+student);
}
// 获取所有分页信息
PageInfo<Student> pageInfo = new PageInfo<Student>(studentList);
System.out.println("分页大小"+pageInfo.getPageSize());
System.out.println("总页大小"+pageInfo.getPages());
System.out.println("总条数大小"+pageInfo.getTotal());
}