mybatis源码阅读(六) ---StatementHandler了解一下

转载自  mybatis源码阅读(六) ---StatementHandler了解一下

StatementHandler类结构图与接口设计

BaseStatementHandler:一个抽象类,只是实现了一些不涉及具体操作的方法

RoutingStatementHandler:类似路由器,根据配置文件来路由选择具体实现类SimpleStatementHandler、CallableStatementHandler和PreparedStatementHandler

SimpleStatementHandler:就是直接使用普通的Statement对象,这样每次执行SQL语句都需要数据库对SQL进行预编译

PrepareStatementHandler:使用PrepareStatement执行,虽然初次创建PrepareStatement时开销比较大,但在多次处理SQL时只需要初始化一次,可以有效提高性能

CallableStatementHandler:使用CallableStatement执行,CallableStatement是用来执行存储过程的。

在每个mapper节点可以设置statementType决定是否使用谁 ,如下

<!-- statementType (可选配置,默认配置为PREPARED)STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 -->
<select id="findUserById" resultType="com.lpf.entity.User" statementType="PREPARED">select * from m_user where id = #{id}
</select>

1. StatementHandler

public interface StatementHandler {// 从连接中获取一个Statement对象Statement prepare(Connection connection, Integer transactionTimeout)throws SQLException;// 绑定Statement执行是所需要的参数void parameterize(Statement statement)throws SQLException;// 批量执行SQLvoid batch(Statement statement)throws SQLException;// 执行update,delete,insert语句int update(Statement statement)throws SQLException;// 执行select语句<E> List<E> query(Statement statement, ResultHandler resultHandler)throws SQLException;<E> Cursor<E> queryCursor(Statement statement)throws SQLException;BoundSql getBoundSql();// 获取封装的ParameterterHandler对象ParameterHandler getParameterHandler();}

2. BaseStatementHandler

BaseStatementHandler是一个抽象类,和Executor一样使用了模板设计方法。代码如下:

public abstract class BaseStatementHandler implements StatementHandler {protected final Configuration configuration;protected final ObjectFactory objectFactory;protected final TypeHandlerRegistry typeHandlerRegistry;//将结果映射成结果对象protected final ResultSetHandler resultSetHandler;// 使用传入的实参替换SQL语句中的?protected final ParameterHandler parameterHandler;// 记录执行sql的executorprotected final Executor executor;protected final MappedStatement mappedStatement;// 分页用到protected final RowBounds rowBounds;protected BoundSql boundSql;protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {this.configuration = mappedStatement.getConfiguration();this.executor = executor;this.mappedStatement = mappedStatement;this.rowBounds = rowBounds;this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();this.objectFactory = configuration.getObjectFactory();if (boundSql == null) { // issue #435, get the key before calculating the statement// 获取主键generateKeys(parameterObject);boundSql = mappedStatement.getBoundSql(parameterObject);}this.boundSql = boundSql;this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);}@Overridepublic BoundSql getBoundSql() {return boundSql;}@Overridepublic ParameterHandler getParameterHandler() {return parameterHandler;}@Overridepublic Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {ErrorContext.instance().sql(boundSql.getSql());Statement statement = null;try {// 初始化Statement对象 instantiateStatement由具体的子类对象实现statement = instantiateStatement(connection);// 设置超时时间setStatementTimeout(statement, transactionTimeout);setFetchSize(statement);return statement;} catch (SQLException e) {closeStatement(statement);throw e;} catch (Exception e) {closeStatement(statement);throw new ExecutorException("Error preparing statement.  Cause: " + e, e);}}protected abstract Statement instantiateStatement(Connection connection) throws SQLException;protected void setStatementTimeout(Statement stmt, Integer transactionTimeout) throws SQLException {Integer queryTimeout = null;if (mappedStatement.getTimeout() != null) {queryTimeout = mappedStatement.getTimeout();} else if (configuration.getDefaultStatementTimeout() != null) {queryTimeout = configuration.getDefaultStatementTimeout();}if (queryTimeout != null) {stmt.setQueryTimeout(queryTimeout);}StatementUtil.applyTransactionTimeout(stmt, queryTimeout, transactionTimeout);}protected void setFetchSize(Statement stmt) throws SQLException {Integer fetchSize = mappedStatement.getFetchSize();if (fetchSize != null) {stmt.setFetchSize(fetchSize);return;}Integer defaultFetchSize = configuration.getDefaultFetchSize();if (defaultFetchSize != null) {stmt.setFetchSize(defaultFetchSize);}}protected void closeStatement(Statement statement) {try {if (statement != null) {statement.close();}} catch (SQLException e) {//ignore}}protected void generateKeys(Object parameter) {KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();ErrorContext.instance().store();keyGenerator.processBefore(executor, mappedStatement, null, parameter);ErrorContext.instance().recall();}}

2.1 ParameterHandler

在BaseStatementHandler中有一个对象叫ParameterHandler是用来设置参数规则的,当StatementHandler调用prepare方法之后,接下来就是调用它来进行设置参数。

public interface ParameterHandler {Object getParameterObject();void setParameters(PreparedStatement ps)throws SQLException;}

getParameterObject是用来获取参数的,setParameters(PreparedStatement ps)是用来设置参数的,相当于对sql中所有的参数都执行ps.setXXX(value);

ParameterHandler的默认实现类是DefaultParameterHandler,其实现了接口中定义的两个方法。

getParameterObject是获取参数,这个参数值就是你传递进来的值,可能是个实体、map或单个基本类型数据。

@Override
public void setParameters(PreparedStatement ps) {ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());// 去除SQL中的参数映射列表List<ParameterMapping> 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 {// 获取对象中相应的属性值或查找map对象中的值MetaObject metaObject = configuration.newMetaObject(parameterObject);value = metaObject.getValue(propertyName);}//获取参数值对应的jdbc类型TypeHandler typeHandler = parameterMapping.getTypeHandler();JdbcType jdbcType = parameterMapping.getJdbcType();if (value == null && jdbcType == null) {jdbcType = configuration.getJdbcTypeForNull();}try {//设置参数值和jdbc类型的对应关系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);}}}}
}

3. RoutingStatementHandler

/*** RoutingStatementHandler的主要功能就是根据mapper文件中的Statement的配置,生成一个对应的StatementHandler* 可以是 SimpleStatementHandler,PreparedStatementHandler,CallableStatementHandler* 此类中的所有方法都是通过调用delegate对象的对应方法实现的*/
public class RoutingStatementHandler implements StatementHandler {private final StatementHandler delegate;public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {switch (ms.getStatementType()) {case STATEMENT:delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);break;case PREPARED:delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);break;case CALLABLE:delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);break;default:throw new ExecutorException("Unknown statement type: " + ms.getStatementType());}}
}

4. SimpleStatementHandler

SimpleStatementHandler就是使用基本的Statement来执行query、batch、update等操作,其实现还是比较简单的,SQL语句中是没有占位符的,所以相应 的paramterize()方法是空实现。

public class SimpleStatementHandler extends BaseStatementHandler {public SimpleStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);}@Overridepublic int update(Statement statement) throws SQLException {String sql = boundSql.getSql();Object parameterObject = boundSql.getParameterObject();KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();int rows;if (keyGenerator instanceof Jdbc3KeyGenerator) {// 生成主键statement.execute(sql, Statement.RETURN_GENERATED_KEYS);rows = statement.getUpdateCount();keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);} else if (keyGenerator instanceof SelectKeyGenerator) {statement.execute(sql);rows = statement.getUpdateCount();// 生成主键  keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);} else {statement.execute(sql);rows = statement.getUpdateCount();}return rows;}@Overridepublic void batch(Statement statement) throws SQLException {String sql = boundSql.getSql();statement.addBatch(sql);}@Overridepublic <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {String sql = boundSql.getSql();statement.execute(sql);return resultSetHandler.<E>handleResultSets(statement);}@Overridepublic <E> Cursor<E> queryCursor(Statement statement) throws SQLException {String sql = boundSql.getSql();statement.execute(sql);return resultSetHandler.<E>handleCursorResultSets(statement);}@Overrideprotected Statement instantiateStatement(Connection connection) throws SQLException {if (mappedStatement.getResultSetType() != null) {return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);} else {return connection.createStatement();}}@Overridepublic void parameterize(Statement statement) throws SQLException {// N/A}}

5. PreparedStatementHandler

PreparedStatementHandler底层依赖PreparedStatement对象来完成数据库的相关操作,在调用parameterize()方法完成SQL语句的参数绑定,代码也比较简单。

public class PreparedStatementHandler extends BaseStatementHandler {public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);}@Overridepublic int update(Statement statement) throws SQLException {PreparedStatement ps = (PreparedStatement) statement;ps.execute();int rows = ps.getUpdateCount();Object parameterObject = boundSql.getParameterObject();KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);return rows;}@Overridepublic void batch(Statement statement) throws SQLException {PreparedStatement ps = (PreparedStatement) statement;ps.addBatch();}@Overridepublic <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {PreparedStatement ps = (PreparedStatement) statement;ps.execute();return resultSetHandler.<E> handleResultSets(ps);}@Overridepublic <E> Cursor<E> queryCursor(Statement statement) throws SQLException {PreparedStatement ps = (PreparedStatement) statement;ps.execute();return resultSetHandler.<E> handleCursorResultSets(ps);}@Overrideprotected Statement instantiateStatement(Connection connection) throws SQLException {String sql = boundSql.getSql();if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {String[] keyColumnNames = mappedStatement.getKeyColumns();if (keyColumnNames == null) {return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);} else {return connection.prepareStatement(sql, keyColumnNames);}} else if (mappedStatement.getResultSetType() != null) {return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);} else {return connection.prepareStatement(sql);}}@Overridepublic void parameterize(Statement statement) throws SQLException {parameterHandler.setParameters((PreparedStatement) statement);}}

6. CallableStatementHandler

CallableStatementHandler实际就是使用CallableStatement来执行SQL语句,当然它执行的是存储过程。

public class CallableStatementHandler extends BaseStatementHandler {public CallableStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);}@Overridepublic int update(Statement statement) throws SQLException {//用来调用存储过程,它提供了对输出和输入/输出参数的支持CallableStatement cs = (CallableStatement) statement;cs.execute();int rows = cs.getUpdateCount();Object parameterObject = boundSql.getParameterObject();KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();keyGenerator.processAfter(executor, mappedStatement, cs, parameterObject);resultSetHandler.handleOutputParameters(cs);return rows;}@Overridepublic void batch(Statement statement) throws SQLException {CallableStatement cs = (CallableStatement) statement;cs.addBatch();}@Overridepublic <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {CallableStatement cs = (CallableStatement) statement;cs.execute();List<E> resultList = resultSetHandler.<E>handleResultSets(cs);resultSetHandler.handleOutputParameters(cs);return resultList;}@Overridepublic <E> Cursor<E> queryCursor(Statement statement) throws SQLException {CallableStatement cs = (CallableStatement) statement;cs.execute();Cursor<E> resultList = resultSetHandler.<E>handleCursorResultSets(cs);resultSetHandler.handleOutputParameters(cs);return resultList;}@Overrideprotected Statement instantiateStatement(Connection connection) throws SQLException {String sql = boundSql.getSql();if (mappedStatement.getResultSetType() != null) {return connection.prepareCall(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);} else {return connection.prepareCall(sql);}}@Overridepublic void parameterize(Statement statement) throws SQLException {//注册out参数registerOutputParameters((CallableStatement) statement);parameterHandler.setParameters((CallableStatement) statement);}private void registerOutputParameters(CallableStatement cs) throws SQLException {List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();for (int i = 0, n = parameterMappings.size(); i < n; i++) {ParameterMapping parameterMapping = parameterMappings.get(i);//处理存储过程的INOUT和OUT  if (parameterMapping.getMode() == ParameterMode.OUT || parameterMapping.getMode() == ParameterMode.INOUT) {if (null == parameterMapping.getJdbcType()) {throw new ExecutorException("The JDBC Type must be specified for output parameter.  Parameter: " + parameterMapping.getProperty());} else {if (parameterMapping.getNumericScale() != null && (parameterMapping.getJdbcType() == JdbcType.NUMERIC || parameterMapping.getJdbcType() == JdbcType.DECIMAL)) {cs.registerOutParameter(i + 1, parameterMapping.getJdbcType().TYPE_CODE, parameterMapping.getNumericScale());} else {if (parameterMapping.getJdbcTypeName() == null) {cs.registerOutParameter(i + 1, parameterMapping.getJdbcType().TYPE_CODE);} else {cs.registerOutParameter(i + 1, parameterMapping.getJdbcType().TYPE_CODE, parameterMapping.getJdbcTypeName());}}}}}}}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/322736.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于OIDC(OpenID Connect)的SSO(纯JS客户端)

在上一篇基于OIDC的SSO的中涉及到了4个Web站点&#xff1a; oidc-server.dev&#xff1a;利用oidc实现的统一认证和授权中心&#xff0c;SSO站点。 oidc-client-hybrid.dev&#xff1a;oidc的一个客户端&#xff0c;采用hybrid模式。 oidc-client-implicit.dev&#xff1a;od…

IIS中的 Asp.Net Core 和 dotnet watch

在基于传统的.NET Framework的Asp.Net Mvc的时候&#xff0c;本地开发环境中可以在IIS中建立一个站点&#xff0c;可以直接把站点的目录指向asp.net mvc的项目的根目录。然后build一下就可以在浏览器里面刷新到最新的修改了&#xff0c;也可以附加到w3wp的进程进行调试。但是在…

foreach方法使用

用法 foreach方法主要是针对数组而言的&#xff0c;对数组中的每个元素可以执行一次方法 var array [a, b, c, e]; array.forEach((a)> {console.log(a); });属性 foreach方法主要有三个参数&#xff0c;分别是数组内容、数组索引、整个数组 var array [a, b, c, e]; arra…

.NET Core 控制台程序读 appsettings.json 、注依赖、配日志、设 IOptions

.NET Core 控制台程序没有 ASP.NET Core 的 IWebHostBuilder 与 Startup.cs &#xff0c;那要读 appsettings.json、注依赖、配日志、设 IOptions 该怎么办呢&#xff1f;因为这些操作与 ASP.NET Core 无依赖&#xff0c;所以可以自己动手&#xff0c;轻松搞定。 1、读 appsett…

Object.keys方法拿到对象的key值

项目中的高级搜索选项用到了Object.keys方法&#xff0c; 那么它是用来干嘛的呢&#xff1a;删除某个子对象里的数据 var anObj { aaa: kejin,bbb: shenxian,ccc: yuanshan };let params {...anObj,ddd: luanwu } console.log(params) console.log(Object.keys(anObj)); // …

jsdiff 比较文本内容差异

翻译自 jsdiff JavaScript文本内容差异实现。 基于"An O(ND) Difference Algorithm and its Variations" (Myers, 1986) 中提出的算法 。 安装 npm install diff --save API Diff.diffChars(oldStr, newStr[, options]) -区分两个文本块&#xff0c;逐字符比较。…

Net Core下多种ORM框架特性及性能对比

在.NET Framework下有许多ORM框架&#xff0c;最著名的无外乎是Entity Framework&#xff0c;它拥有悠久的历史以及便捷的语法&#xff0c;在占有率上一路领先。但随着Dapper的出现&#xff0c;它的地位受到了威胁&#xff0c;本文对比了在.NET Core下 两种框架的表现以及与原生…

从ThoughtWorks 2017技术雷达看微软技术

ThoughtWorks在每年都会出品两期技术雷达&#xff0c;这是一份关于技术趋势的报告&#xff0c;它比起一些我们能在市面上见到的其他各种技术行情和预测报告&#xff0c;更加具体&#xff0c;更具可操作性&#xff0c;因为它不仅涉及到新技术大趋势&#xff0c;比如云平台和大数…

Spark入门(一)单主standalone安装

一、集群安装条件前置 实验spark安装在【Hadoop入门&#xff08;二&#xff09;集群安装】机器上&#xff0c; 已完成安装jdk,hadoop和ssh、网络等配置环境等。 spark所依赖的虚拟机和操作系统配置 环境&#xff1a;ubuntu14 spark-2.4.4-bin-hadoop2.6jdk1.8ssh 虚拟机&a…

AutoMapper在asp.netcore中的使用

automapper 是.net 项目中针对模型之间转换映射的一个很好用的工具&#xff0c;不仅提高了开发的效率还使代码更加简洁&#xff0c;当然也是开源的&#xff0c;https://github.com/AutoMapper&#xff0c;这不多做介绍&#xff0c;详细看&#xff0c;官网下面是介绍它在 .net c…

Hadoop生态Zookeeper安装

一、安装条件前置 实验zookeeper安装在【Hadoop入门&#xff08;二&#xff09;集群安装】机器上&#xff0c;已完成安装jdk,hadoop和ssh配置环境等。 zookeeper所依赖的虚拟机和操作系统配置 环境&#xff1a;ubuntu14 apache-zookeeper-3.5.6-bin.tar jdk1.8ssh 虚拟机…

Hangfire在ASP.NET CORE中的简单实现

hangfire是执行后台任务的利器&#xff0c;具体请看官网介绍&#xff1a;https://www.hangfire.io/ 新建一个asp.net core mvc 项目 引入nuget包 Hangfire.AspNetCore hangfire的任务需要数据库持久化&#xff0c;我们在Startup类中修改ConfigureServices 然后在Configure方法中…

Spark入门(二)多主standalone安装

一、集群安装条件前置 实验spark安装在【Hadoop生态Zookeeper安装】机器上&#xff0c; 已完成安装zookeeper、jdk、hadoop和ssh、网络等配置环境等。 spark所依赖的虚拟机和操作系统配置 环境&#xff1a;ubuntu14 spark-2.4.4-bin-hadoop2.6 apache-zookeeper-3.5.6 jd…

Ocelot——初识基于.Net Core的API网关

前言 前不久看到一篇《.NET Core 在腾讯财付通的企业级应用开发实践》&#xff0c;给现在研究.Net Core及想往微服务方向发展的人来了一剂强心针。于是我也就立刻去下Ocelot的源码及去阅读官方文档。 Ocelot的Github地址&#xff1a;https://github.com/TomPallister/Ocelot 官…

forEach和for in

如果是对数组本身进行操作&#xff0c;则必然要用到for循环&#xff0c;如果是依赖数组做其他东西的操作&#xff0c;则可以用 forEach 最经典的两种操作 1、批量获取数组中的单个元素 let _selectSns [];this.multipleSelection.forEach(item > {_selectSns.push(item.sn…

Spark入门(三)Idea构建spark项目

一、依赖包配置 scala与spark的相关依赖包&#xff0c;spark包后尾下划线的版本数字要跟scala的版本第一二位要一致&#xff0c;即2.11 pom.xml <?xml version"1.0" encoding"UTF-8"?><project xmlns"http://maven.apache.org/POM/4.0.…

VS Tools for AI全攻略

大家都知道微软在Connect();17大会上发布了VS Tools for AI&#xff0c;旨在提升Visual Studio和VSCode对日益增长的深度学习需求的体验。看了一圈&#xff0c;网上似乎没有一个完整的中文教程来教你怎么完整配置深度学习方案。 与此同时我也接了一个活&#xff0c;是俱乐部与M…

Spark入门(四)Idea远程提交项目到spark集群

一、依赖包配置 scala与spark的相关依赖包&#xff0c;spark包后尾下划线的版本数字要跟scala的版本第一二位要一致&#xff0c;即2.11 pom.xml <?xml version"1.0" encoding"UTF-8"?><project xmlns"http://maven.apache.org/POM/4.0.…

关于负载均衡的一切:总结与思考

正文 古人云&#xff0c;不患寡而患不均。 在计算机的世界&#xff0c;这就是大家耳熟能详的负载均衡&#xff08;load balancing&#xff09;&#xff0c;所谓负载均衡&#xff0c;就是说如果一组计算机节点&#xff08;或者一组进程&#xff09;提供相同的&#xff08;同质的…

Spark入门(五)Spark SQL shell启动方式(元数据存储在derby)

一、spark-sql shell介绍 Spark sql是以hive SQL提交spark任务到spark集群执行。 由于spark是计算框架没有存储功能&#xff0c;所有spark sql数据表映射关系存储在运行shell的当前目录下metastore_db目录里面&#xff08;spark默认使用derby数据库创建的本地存储&#xff0c…