延迟加载
1、延迟加载:先从单表查询、需要时再从关联表去关联查询,大大提高 数据库性能,因为查询单表要比关联查询多张表速度要快。
2、如果查询角色并且关联查询动漫信息。如果先查询角色信息即可满足要求,当我们需要查询动漫信息时再查询动漫信息。把对动漫信息的按需去查询就是延迟加载。
3、resultMap可以实现高级映射(使用association、collection实现一对一及一对多映射),association、collection具备延迟加载功能。
4、使用association实现延迟加载
需要定义两个mapper的方法对应的statement。
a、只查询角色信息
SELECT * FROM t_role
在查询角色的statement中使用association去延迟加载(执行)下边的satatement(关联查询动漫信息)
b、关联查询动漫信息
通过上边查询到的角色信息中comic_id去关联查询动漫信息,使用配置文件中的findComicById
上边先去执行findRoleComicsByLazyLoading,当需要去查询动漫的时候再去执行findComicById,通过resultMap的定义将延迟加载执行配置起来,
使用association中的select指定延迟加载去执行的statement的id。
接口类
package ecut.association.mapper;
import java.util.List;
import ecut.association.po.Comic;
import ecut.association.po.Role;
import ecut.association.po.RoleComic;
public interface RoleMapperComic {
//根据id查询动漫
public Comic findComicById(Integer id) throws Exception ;
//查询角色信息,关联查询所属动漫信息,使用resultMap和延迟加载
public List findRoleComicsByLazyLoading() throws Exception ;
}
在配置文件中开启延迟查询(默认false),并将积极加载改为消极加载(默认true)
package ecut.association.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;
import ecut.association.mapper.RoleMapperComic;
import ecut.association.po.Comic;
import ecut.association.po.Role;
import ecut.association.po.RoleComic;
public class ComicMapperTest {
private SqlSessionFactory factory;
@Before
public void init() throws IOException {
String resource = "ecut/association/mybaits-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactoryBuilder builer =new SqlSessionFactoryBuilder();
factory = builer.build(inputStream);
}
@Test
public void testFindRoleComicsByLazyLoading() throws Exception {
SqlSession session = factory.openSession();
RoleMapperComic mapper = session.getMapper(RoleMapperComic.class);
List roleComics = mapper.findRoleComicsByLazyLoading();
//执行getComic()去查询动漫信息,这里实现按需懒加载
System.out.println(roleComics);
}
}
执行上边mapper方法(findRoleComicsByLazyLoading),内部去调用映射文件中的findComicById只查询角色信息(单表)。
在程序中去遍历上一步骤查询出的List
延迟加载,去调用映射文件中findComicById这个方法获取动漫信息。
DEBUG [main] - ==> Preparing: SELECT * FROM t_role
DEBUG [main] - ==> Parameters:
DEBUG [main] - <== Total: 7
DEBUG [main] - ==> Preparing: SELECT * FROM t_comic WHERE id = ?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
DEBUG [main] - ==> Preparing: SELECT * FROM t_comic WHERE id = ?
DEBUG [main] - ==> Parameters: 2(Integer)
DEBUG [main] - <== Total: 1
DEBUG [main] - ==> Preparing: SELECT * FROM t_comic WHERE id = ?
DEBUG [main] - ==> Parameters: 3(Integer)
DEBUG [main] - <== Total: 1
[
角色信息:id =1,roleName =Saber, note = fate stay night, comicId = 1;动漫信息:id=1,comicName=null,remark=null,
角色信息:id =2,roleName =鸣人, note = 火影忍者, comicId = 2;动漫信息:id=2,comicName=null,remark=null,
角色信息:id =3,roleName =佐助, note = 火影忍者, comicId = 2;动漫信息:id=2,comicName=null,remark=null,
角色信息:id =4,roleName =赤丸, note = 火影忍者, comicId = 2;动漫信息:id=2,comicName=null,remark=null,
角色信息:id =5,roleName =兜, note = 火影忍者, comicId = 2;动漫信息:id=2,comicName=null,remark=null,
角色信息:id =6,roleName =鹿丸, note = 火影忍者, comicId = 2;动漫信息:id=2,comicName=null,remark=null,
角色信息:id =7,roleName =萨博, note = 海贼王, comicId = 3;动漫信息:id=3,comicName=null,remark=null]
先查询角色表然后根据comi_id去查询动漫表,查询了三次因为有三个不同的comic_id,相同的comic_id不会调用查询因为会存在缓存。
4、不使用mybatis提供的association及collection中的延迟加载功能,如何实现延迟加载??
查询缓存
1、mybatis提供查询缓存,用于减轻数据压力,提高数据库性能。
2、mybaits提供一级缓存,和二级缓存。
3、作用:如果缓存中有数据就不用从数据库中获取,大大提高系统性能。
一级缓存
1、工作原理
2、测试案例
mybatis默认支持一级缓存,不需要在配置文件去配置。
package ecut.association.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;
import ecut.association.mapper.RoleMapperComic;
import ecut.association.po.Comic;
import ecut.association.po.Role;
import ecut.association.po.RoleComic;
public class RoleMapperComicTest {
private SqlSessionFactory factory;
@Before
public void init() throws IOException {
String resource = "ecut/association/mybaits-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactoryBuilder builer =new SqlSessionFactoryBuilder();
factory = builer.build(inputStream);
}
// 一级缓存测试
@Test
public void testCache1() throws Exception {
SqlSession session = factory.openSession();// 创建代理对象
RoleMapperComic mapper = session.getMapper(RoleMapperComic.class);
// 下边查询使用一个SqlSession
// 第一次发起请求,查询id为1的动漫
Comic comic1 = mapper.findComicById(1);
System.out.println("动漫信息:id="+comic1.getId()+",comicName="+comic1.getComicName()+",remark="+comic1.getRemark());
// 如果session去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
/*// 更新comic1的信息
comic1.setComicName("fate");
mapper.updateComic(comic1);
//执行commit操作去清空缓存
session.commit();*/
// 第二次发起请求,查询id为1的动漫
Comic comic2 = mapper.findComicById(1);
System.out.println("动漫信息:id="+comic2.getId()+",comicName="+comic2.getComicName()+",remark="+comic2.getRemark());
session.close();
}
}
第二次查询直接从缓存中获取没有查询数据库,若进行了update操作缓存中的数据被清空,第二次查询需要重新查询数据库
3、应用场景
正式开发,是将mybatis和spring进行整合开发,事务控制在service中。一个service方法中包括 很多mapper方法调用。
二级缓存
1、工作原理
2、测试案例
mybaits的二级缓存是mapper范围级别,除了在SqlMapConfig.xml设置二级缓存的总开关,还要在具体的mapper.xml中开启二级缓存。
在RoleMapperComic.xml中开启二缓存,RoleMapperComic.xml下的sql执行完成会存储到它的缓存区域(HashMap)。
package ecut.association.po;
import java.io.Serializable;
import java.util.List;
public class Comic implements Serializable {
private static final long serialVersionUID = -3674954993814032572L;
private Integer id;
private String comicName;
private String remark;
private List roles;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getComicName() {
return comicName;
}
public void setComicName(String comicName) {
this.comicName = comicName;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public List getRoles() {
return roles;
}
public void setRoles(List roles) {
this.roles = roles;
}
}
为了将缓存数据取出执行反序列化操作,因为二级缓存数据存储介质多种多样,不一样在内存。
package ecut.association.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;
import ecut.association.mapper.RoleMapperComic;
import ecut.association.po.Comic;
import ecut.association.po.Role;
import ecut.association.po.RoleComic;
public class RoleMapperComicTest {
private SqlSessionFactory factory;
@Before
public void init() throws IOException {
String resource = "ecut/association/mybaits-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactoryBuilder builer =new SqlSessionFactoryBuilder();
factory = builer.build(inputStream);
}
// 二级缓存测试
@Test
public void testCache2() throws Exception {
SqlSession session1 = factory.openSession();
SqlSession session2 = factory.openSession();
SqlSession session3 = factory.openSession();
// 创建代理对象
RoleMapperComic mapper1 = session1.getMapper(RoleMapperComic.class);
// 第一次发起请求,查询id为1的动漫
Comic comic1 = mapper1.findComicById(1);
System.out.println("动漫信息:id="+comic1.getId()+",comicName="+comic1.getComicName()+",remark="+comic1.getRemark());
//这里执行关闭操作,将session中的数据写到二级缓存区域
session1.close();
//使用session3执行commit()操作
RoleMapperComic mapper3 = session3.getMapper(RoleMapperComic.class);
Comic comic = mapper3.findComicById(1);
comic.setComicName("fate");
mapper3.updateComic(comic);
//执行提交,清空RoleMapperComic下边的二级缓存
session3.commit();
session3.close();
RoleMapperComic mapper2 = session2.getMapper(RoleMapperComic.class);
// 第二次发起请求,查询id为1的动漫
Comic comic2 = mapper2.findComicById(1);
System.out.println("动漫信息:id="+comic2.getId()+",comicName="+comic2.getComicName()+",remark="+comic2.getRemark());
session2.close();
}
}
即使session关闭也可以在二级缓存中获取数据,无需查询数据库。
3、useCache和刷新缓存的配置
总结:针对每次查询都需要最新的数据sql,要设置成useCache=false,禁用二级缓存。
在mapper的同一个namespace中,如果有其它insert、update、delete操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读。设置statement配置中的flushCache="true" 属性,默认情况下为true即刷新缓存,如果改成false则不会刷新。使用缓存时如果手动修改数据库表中的查询数据会出现脏读。
update t_comic set comic_name = #{comicName} where id =#{id}
总结:一般下执行完commit操作都需要刷新缓存,flushCache=true表示刷新缓存,这样可以避免数据库脏读。
4、二级缓存应用场景
5、二级缓存的局限性
MyBaitis整合Ehcache
1、分布式缓存
不使用分布缓存,缓存的数据在各各服务单独存储,不方便系统 开发。所以要使用分布式缓存对缓存数据进行集中管理。
mybatis无法实现分布式缓存,需要和其它分布式缓存框架进行整合。
2、整合方法
3、整合步骤(参照官网配置http://www.mybatis.org/ehcache-cache/)
配置mapper中cache中的type为ehcache对cache接口的实现类型。
在classpath下配置ehcache.xml
转载请于明显处标明出处
https://www.cnblogs.com/AmyZheng/p/9382573.html