200字范文,内容丰富有趣,生活中的好帮手!
200字范文 > mybatis源码_Mybatis源码之SqlSession

mybatis源码_Mybatis源码之SqlSession

时间:2020-12-06 12:18:42

相关推荐

mybatis源码_Mybatis源码之SqlSession

SqlSession简介

Mybatis是一个强大的ORM框架,它通过接口式编程为开发者屏蔽了传统JDBC的诸多不便,以简单的方式提供强大的扩展能力。其中的接口式编程就是指日常使用的Mapper接口,Mybatis借助动态代理实现了sql语句与Mapper的接口的动态绑定,降低复杂度的同时为开发人员提供强大自主权。

经过上一篇文章《Mybatis源码之SQL执行过程》已经知道,Mapper其实是Mybatis暴露给开发人员的扩展能力,在运行时Mybatis把Mapper接口转为动态方法,最终也会转交给SqlSession的CRUD方法执行。所以,SqlSession是Mybatis框架真正意义上的命令执行入口。

SqlSession是Mybatis中最基础的一个接口类,它代表一个与数据库的会话,在其生命周期内完成与数据库的交互,通过它可以:

执行基本的SQL命令(select/update/insert/delete):SQL命令具有唯一ID,每个ID对应Configration中的一个MappedStatement。

获取自定义的Mapper接口对象(getMapper):使用动态代理技术动态生成Mapper接口实现类,由MapperProxy代理实现。

控制管理事务操作(rollback/commit):数据库事务操作中提交与回滚操作。

image.png

以上类图为SqlSession等Mybatis接口层的继承体系与关系,由上图可知:

SqlSessionFactory负责SqlSession的创建工作,其默认实现为DefaultSqlSessionFactory。

SqlSession有两个实现类:DefaultSqlSession和SqlSessionManager,其中DefaultSqlSession为默认实现;SqlSessionManager除了实现SqlSession接口外,还实现了SqlSessionFactory接口。

接下来,我们通过实例和源码来详细了解下DefaultSqlSession和SqlSessionManager两者的特点与差别,进一步加深对它的理解。

DefaultSqlSession

DefaultSqlSession是SqlSession的默认实现,它纯粹是一个功能类,完全实现了SqlSession的接口方法,但是它并不具备自主管理能力。在开始之前,我们先了解两个问题:

DefaultSqlSession是非线程安全的,因此需要避免多线程并发使用,随用随取,及时释放;

DefaultSqlSession它不具有自动关闭的能力,需要开发者自主调用关闭方法。

下面这个示例代码还是跟之前一样,我们重点关注第7-9行,其实只有两行代码。

1publicstaticvoidmain(String[]args)throwsIOException{

2//mybatis配置文件

3Stringpath="mybatis-config.xml";

4InputStreaminputStream=Resources.getResourceAsStream(path);

5

6//获取SqlSessionFactory

7SqlSessionFactorysqlSessionFactory=newSqlSessionFactoryBuilder().build(inputStream);

8//创建SqlSession

9try(SqlSessionsqlSession=sqlSessionFactory.openSession()){

10//……

11}

12}

第7行:主要完成配置文件解析并保存到Configuration,由SqlSessionFactoryBuilder创建SqlSessionFactory对象,为创建SqlSession做准备。

第9行:采用“try-with-resource”方式调用SqlSessionFactory#openSession方法创建了SqlSession对象。当代码离开try代码块时会自动关闭SqlSession,但是这里的自动关闭并不是SqlSession的能力,相当于手动调用了SqlSession#close方法。看下openSession的实现:

1publicSqlSessionopenSession(){

2returnopenSessionFromDataSource(configuration.getDefaultExecutorType(),null,false);

3}

4

5privateSqlSessionopenSessionFromDataSource(ExecutorTypeexecType,TransactionIsolationLevellevel,booleanautoCommit){

6Transactiontx=null;

7try{

8//获取环境配置信息

9finalEnvironmentenvironment=configuration.getEnvironment();

10//创建事务处理器

11finalTransactionFactorytransactionFactory=getTransactionFactoryFromEnvironment(environment);

12tx=transactionFactory.newTransaction(environment.getDataSource(),level,autoCommit);

13//创建Executor,这里默认使用CachingExecutor

14finalExecutorexecutor=configuration.newExecutor(tx,execType);

15//创建SqlSession,默认实例DefaultSqlSession

16returnnewDefaultSqlSession(configuration,executor,autoCommit);

17}catch(Exceptione){

18closeTransaction(tx);//mayhavefetchedaconnectionsoletscallclose()

19throwExceptionFactory.wrapException("Erroropeningsession.Cause:"+e,e);

20}finally{

21ErrorContext.instance().reset();

22}

23}

由此可知,openSessionFromDataSource方法完成了SqlSession的创建,它返回了DefaultSqlSession对象。DefaultSqlSession中聚合了Configuration、Executor等对象。

selectXxx方法

以selectList为例看下selectXxx方法的执行过程:

1publicListselectList(Stringstatement,Objectparameter,RowBoundsrowBounds){2try{3//statement为MappedStatement的唯一id,注册在Configuration#mappedStatements 4//根据id获取MappedStatement 5MappedStatementms=configuration.getMappedStatement(statement); 6//调用执行器的query方法执行查询命令。 7returnexecutor.query(ms,wrapCollection(parameter),rowBounds,Executor.NO_RESULT_HANDLER); 8}catch(Exceptione){9throwExceptionFactory.wrapException("Errorqueryingdatabase.Cause:"+e,e);10}finally{11ErrorContext.instance().reset();12}13}

SqlSession中selectXxx方法,最终都走到了selectList方法,但是它并没有直接与数据库交互,获取MappedStatement对象后调用了执行器Executor的query方法。

insert方法

insert对应sql的insert命令,代码如下:

1publicintinsert(Stringstatement){

2returninsert(statement,null);

3}

4

5publicintinsert(Stringstatement,Objectparameter){

6returnupdate(statement,parameter);

7}

8

9publicintupdate(Stringstatement,Objectparameter){

10try{

11dirty=true;

12MappedStatementms=configuration.getMappedStatement(statement);

13returnexecutor.update(ms,wrapCollection(parameter));

14}catch(Exceptione){

15throwExceptionFactory.wrapException("Errorupdatingdatabase.Cause:"+e,e);

16}finally{

17ErrorContext.instance().reset();

18}

19}

与select类似,首先获取MappedStatement,然后调用了执行器的update方法。整体流程比较简单,有一点需要说明的是:对于数据库而言,insert/update/delete其实都是更新操作,所以这里虽然是insert方法入口,但是实际调用了Executor#update方法。

delete方法/update方法

delete/update方法的执行过程与insert基本是一样的,不再赘述。

小结

DefaultSqlSession是SqlSession的默认实现,提供了数据库的CURD能力,但是它并没有直接与数据库交互,而是调用了Executor的相关方法,是一个名副其实的“甩手掌柜”。

在selectXxx或insert方法中,我们并没有发现它调用close方法,所以它不具备自动关闭的能力,需要我们开发者手动调用,比如示例中的“try-with-resource”方式。

回过头来说下“线程安全”:通过上述示例和源码,我们会发现一旦DefaultSqlSession对象被创建,任何获取到它的线程都可以访问其方法,CURD或者close,多个线程同时操作就会导致不可预知的结果。所以,我们不能把它直接设置为公共实例,应该即用即取,及时释放。

SqlSessionManager

看完DefaultSqlSession,继续看下SqlSessionManager。由上述类图可知,它实现了SqlSession和SqlSessionFactory两个接口,同时具备SqlSession接口能力及SqlSession对象创建能力。

仔细观察类图会发现,SqlSessionManager内部还聚合了一个SqlSessionFactory和SqlSession对象,什么情况?实现了这两个接口,却又聚合了这两个接口的实例,难道它只是个代理,就是包了一层?源码走走看!

1publicclassSqlSessionMgrDemo{

2publicstaticvoidmain(String[]args)throwsIOException{

3//mybatis配置文件

4Stringpath="mybatis-config.xml";

5InputStreaminputStream=Resources.getResourceAsStream(path);

6//获取SqlSessionManager

7SqlSessionManagersqlSessionManager=SqlSessionManager.newInstance(inputStream);

8//开启对session的管理:没有这行代码也可以使用

9sqlSessionManager.startManagedSession();

10//获取mapper

11CompanyDaodao=sqlSessionManager.getMapper(CompanyDao.class);

12//执行查询

13CompanyDOcompanyDO=dao.selectById(1);

14System.out.println(companyDO);

15}

16}

简单说下这段代码的执行流程:

4~5行:加载并读取配置文件;

7行:创建SqlSessionManager实例;

9行:开启对SqlSession的管理,通过它可以实现对SqlSession的线程安全管理,使SqlSessionManager实例可共享(这个与DefaultSqlSession有差别);

11~14行:执行查询;

实例化

通过示例代码可知,SqlSessionManager#newInstance创建了SqlSessionManager对象,看下执行过程,我们仔细看看SqlSessionManager到底是个什么东东。

1//创建SqlSessionManager

2publicstaticSqlSessionManagernewInstance(InputStreaminputStream){

3returnnewSqlSessionManager(newSqlSessionFactoryBuilder().build(inputStream,null,null));

4}

5

6//构造方法,这里入参为DefaultSqlSessionFactory实例

7privateSqlSessionManager(SqlSessionFactorysqlSessionFactory){

8

9this.sqlSessionFactory=sqlSessionFactory;

10

11//通过动态代理,创建SqlSession实例

12this.sqlSessionProxy=(SqlSession)Proxy.newProxyInstance(

13SqlSessionFactory.class.getClassLoader(),

14newClass[]{SqlSession.class},

15newSqlSessionInterceptor());

16}

17

18//SqlSessionFactory实例

19privatefinalSqlSessionFactorysqlSessionFactory;

20

21//SqlSession代理对象

22privatefinalSqlSessionsqlSessionProxy;

newInstance方法调用了SqlSessionManager的私有构造方法(仅有私有构造方法),构造方法的入参是SqlSessionFactory(与DefaultSqlSession的实现一致),这里是其默认实现DefaultSqlSessionFactory。

构造方法内通过动态代理创建对象sqlSessionProxy,它是SqlSession的动态实现,这里需要重点关注SqlSessionInterceptor这个类。从名称看它是一个拦截器,也就是说当我们调用SqlSession的方法时,会被它拦截,看下SqlSessionInterceptor#invoke方法:

1privateclassSqlSessionInterceptorimplementsInvocationHandler{

2publicSqlSessionInterceptor(){

3//PreventSyntheticAccess

4}

5

6@Override

7publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{

8

9//判断本地线程是否有sqlSession

10finalSqlSessionsqlSession=SqlSessionManager.this.localSqlSession.get();

11//如果有,直接使用,无需创建

12if(sqlSession!=null){

13try{

14//调用SqlSession接口方法实现

15returnmethod.invoke(sqlSession,args);

16}catch(Throwablet){

17throwExceptionUtil.unwrapThrowable(t);

18}

19}else{

20//如果没有,通过openSession创建,openSession方法返回的是DefaultSqlSession实例

21//这里使用try-with-resource,使用完成会自动关闭

22try(SqlSessionautoSqlSession=openSession()){

23try{

24//调用SqlSession接口方法实现

25finalObjectresult=method.invoke(autoSqlSession,args);

26//事务提交

mit();

28returnresult;

29}catch(Throwablet){

30//事务回滚

31autoSqlSession.rollback();

32throwExceptionUtil.unwrapThrowable(t);

33}

34}

35}

36}

37}

SqlSessionInterceptor#invoke首先会从localSqlSession获取SqlSession对象,localSqlSession是什么呢?这里只提到get,那set在哪里?从代码里面找下吧:

1//localSqlSession是一个ThreadLocal对象,用来存储SqlSession

2//这是一个线程安全的标志

3privatefinalThreadLocallocalSqlSession=newThreadLocal<>(); 4 5//开启对SqlSession的管理,我们示例代码有调用 6publicvoidstartManagedSession(){7//为localSqlSession赋值,参数为openSession返回值 8this.localSqlSession.set(openSession()); 9}1011publicSqlSessionopenSession(){12//与之前一样,它返回的是DefaultSqlSession对象13returnsqlSessionFactory.openSession();14}

localSqlSession是一个ThreadLocal对象,用来存储SqlSession,它用来在线程内共享对象,很明显的线程安全处理方式。localSqlSession是在startManagedSession及其重载方法中设置的,通过openSession方法可知,localSqlSession内部存放的是DefaultSqlSession对象。

继续SqlSessionInterceptor#invoke分析,根据localSqlSession中是否存有sqlSession,有两个不同的处理方式:

有:如果有,则直接使用,不再新建。这里只能是在同一线程内共享,避免了重复创建对象的额外开销。

无:如果没有,就与我们DefaultSqlSession一节中的使用方式一样,使用了“try-with-resource”方式,实现了SqlSession的自动关闭。

SqlSessionManager持有的SqlSessionFactory引用为DefaultSqlSessionFactory,作用自然是创建SqlSession实例;SqlSessionFactory创建的SqlSession是DefaultSqlSession实例;但是SqlSessionManager内部通过动态代理模式使用SqlSessionInterceptor对DefaultSqlSession进行了拦截,在拦截处理中有两种逻辑:一是使用ThreadLocal对SqlSession进行了线程安全共享;二是实现了对SqlSession的自动关闭。由此可见:SqlSessionManager是DefaultSqlSession的加强版,或者说是一个高级封装。

SqlSession接口实现

SqlSessionManager对SqlSession接口的实现全部转交给其内部的sqlSessionProxy代理对象完成,没有什么处理逻辑,贴几个方法看下:

1publicvoidselect(Stringstatement,Objectparameter,RowBoundsrowBounds,ResultHandlerhandler){

2sqlSessionProxy.select(statement,parameter,rowBounds,handler);

3}

4

5publicintinsert(Stringstatement){

6returnsqlSessionProxy.insert(statement);

7}

8

9publicintupdate(Stringstatement){

10returnsqlSessionProxy.update(statement);

11}

12

13publicintdelete(Stringstatement){

14returnsqlSessionProxy.delete(statement);

15}

sqlSessionProxy其实是一个包装了DefaultSqlSession的SqlSessionInterceptor实例,所有方法会被SqlSessionInterceptor#invoke拦截,最终是由DefaultSqlSession来实现的(就是前面一节说的内容)。

前面“实例化”一节说invoke内有两种逻辑,那实际会走哪种逻辑呢?实际上,这引出了SqlSessionManager的两种使用方式,使用方式不同invoke的执行逻辑也不同。

一是:像使用DefaultSqlSession一样,按需创建,此时SqlSessionManager提供了自动关闭功能。

二是:启用SqlSession管理功能。此时SqlSessionManager会在同一线程内共享SqlSession,它是线程安全的。

小伙伴儿们可以尝试把示例代码中第9行关闭或打开注释作下调试,一试便知。

小结

SqlSessionManager虽然同时实现了SqlSession和SqlSessionFactory两个接口,但是真正“干活”的还是DefaultSqlSession和DefaultSqlSessionFactory;它使用动态代理对SqlSession的方法进行拦截,提供了SqlSession自动关闭或线程安全的解决方案。整体上看,SqlSessionManager是对安全、智能、高级封装版的DefaultSqlSession。

SqlSession总结

本文“啰里八嗦”的介绍了DefaultSqlSession和SqlSessionManager两者的区别与联系,重点关注了SqlSessionManager如何解决线程安全和自动关闭两个问题,从源码层面了解到SqlSessionManager其实是对DefaultSqlSession的高级封装。

我们还了解到,所有的SQL命令都会从SqlSession开始,然而SqlSession并没有直接操作数据库,脏活累活都交给了Executor,形象点说SqlSession其实是一个“包工头儿”。具体Executor如何做,且听下文讲解。

感谢您的阅读,如有问题欢迎讨论!也可以关注我的微信公众号:“兮一昂吧”。欢迎点赞、在看、转发。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。