mybatis总结

推荐一个介绍mybatis的:https://blog.csdn.net/zwx900102/category_8934270.html

学习了自己做个总结

一:流程

        //1,启动加载创建sqlSessionFactory
        String config = "config.xml";
        InputStream inputStream = Resources.getResourceAsStream(config);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //2,创建 sqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //3,创建mapper
        EmailMapper emailMapper = sqlSession.getMapper(EmailMapper.class);
        //4,执行sql
        Email email = emailMapper.selectById(1L);

第一步:加载创建sqlSessionFactory

SqlSessionFactoryBuilder是一个创建sqlSessionFactory的类,采用了构造器模式的,这个类职责很简单,就是new 出sqlSessionFactory,内部有多个build方法,所以这个类生命周期很短。

 build方法核心在于parser.parse()方法,这个方法会去解析配置文件,解析mapper接口,xml文件,关联mapper与xml,解析sql,保存Configuration中。

所以加载这一步是在spring启动的时候就加载起来的。

第二步,获取SqlSession

从sqlSessionFactory中获取SqlSession,sqlSessionFactory采用了工厂模式,sqlSessionFactory有多个openSession方法,根据入参不同来区别。

 sqlSessionFactory#openSession方法核心是会根据configuration获取一个executor对象,

executor对象就是后面真正执行sql的对象。

第三步 获取mapper

在第一步加载的时候,mybatis会把加载好的所有mapper和方法保存在Configuration中,所以

EmailMapper emailMapper = sqlSession.getMapper(EmailMapper.class);

是在Configuration中get出来的

 第四步:执行SQL

4.1 寻找SQL

通过MapperMethodInvoker代理模式构造了一个MapperMethod对象,MapperMethod会有执行的SQL,入参,返回结果集等信息

4.2执行SQL

前面说到sqlSession会有一个executor对象,executor是真正执行SQL流程的对象,具体知识可以看:【MyBatis系列7】原来SqlSession只是个甩手掌柜,真正干活的却是Executor等四大对象_zwx900102的博客-CSDN博客

executor总结:解析SQL,拼装入参,最终转换成PreparedStatement,去执行JDBC的execute方法,返回结果集,再解析结果集转成dto对象映射。所以底层还是用java的jdbc执行。

当然怎么转成PreparedStatement,怎么映射结果集,具体还是很复杂的。

二:mybatis的缓存

具体解析请看:​​​​​​【MyBatis系列8】给我五分钟,带你彻底掌握MyBatis的缓存工作原理_zwx900102的博客-CSDN博客

我这只是做简单的总结,方便自己理解。

看源码

首先,从结构来看,缓存的核心是Cache接口,decorators包下面的类都是实现了Cache接口的,但同时每一种Cache类中都定义了Cache delegate对象:

private final Cache delegate

其实这里用到了设计模式中的装饰器模式,实际Cache的实现类只有一个PerpetualCache

PerpetualCache实际是用了一个map来做缓存。

一级缓存

一级缓存也叫本地缓存,在MyBatis中,一级缓存是在会话(SqlSession)层面实现的,这就说明一级缓存作用范围只能在同一个SqlSession中,跨SqlSession是无效的。

MyBatis中一级缓存是默认开启的,不需要任何配置。

一级缓存只对同一个SqlSession有效。

一级缓存原理

既然一级缓存是sqlsession的,我们来看sqlSession的源码,sqlSession只有这5个对象,看前面两个:

Configuration是全局的,Configuration类里只有关于mapper和xml,结果集等信息,没有关于缓存的信息,所以跳过。

Executor,前面说过Executor是实际去执行SQL的地方,所以缓存也是放在这最合适的

 这里用到了Cache的实际实现类,所以我们才说一级缓存是sqlsession(一个会话)层面的。

具体使用缓存,大概无非就是执行查询前判断下缓存(CacheKey)有没有,有就用,没有则执行查询SQL,并缓存结果,

一级缓存什么时候会被清除

1、就是获取缓存之前会先进行判断用户是否配置了flushCache=true属性(参考一级缓存的创建代码截图),如果配置了则会清除一级缓存。
2、MyBatis全局配置属性localCacheScope配置为Statement时,那么完成一次查询就会清除缓存。
3、在执行commit,rollback,update方法时会清空一级缓存。

二级缓存

二级缓存目的就是要实现作用范围更广,那肯定是要实现跨会话共享的,在MyBatis中二级缓存的作用域是namespace,也就是作用范围是同一个命名空间

在MyBatis中为了实现二级缓存,专门用了一个Executor的装饰器来维护:CachingExecutor

 /**
     * 大致流程
     * 1,创建一级缓存CacheKey
     * 2,获取二级缓存
     * 3,如果没有获取到二级缓存则执行Executor的query方法,会走一遍一级缓存流程
     * 4,查询结果进行缓存
     */
    public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
            throws SQLException {
        //获取二级缓存
        Cache cache = ms.getCache();
        //如果Mapper.xml没有开启二级缓存,cache则会是Null
        if (cache != null) {
            //判断select标签是否配置了flushCache属性
            flushCacheIfRequired(ms);
            if (ms.isUseCache() && resultHandler == null) {
                //二级缓存不能缓存输出类型参数,所以判断确保没有输出参数
                ensureNoOutParams(ms, boundSql);
                //从缓存中获取数据
                List<E> list = (List<E>) tcm.getObject(cache, key);
                if (list == null) {
                    //如果为空就直接去查
                    list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

                    //注意:先缓存到一个临时属性中,等事务提交后才真正保存到二级缓存中,目的就是防止脏读
                    //查到进行缓存,
                    tcm.putObject(cache, key, list); // issue #578 and #116
                }
                return list;
            }
        }
        return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
    }

二级缓存需要开启吗

既然一级缓存默认是开启的,而二级缓存是需要我们手动开启的,那么我们什么时候应该开启二级缓存呢?

1、因为所有的update操作(insert,delete,uptede)都会触发缓存的刷新,从而导致二级缓存失效,所以二级缓存适合在读多写少的场景中开启。

2、因为二级缓存针对的是同一个namespace,所以建议是在单表操作的Mapper中使用,或者是在相关表的Mapper文件中共享同一个缓存。

自定义缓存

mybatis支持自定义使用第三方作为缓存,比如redis等。

三 mybatis的日志

mybatis支持6种日志:SLF4J,LOG4J,LOG4J2,JDK_LOGGING,COMMONS_LOGGING,STDOUT_LOGGING,NO_LOGGING。

采用的设计模式是适配器模式

 

在Configuration类中,可以看到有注册所有类型的日志,然后再setLogImpl中用LogFactory来创建日志对应的适配器,LogFactory静态初始化了所有的类型

 

 setImplementation()方法适配指定的日志类型。

四 mybatis分页插件

在MyBatis中插件式通过拦截器来实现的,真正执行Sql的是四大对象:Executor,StatementHandler,ParameterHandler,ResultSetHandler。而MyBatis的插件正是基于拦截这四大对象来实现的。需要注意的是,虽然我们可以拦截这四大对象,但是并不是这四大对象中的所有方法都能被拦截,下面就是官网提供的可拦截的对象和方法汇总:

在这里插入图片描述

mybatis拦截器插件源码解析 

在SqlSessionFactory build的时候解析配置文件,这里有个解析插件的 ,

private void pluginElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        //拿到interceptor
        String interceptor = child.getStringAttribute("interceptor");
        //拿到配置文件
        Properties properties = child.getChildrenAsProperties();
        //反射得到拦截器对象
        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
        //把配置我呢见属性放在拦截器中
        interceptorInstance.setProperties(properties);
        //把拦截器保存到configuration中,这其实是一个List
        configuration.addInterceptor(interceptorInstance);
      }
    }
  }

可以看到 InterceptorChain是Configuration一个属性,InterceptorChain内部其实是一个List,就是说如果我们实现了多个拦截器,Mybatis是遍历list执行所有的拦截器的。

 

 注意:

interceptor.plugin(target)这个方法里,其实是wrap()方法,这个方法最底层这里有用到动态代理哦,所以mybatis的插件是通过拦截器使用代理模式工作的。

 PageHelper分页插件

也是用到拦截器,在执行sql前拦截并setPage,注意,这里有用到线程本地变量。

为什么PageHelper只对startPage后的第一条select语句有效

在这里插入图片描述

在finally内把ThreadLocal中的分页数据给清除掉了,所以只要执行一次查询语句就会清除分页信息,故而后面的select语句自然就无效了。

五  mybatis中的设计模式 

学习mybatis源码还能学习设计模式:构造模式,工厂模式,代理模式

缓存中用到的模式:装饰器模式

日志用到的模式:适配器模式,工厂模式

插件(分页插件)用到模式:拦截器(aop),动态代理模式

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值