在说动态SQL前,我补充一下之前没有将的mybatis的缓存,嘿嘿~
一、mybatis缓存
在开讲之前,我先说一句:mybatis的缓存不是目前大家所能见到的软件会普遍使用的,因为mybatis缓存只是在每个人的电脑中存在,换一台电脑这个缓存就无啦!对减轻数据库压力基本没有什么帮助!只是大致的了解一下是怎么回事!因为软件开发市场上目前的主流是缓存服务器,像redis、noSQL等!这个我画一张图解释一下,就是这样:
mybatis的缓存:
分为一级缓存: 无法关闭,默认打开 缓存在SqlSession对象
二级缓存: 以Mapper的namespace分类, 同一个namespace的数据缓存到二级缓存,
Mybatis内部存储缓存使用一个HashMap,key为hashCode+sqlId+Sql语句。value为从查询出来映射生成的java对象
不同sqlSession,查询相同的statement, 走二级缓存, 数据可以共享
mybatis 的二级缓存默认是关闭。
开启mybatis的二级缓存:
1) 在Mybatis的全局配置文件中,允许使用二级缓存
<settings>
<!--允许使用二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
2) 在使用二级缓存的sql映射文件中使用 <cache/> 表示该mapper开启二级缓存
<!--对该mapper开启二级缓存-->
<cache/>
报错:
Caused by: java.io.NotSerializableException: com.fs.entity.User
原因: User实体类没有开启序列化
3) 对我们实体类实现序列化接口
@Data
public class User implements Serializable {
对某个statement 禁用二级缓存:
select标签: 属性: useCache="true" 默认true, 使用缓存
useCache="false" 不使用缓存
<select id="queryById" parameterType="int" resultType="com.fs.entity.User" useCache="false">
自定义缓存框架:
1) 编写一个缓存类实现 org.apache.ibatis.cache.Cache接口
2) <cache type="指向的自定义缓存类包.类" />
之前的缓存框架: Ehcache
二、Mybatis的util工具类
核心类: SqlSessionFactory: 只能有一个
SqlSession: 每请求一次,创建一个SqlSession, 多例
每一次请求都有自己的SqlSession, 每一个线程都有自己的SqlSession
原因:SqlSession已关闭
引起原因: 多个线程共享了同一个SqlSession, SqlSession要线程隔离
ThreadLocal类
所以每个用户的电脑或者主机上都会有一个sqlSession用于存放自己访问这个APP时所使用的线程对象,我们可以根据这个SQLSession来实现多个请求多线程同时访问我们的数据库
这是一个测试类用于测试多个线程同时访问发起请求,测试我们的多个SQLSession请求多线程访问我们的数据库会怎样(下面这个代码是已经完成的代码):
public class TestMybatisUtils {
//开多个线程使用sqlSession
public static void main(String[] args){
new Thread(()->{
SqlSession sqlSession = MybatisUtils.getSqlSession();
System.out.println("t1的SqlSession:"+sqlSession.hashCode());
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
User user = userMapper.queryById(1); //sqlSession.selectOne()
System.out.println(user);
MybatisUtils.closeSqlSession();
}, "t1").start();
new Thread(()->{
SqlSession sqlSession = MybatisUtils.getSqlSession();
System.out.println("t2的SqlSession:"+sqlSession.hashCode());
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.queryById(2);
System.out.println(user);
MybatisUtils.closeSqlSession();
}, "t2").start();
}
}
然后我们需要写一个mybatisUtils工具类用于实现每个用户发起请求时的在本地的sqlSession中存放相应的线程对象,并在合适的时候关闭它。
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
//private static SqlSession sqlSession; //单例, 整个共享同一个SqlSession
private static ThreadLocal<SqlSession> tl = new ThreadLocal<>();
//
static{
InputStream in = null;
try {
in = Resources.getResourceAsStream("mybatis-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 得到SqlSession方法
*/
public static SqlSession getSqlSession(){
//从当前执行的线程本地区获取SqlSession
SqlSession sqlSession = tl.get();
if( sqlSession== null){ //如果没有
sqlSession = sqlSessionFactory.openSession(); //创建SqlSession
tl.set(sqlSession); //把创建SqlSession保存到当前线程的本地区
}
return sqlSession;
}
/**
* 关闭SqlSession
*/
public static void closeSqlSession(){
SqlSession sqlSession = tl.get();
if(sqlSession != null){
sqlSession.commit();
sqlSession.close();
//把线程本地区的sqlSession移除
tl.remove();
}
}
/**
* 得到指定Mapper类的对象
* <T> 定义了泛型变量
* T 使用泛型变量 返回值类型
* Class<T> T给泛型变量赋值
*/
public static <T> T getMapper(Class<T> clazz){
return getSqlSession().getMapper(clazz);
}
}
这样我们就能实现多线程访问数据库啦!我可能讲的还不是特别的清楚,所以希望大佬们看到了见谅,谢谢!感恩戴德!