session mysql登录实现原理_Mybatis的SqlSession运行原理

前言

SqlSession是Mybatis最重要的构建之一,可以简单的认为Mybatis一系列的配置目的是生成类似 JDBC生成的Connection对象的SqlSession对象,这样才能与数据库开启“沟通”,通过SqlSession可以实现增删改查(当然现在更加推荐是使用Mapper接口形式),那么它是如何执行实现的,这就是本篇博文所介绍的东西,其中会涉及到简单的源码讲解。

了解SqlSession的运作原理是学习Mybatis插件的必经之路,因为Mybatis的插件会在SqlSession运行过程中“插入”运行,如果没有很好理解的话,Mybatis插件可能会覆盖相应的源码造成严重的问题。鉴于此,本篇博文尽量详细介绍SqlSession运作原理!

建议:在我之前的博文《Mybatis缓存(1)--------系统缓存及简单配置介绍》中介绍到SqlSession的产生过程,可以先理解后再读此博文可能会更加好理解!

注:本篇博文也是我最近真正理解Mybatis才开始编写的,可能有些地方不太准确,如果有错误之处敬请指出,另外创作不易,望转载告之,谢谢!

参数资料:《深入浅出Mybatis基础原理与实践》(我这里只有电子版PDF,需要的朋友可以联系我)

1、SqlSession简单介绍

(1)SqlSession简单原理介绍

SqlSession提供select/insert/update/delete方法,在旧版本中使用使用SqlSession接口的这些方法,但是新版的Mybatis中就会建议使用Mapper接口的方法。

映射器其实就是一个动态代理对象,进入到MapperMethod的execute方法就能简单找到SqlSession的删除、更新、查询、选择方法,从底层实现来说:通过动态代理技术,让接口跑起来,之后采用命令模式,最后还是采用了SqlSession的接口方法(getMapper()方法等到Mapper)执行SQL查询(也就是说Mapper接口方法的实现底层还是采用SqlSession接口方法实现的)。

注:以上虽然只是简单的描述,但实际上源码相对复杂,下面将结合源码进行简单的介绍!

(2)SqlSession重要的四个对象

1)Execute:调度执行StatementHandler、ParmmeterHandler、ResultHandler执行相应的SQL语句;

2)StatementHandler:使用数据库中Statement(PrepareStatement)执行操作,即底层是封装好了的prepareStatement;

3)ParammeterHandler:处理SQL参数;

4)ResultHandler:结果集ResultSet封装处理返回。

2、SqlSession四大对象

(1)Execute执行器:

执行器起到至关重要的作用,它是真正执行Java与数据库交互的东西,参与了整个SQL查询执行过程中。

1)主要有三种执行器:简易执行器SIMPLE(不配置就是默认执行器)、REUSE是一种重用预处理语句、BATCH批量更新、批量专用处理器

packageorg.apache.ibatis.session;/***@authorClinton Begin*/

public enumExecutorType {

SIMPLE, REUSE, BATCH

}

2)执行器作用:Executor会先调用StatementHandler的prepare()方法预编译SQL语句,同时设置一些基本的运行参数,然后调用StatementHandler的parameterize()方法(实际上是启用了ParameterHandler设置参数)设置参数,resultHandler再组装查询结果返回调用者完成一次查询完成预编译,简单总结起来就是即先预编译SQL语句,之后设置参数(跟JDBC的prepareStatement过程类似)最后如果有查询结果就会组装返回。

首先,以SimpleExecutor为例,查看源码我们得到如下几点重要知识点:

第一:Executor通过Configuration对象中newExecutor()方法中选择相应的执行器生成

publicExecutor newExecutor(Transaction transaction, ExecutorType executorType) {

executorType= executorType == null ?defaultExecutorType : executorType;

executorType= executorType == null ?ExecutorType.SIMPLE : executorType;

Executor executor;if (ExecutorType.BATCH ==executorType) {

executor= new BatchExecutor(this, transaction);

}else if (ExecutorType.REUSE ==executorType) {

executor= new ReuseExecutor(this, transaction);

}else{

executor= new SimpleExecutor(this, transaction);

}if(cacheEnabled) {

executor= newCachingExecutor(executor);

}

executor=(Executor) interceptorChain.pluginAll(executor);returnexecutor;

}

(注:最后interceptorChain.pluginAll()中执行层层动态代理,最后在可以在调用真正的Executor前可以修改插件代码,这也就是为什么学会Mybatis的插件必须要知道SqlSession的运行过程)

第二:在执行器中StatementHandler是根据Configuration构建的

publicSimpleExecutor(Configuration configuration, Transaction transaction) {super(configuration, transaction);

}

@Overridepublic int doUpdate(MappedStatement ms, Object parameter) throwsSQLException {

Statement stmt= null;try{

Configuration configuration=ms.getConfiguration();

StatementHandler handler= configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);

stmt=prepareStatement(handler, ms.getStatementLog());returnhandler.update(stmt);

}finally{

closeStatement(stmt);

}

}

第三:Executor会执行StatementHandler的prepare()方法进行预编译---->填入connection对象等参数---->再调用parameterize()方法设置参数---->完成预编译

private Statement prepareStatement(StatementHandler handler, Log statementLog) throwsSQLException {

Statement stmt;

Connection connection=getConnection(statementLog);

stmt=handler.prepare(connection, transaction.getTimeout());

handler.parameterize(stmt);returnstmt;

}

总结以上绘制简单思维图如下:

b47a4720c18b973e1c33cc7fce1e0934.png

(2)StatementHanlder数据库会话器

1)作用:简单来说就是专门处理数据库会话。详细来说就是进行预编译并且调用ParameterHandler的setParameters()方法设置参数。

2)数据库会话器主要有三种:SimpleStatementHandler、PrepareStatementHandler、CallableStatementHandler,分别对应Executor的三种执行器(SIMPLE、REUSE、BATCH)

我们从上述Executor的prepareStatement()方法中调用了StatementHandler的parameterize()开始一步步地查看源码,如下得到几点重要的知识点:

第一:StatementHandler的生成是由Configuration方法中newStatementHandler()方法生成的,但是正在创建的是实现了StatementHandler接口的RoutingStatementHandler对象

publicStatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject,

RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

StatementHandler statementHandler= newRoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);

statementHandler=(StatementHandler) interceptorChain.pluginAll(statementHandler);returnstatementHandler;

}

第二:RoutingStatementHandler的通过适配器模式找到对应(根据上下文)的StatementHandler执行的,并且有SimpleStatementHandler、PrepareStatementHandler、CallableStatementHandler,分别对应Executor的三种执行器(SIMPLE、REUSE、BATCH)

publicRoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {switch(ms.getStatementType()) {caseSTATEMENT:

delegate= newSimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);break;casePREPARED:

delegate= newPreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);break;caseCALLABLE:

delegate= newCallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);break;default:throw new ExecutorException("Unknown statement type: " +ms.getStatementType());

}

之后主要以PrepareStatementHandler为例,我们观察到:它是实现BaseStatementHandler接口的,最后BaseStatementHandler又是实现StatementHandler接口的

public class PreparedStatementHandler extendsBaseStatementHandler

......public abstract class BaseStatementHandler implements StatementHandler

它主要有三种方法:prepare、parameterize和query,我们查看源码:

第三:在BaseStatementHandler中重写prepare()方法,instantiateStatement()方法完成预编译,之后设置一些基础配置(获取最大行数,超时)

@Overridepublic Statement prepare(Connection connection, Integer transactionTimeout) throwsSQLException {

ErrorContext.instance().sql(boundSql.getSql());

Statement statement= null;try{

statement=instantiateStatement(connection);

setStatementTimeout(statement, transactionTimeout);

setFetchSize(statement);returnstatement;

}catch(SQLException e) {

closeStatement(statement);throwe;

}catch(Exception e) {

closeStatement(statement);throw new ExecutorException("Error preparing statement. Cause: " +e, e);

}

}

第四:instantiateStatement()预编译实际上也是使用了JDBC的prepareStatement()完成预编译

@Overrideprotected Statement instantiateStatement(Connection connection) throwsSQLException {

String sql=boundSql.getSql();if (mappedStatement.getKeyGenerator() instanceofJdbc3KeyGenerator) {

String[] keyColumnNames=mappedStatement.getKeyColumns();if (keyColumnNames == null) {returnconnection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);

}else{returnconnection.prepareStatement(sql, keyColumnNames);

}

}else if (mappedStatement.getResultSetType() != null) {returnconnection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);

}else{returnconnection.prepareStatement(sql);

}

}

第五:在prepareStatement中重写parameterize()方法。prepare()预编译完成之后,Executor会调用parameterize()方法(在上面的Executor部分中已经做了介绍),实际上是调用ParameterHandler的setParameters()方法

@Overridepublic void parameterize(Statement statement) throwsSQLException {

parameterHandler.setParameters((PreparedStatement) statement);

}

(3)ParameterHandler参数处理器

作用:对预编译中参数进行设置,如果有配置typeHandler,自然会对注册的typeHandler对参数进行处理

查看并学习源码,得到以下几点重要知识点:

第一:Mybatis提供了ParamterHandler的默认实现类DefalutParameterHandler

4a8bbedbd72612529bbd7a3af58d6f04.png

public interfaceParameterHandler {

Object getParameterObject();voidsetParameters(PreparedStatement ps)throwsSQLException;

}

(其中:getParameterObject是返回参数对象,setParameters()是设置预编译参数)

第二:从parameterObject中取到参数,然后使用typeHandler(注册在Configuration中)进行参数处理:

@Overridepublic voidsetParameters(PreparedStatement ps) {

ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());

List parameterMappings =boundSql.getParameterMappings();if (parameterMappings != null) {for (int i = 0; i < parameterMappings.size(); i++) {

ParameterMapping parameterMapping=parameterMappings.get(i);if (parameterMapping.getMode() !=ParameterMode.OUT) {

Object value;

String propertyName=parameterMapping.getProperty();if (boundSql.hasAdditionalParameter(propertyName)) { //issue #448 ask first for additional params

value =boundSql.getAdditionalParameter(propertyName);

}else if (parameterObject == null) {

value= null;

}else if(typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {

value=parameterObject;

}else{

MetaObject metaObject=configuration.newMetaObject(parameterObject);

value=metaObject.getValue(propertyName);

}

TypeHandler typeHandler=parameterMapping.getTypeHandler();

JdbcType jdbcType=parameterMapping.getJdbcType();if (value == null && jdbcType == null) {

jdbcType=configuration.getJdbcTypeForNull();

}try{

typeHandler.setParameter(ps, i+ 1, value, jdbcType);

}catch(TypeException e) {throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " +e, e);

}catch(SQLException e) {throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " +e, e);

}

}

}

}

}

(4)ResultSetHandler结果集处理器

作用:很简单,就是组装结果返回结果集

第一:ResultSetHandler接口,handlerResultSets()是包装并返回结果集的,handleOutputParameters()是处理存储过程输出参数的

public interfaceResultSetHandler { List handleResultSets(Statement stmt) throwsSQLException; Cursor handleCursorResultSets(Statement stmt) throwsSQLException;void handleOutputParameters(CallableStatement cs) throwsSQLException;

第二:Mybatis提供了默认的ResultSetHandler实现类DefaultResultSetHandler,其中重点是handlerResultSets()的实现,但是其实现过程比较复杂,这里不过多介绍(emmmmm....个人目前能力还达理解,仍需努力)

2d922f50aa928b6802eb9581777db67e.png

第三:在Executor中doQuery()方法返回了封装的结果集

@Overridepublic List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throwsSQLException {

Statement stmt= null;try{

Configuration configuration=ms.getConfiguration();

StatementHandler handler=configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);

stmt=prepareStatement(handler, ms.getStatementLog());return handler.query(stmt, resultHandler);

}finally{

closeStatement(stmt);

}

}

第四:实际上是返回结果是调用了resultSetHandler的handleResultSets()方法

@Overridepublic List query(Statement statement, ResultHandler resultHandler) throwsSQLException {

PreparedStatement ps=(PreparedStatement) statement;

ps.execute();return resultSetHandler.handleResultSets(ps);

}

3、SqlSession运行总结

(1)文字总结

SqlSession的运行主要是依靠Executor执行器调用(调度)StatementHandler、parameterHanlder、ResultSetHandler,Executor首先通过创建StamentHandler执行预编译并设置参数运行,而整个过程需要如下几步才能完成:

private Statement prepareStatement(StatementHandler handler, Log statementLog) throwsSQLException {

Statement stmt;

Connection connection=getConnection(statementLog);

stmt=handler.prepare(connection, transaction.getTimeout());

handler.parameterize(stmt);returnstmt;

}

1)prepare预编译SQL

由适配模式生成的RoutingStatementHandler根据上下文选择生成三种相应的XXXStatementHandler;

在生成的XXXStatementHandler内部instantiateStatement()方法执行底层JDBC的prepareStatement()方法完成预编译

2)parameterize设置参数

默认是DefaultParameterHandler(实现了parameterHandler接口)中setParameter()方法完成参数配置,其中参数从ParameterObject中取出,交给typeHandler处理

3)doUpdate/doQuery执行SQL

返回的结果通过默认的DefaultResultSetHandler(实现了ResultSetHandler接口)封装

(2)运行图总结

1)SqlSession内部总运行图

1c728abb9d8180ab62d468eb822b6564.png

2)prepare()方法运行图:                                  3)parameterize()方法运行图

f92c3b3c8f85bd0c6b4e3e0cc215d799.png    

e80ec7b6ef90d988c442e68af1cd8373.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值