mybatis是基于JDBC的封装,数据库的操作更加的便捷,mybatis除了对JDBC的操作步骤进行封装之外也对其性能进行了优化,在mybatis引入缓存机制,用于提升mybatis的检索效率,而缓存的原理简单地说就是将存储数据的内存
一级缓存:
一级缓存,也叫SqlSession级缓存,无需手动开启可直接使用,为每个sqlsession单独分配的缓存空间,多个sqlsession之间的缓存不共享
mybatisUntil工具类:
由于无论一级缓存还是二级缓存都是基于工具类展开的,所以在整理之前,在这里先附上mybatisUntil工具类的封装代码~而关于缓存机制的整理主要将会根据其特性进行展开
package com.qhit.Untils;
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 java.io.IOException;
import java.io.InputStream;
/**
* Description: NewSsm
* Created by WuHuaSen .
* Created Date: 2022/4/1 16:23
* Version: V1.0
*/
public class MybatisUtil {
//封装会话工厂
private static SqlSessionFactory Factory;
/**在进行对象的跨层传递的时候,使用ThreadLocal可以避免多次传递,打破层次间的约束
*/
private static ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>();
static {
try {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
Factory = sqlSessionFactoryBuilder.build(is);
} catch (
IOException e)
{
e.printStackTrace();
}
}
//封装factory方法
public static SqlSessionFactory getFactory(){
return Factory;
}
//封装sqlSession会话
public static SqlSession getSqlSession(boolean IsAutoComiit) {
SqlSession sqlSession = local.get();
if (sqlSession == null) {
sqlSession = Factory.openSession(IsAutoComiit);
local.set(sqlSession);
}
return sqlSession;
}
//使用泛型封装getMapper
public static <T extends Object> T getMapper(Class<T> c) {
SqlSession sqlSession = getSqlSession(true);
return sqlSession.getMapper(c);
}
}
案例需求:
案例为:“通过会员店ID进行用户信息的查询”;接下来将通过具体的实例来走进mybatis缓存机制,下面代码是对接口类(Dao层)以及接口的映射实现
MemberDao接口 | MemberDaoMapper.xml |
package com.qhit.Dao; import com.mysql.fabric.xmlrpc.base.Params; import com.qhit.pojo.Member; import com.qhit.pojo.MemberSearchCondition; import org.apache.ibatis.annotations.Param; import org.omg.CORBA.Object; import java.util.HashMap; import java.util.List; public interface MemberDao { //修改方法-根据id修改年龄 public int UpdateMemberById(@Param("MemberId") int MemberId,@Param("MemberAge") int MemberAge); //根据会员ID查询 public List<Member> SearchMemberById(int MemberId); } | <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.qhit.Dao.MemberDao"> <sql id="Member_colum_List"> member_id,member_nick,member_gender,member_age,member_city </sql> <!--修改操作--> <update id="UpdateMemberById" parameterType="java.lang.Integer"> UPDATE member SET member_age = #{MemberAge} WHERE member_id = #{MemberId}; </update> <!--根据会员id进行查询--> <select id="SearchMemberById" resultMap="MemberMap"> select <include refid="Member_colum_List"/> from member WHERE member_id = #{MemberId}; </select> </mapper> |
一级缓存的特性:
特性1:
如果多次查询使用的同一个sqlsession对象,第一次查询之后数据会被存放到缓存,后续的查询会直接访问缓存中的存储数据,如果查不到才会访问数据库;
package com.qhit.Dao;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.qhit.Untils.MybatisUtil;
import com.qhit.pojo.Member;
import com.qhit.pojo.MemberSearchCondition;
import com.sun.xml.internal.ws.api.model.MEP;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import org.omg.CORBA.PUBLIC_MEMBER;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* Description: member_RemSql
* Created by WuHuaSen .
* Created Date: 2022/4/7 8:31
* Version: V1.0
*/
public class MemberDaoTest {
MybatisUtil mybatisUtil = new MybatisUtil();
@Test
//根据i会员id进行查询
public void SearchMemberById() {
//通过封装会话获取第一个sqlsession
SqlSession sqlSession1 = mybatisUtil.getSqlSession(true);
//通过封装会话获取第二个sqlsession
SqlSession sqlSession2 = mybatisUtil.getSqlSession(true);
;
//通过第一个sqlsession1获取getMapper
MemberDao memberDao01 = sqlSession1.getMapper(MemberDao.class);
//通过第二个sqlsession2获取getMapper
MemberDao memberDao02 = sqlSession2.getMapper(MemberDao.class);
//通过memberDao01实现第一次查询
List<Member> members01 = memberDao01.SearchMemberById(1);
for (Member m1 : members01
) {
System.out.println(m1);
}
//通过memberDao02实现第二次查询
List<Member> members02 = memberDao02.SearchMemberById(1);
System.out.println(memberDao02);
for (Member m2 : members02
) {
System.out.println(m2);
}
}
}
由于sqlsession是通过getSqlSession(boolean IsAutoComiit)方法直接获取,根据查看工具类我们可以知道,虽然这里我们获取了两次getSqlSession(boolean IsAutoComiit)方法,但是获取到的sqlsession是同一个;所以第二次实现查询方法的时候,会直接从缓存中获取数据~
当我们获取不同的sqlsession时,不同的sqlsession之间缓存并不会共享,所以每次都会从数据库中获取查询数据
package com.qhit.Dao;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.qhit.Untils.MybatisUtil;
import com.qhit.pojo.Member;
import com.qhit.pojo.MemberSearchCondition;
import com.sun.xml.internal.ws.api.model.MEP;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import org.omg.CORBA.PUBLIC_MEMBER;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* Description: member_RemSql
* Created by WuHuaSen .
* Created Date: 2022/4/7 8:31
* Version: V1.0
*/
public class MemberDaoTest {
MybatisUtil mybatisUtil = new MybatisUtil();
@Test
//根据i会员id进行查询
public void SearchMemberById() {
//通过封装会话工厂获取第一个sqlsession
SqlSession sqlSession1 = mybatisUtil.getFactory().openSession();
//通过封装会话工厂获取第二个sqlsession
SqlSession sqlSession2 = mybatisUtil.getFactory().openSession();
;
//通过第一个sqlsession1获取getMapper
MemberDao memberDao01 = sqlSession1.getMapper(MemberDao.class);
//通过第二个sqlsession2获取getMapper
MemberDao memberDao02 = sqlSession2.getMapper(MemberDao.class);
//通过memberDao01实现第一次查询
List<Member> members01 = memberDao01.SearchMemberById(1);
for (Member m1 : members01
) {
System.out.println(m1);
}
//通过memberDao02实现第二次查询
List<Member> members02 = memberDao02.SearchMemberById(1);
System.out.println(memberDao02);
for (Member m2 : members02
) {
System.out.println(m2);
}
}
}
特性2:
如果第一次查询之后对查询出的对象进行修改(修改数据库中的数据),会造成第一次查询的结果和第二次查询结果不同
package com.qhit.Dao;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.qhit.Untils.MybatisUtil;
import com.qhit.pojo.Member;
import com.qhit.pojo.MemberSearchCondition;
import com.sun.xml.internal.ws.api.model.MEP;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.Test;
import org.omg.CORBA.PUBLIC_MEMBER;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* Description: member_RemSql
* Created by WuHuaSen .
* Created Date: 2022/4/7 8:31
* Version: V1.0
*/
public class MemberDaoTest {
MybatisUtil mybatisUtil = new MybatisUtil();
@Test
//根据i会员id进行查询
public void SearchMemberById() {
//通过封装会话获取第一个sqlsession
SqlSession sqlSession1 = mybatisUtil.getSqlSession(true);
//通过封装会话获取第二个sqlsession
SqlSession sqlSession2 = mybatisUtil.getSqlSession(true);
//新开一个Ssqlsession,用于修改
SqlSessionFactory factory = mybatisUtil.getFactory();
SqlSession sqlSession3 =factory.openSession();
//通过第一个sqlsession1获取getMapper
MemberDao memberDao01 = sqlSession1.getMapper(MemberDao.class);
//通过第二个sqlsession2获取getMapper
MemberDao memberDao02 = sqlSession2.getMapper(MemberDao.class);
//通过第三个sqlsession3获取getMapper
MemberDao memberDao03 = sqlSession3.getMapper(MemberDao.class);
//通过memberDao01实现第一次查询
List<Member> members01 = memberDao01.SearchMemberById(1);
for (Member m1 : members01
) {
System.out.println(m1);
}
//通过member03实现修改
int i =memberDao03.UpdateMemberById(1,24);
//通过memberDao02实现第二次查询
List<Member> members02 = memberDao02.SearchMemberById(1);
System.out.println(memberDao02);
for (Member m2 : members02
) {
System.out.println(m2);
}
}
}
根据id(id=1)进行第一次查询,查询之后会把结果存放到缓存中,在第一次查询之后调用修改方法,此修改方法使用的sqlsession与查询所使用的不是同一个,在使用查询的sqlsession进行第二次查询,这是可以看到,虽然我们的数据库数据已经发生改变,但是第二次查询结果却与第一次保持了一致,这是因为,第二次查询并没有访问数据库,而是从缓存中直接获取了数据,进行了打印输出
特性三:
当我们进行查询的时候想要跳过缓存直接获取数据库则可以通过sqlsession.clearCache();来清楚当前sqlsession缓存,
package com.qhit.Dao;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.qhit.Untils.MybatisUtil;
import com.qhit.pojo.Member;
import com.qhit.pojo.MemberSearchCondition;
import com.sun.xml.internal.ws.api.model.MEP;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.Test;
import org.omg.CORBA.PUBLIC_MEMBER;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* Description: member_RemSql
* Created by WuHuaSen .
* Created Date: 2022/4/7 8:31
* Version: V1.0
*/
public class MemberDaoTest {
MybatisUtil mybatisUtil = new MybatisUtil();
@Test
//根据i会员id进行查询
public void SearchMemberById() {
//通过封装会话获取第一个sqlsession
SqlSession sqlSession1 = mybatisUtil.getSqlSession(true);
//新开一个Ssqlsession,用于修改
SqlSessionFactory factory = mybatisUtil.getFactory();
SqlSession sqlSession3 =factory.openSession();
//通过第一个sqlsession1获取getMapper
MemberDao memberDao01 = sqlSession1.getMapper(MemberDao.class);
//通过第三个sqlsession3获取getMapper
MemberDao memberDao03 = sqlSession3.getMapper(MemberDao.class);
//通过memberDao01实现第一次查询
List<Member> members01 = memberDao01.SearchMemberById(2);
sqlSession1.clearCache();
for (Member m1 : members01
) {
System.out.println(m1);
}
//通过member03实现修改
int i =memberDao03.UpdateMemberById(2,25);
//通过memberDao02实现第二次查询
List<Member> members02 = memberDao01.SearchMemberById(2);
System.out.println(memberDao01);
for (Member m2 : members02
) {
System.out.println(m2);
}
}
}
清空内存后,再进行查询会从数据库重新获取数据,实现了数据的一致性~
解决方案 :
1、让修改和查询操作使用相同的sqlsession,但是由于项目servlet中是单例模式,所以此方法不太合理
2、每次进行查询操作之后,清空缓存,让再次进行的查询的时候绕过缓冲直接访问数据库。
3、使用二级缓存~