搜索
首页Javajava教程java中insert方法、update方法、delete方法处理流程(下篇)

java中insert方法、update方法、delete方法处理流程(下篇)

Jun 25, 2017 am 10:02 AM
insertmybatis分析方法源码

Configuration的newStatementHandler分析

SimpleExecutor的doUpdate方法上文有分析过:

 1 public int doUpdate(MappedStatement ms, Object parameter) throws SQLException { 2     Statement stmt = null; 3     try { 4       Configuration configuration = ms.getConfiguration(); 5       StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null); 6       stmt = prepareStatement(handler, ms.getStatementLog()); 7       return handler.update(stmt); 8     } finally { 9       closeStatement(stmt);10     }11 }

这两天重看第5行的newStatementHandler方法的时候,发现方法上文在这个方法中分析地太简略了,这里过一遍一下Configuration的newStatementHandler方法,方法的实现为:

1 public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {2     StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);3     statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);4     return statementHandler;5 }

第3行的代码是加入插件没什么好看的,看下第2行的代码,StatementHandler接口真正实例化出来的是RoutingStatementHandler,构造方法实现为:

 1 public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { 2  3     switch (ms.getStatementType()) { 4       case STATEMENT: 5         delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); 6         break; 7       case PREPARED: 8         delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); 9         break;10       case CALLABLE:11         delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);12         break;13       default:14         throw new ExecutorException("Unknown statement type: " + ms.getStatementType());15     }16 17 }

RoutingStatementHandler同样是装饰器模式的实现,实现了StatementHandler接口并持有StatementHandler接口引用delegate。这里StatementType的为PREPARED,因此执行的第7行的判断,实例化出PreparedStatementHandler,实例化的过程为:

 1 protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { 2     this.configuration = mappedStatement.getConfiguration(); 3     this.executor = executor; 4     this.mappedStatement = mappedStatement; 5     this.rowBounds = rowBounds; 6  7     this.typeHandlerRegistry = configuration.getTypeHandlerRegistry(); 8     this.objectFactory = configuration.getObjectFactory(); 9 10     if (boundSql == null) { // issue #435, get the key before calculating the statement11       generateKeys(parameterObject);12       boundSql = mappedStatement.getBoundSql(parameterObject);13     }14 15     this.boundSql = boundSql;16 17     this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);18     this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);19 }

这里的重点是BoundSql,它可以通过MappedStatement获取到,BoundSql中存储了几个重要的内容:

  1. 参数对象本身

  2. 参数列表

  3. 待执行的SQL语句

有些基于MyBatis二次开发的框架通常都会拿到BoundSql中的SQL语句进行修改并重新设置进BoundSql中。

 

生成Statement

上文已经写了生成Connection的流程,本文继续看,首先还是再贴一下SimpleExecutor的prepareStatement方法:

1 private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {2     Statement stmt;3     Connection connection = getConnection(statementLog);4     stmt = handler.prepare(connection, transaction.getTimeout());5     handler.parameterize(stmt);6     return stmt;7 }

接着就是第4行的代码,生成Statement了,第4行的代码实现为:

  Statement prepare(Connection connection, Integer transactionTimeout)

delegate上文是装饰器模式中的被装饰角色,其接口类型为StatementHandler,真实类型为PreparedStatementHandler,这个在最开头的部分已经分析过了。看一下prepare方法实现:

 1 public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException { 2     ErrorContext.instance().sql(boundSql.getSql()); 3     Statement statement = null; 4     try { 5       statement = instantiateStatement(connection); 6       setStatementTimeout(statement, transactionTimeout); 7       setFetchSize(statement); 8       return statement; 9     } catch (SQLException e) {10       closeStatement(statement);11       throw e;12     } catch (Exception e) {13       closeStatement(statement);14       throw new ExecutorException("Error preparing statement.  Cause: " + e, e);15     }16 }

第6行的代码设置的是查询超时时间、第7行的代码设置的是接收的数据大小,就不跟进去看了,接着看下第6行的instantiateStatement方法实现:

 1 protected Statement instantiateStatement(Connection connection) throws SQLException { 2     String sql = boundSql.getSql(); 3     if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) { 4       String[] keyColumnNames = mappedStatement.getKeyColumns(); 5       if (keyColumnNames == null) { 6         return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS); 7       } else { 8         return connection.prepareStatement(sql, keyColumnNames); 9       }10     } else if (mappedStatement.getResultSetType() != null) {11       return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);12     } else {13       return connection.prepareStatement(sql);14     }15 }

第2行,从boundSql中获取真正的SQL语句,第一部分已经分析过了。拿到SQL语句之后,执行第3行与第5行的判断,这里就是我们熟悉的通过Connection拿Statement的代码了,通过prepareStatement方法获取到PreparedStatement,其真实类型为com.mysql.jdbc.JDBC4PreparedStatement,是PreparedStatement的子类。

 

Statement参数设置

获取了Statement后,下一步就是设置参数了,看一下设置参数的代码,还是回到SimpleExecutor的prepareStatement方法:

1 private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {2     Statement stmt;3     Connection connection = getConnection(statementLog);4     stmt = handler.prepare(connection, transaction.getTimeout());5     handler.parameterize(stmt);6     return stmt;7 }

跟第5行的代码:

 1 public void parameterize(Statement statement) throws SQLException { 2     parameterHandler.setParameters((PreparedStatement) statement); 3 }

继续跟第2行的代码:

 1 public void setParameters(PreparedStatement ps) { 2     ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId()); 3     List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); 4     if (parameterMappings != null) { 5       for (int i = 0; i < parameterMappings.size(); i++) { 6         ParameterMapping parameterMapping = parameterMappings.get(i); 7         if (parameterMapping.getMode() != ParameterMode.OUT) { 8           Object value; 9           String propertyName = parameterMapping.getProperty();10           if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params11             value = boundSql.getAdditionalParameter(propertyName);12           } else if (parameterObject == null) {13             value = null;14           } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {15             value = parameterObject;16           } else {17             MetaObject metaObject = configuration.newMetaObject(parameterObject);18             value = metaObject.getValue(propertyName);19           }20           TypeHandler typeHandler = parameterMapping.getTypeHandler();21           JdbcType jdbcType = parameterMapping.getJdbcType();22           if (value == null && jdbcType == null) {23             jdbcType = configuration.getJdbcTypeForNull();24           }25           try {26             typeHandler.setParameter(ps, i + 1, value, jdbcType);27           } catch (TypeException e) {28             throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);29           } catch (SQLException e) {30             throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);31           }32         }33       }34     }35 }

最终执行的是第26行的代码,从26行的代码我们可以知道,参数设置到最后都是通过参数的TypeHandler来执行的,JDBC为我们预定义了很多TypeHandler,比如int值的TypeHandler就是IntegerTypeHandler,当然我们也可以定义自己的TypeHandler,通常来说继承BaseTypeHandler就可以了。

但是在此之前,会获取到Statement(setParameters方法形参)、占位符位置号(for循环的遍历参数i)、参数值(通过属性名获取)与jdbcType(配置在配置文件中,默认为null),最终执行TypeHandler的setParameters方法,这是BaseTypeHandler中的一个方法:

 1 public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException { 2     if (parameter == null) { 3       if (jdbcType == null) { 4         throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters."); 5       } 6       try { 7         ps.setNull(i, jdbcType.TYPE_CODE); 8       } catch (SQLException e) { 9         throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " +10                 "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " +11                 "Cause: " + e, e);12       }13     } else {14       try {15         setNonNullParameter(ps, i, parameter, jdbcType);16       } catch (Exception e) {17         throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . " +18                 "Try setting a different JdbcType for this parameter or a different configuration property. " +19                 "Cause: " + e, e);20       }21     }22 }

这里的参数不为null,走13行的else,执行setNonNullParameter方法,这是IntegerTypeHandler中的一个方法:

   setNonNullParameter(PreparedStatement ps,

这里的代码就比较熟悉了,PreparedStatement的setInt方法。

执行更新操作并处理结果

最后一步,执行更新操作并对结果进行处理,回到SimpleExecuto的doUpdate方法:

 1 public int doUpdate(MappedStatement ms, Object parameter) throws SQLException { 2     Statement stmt = null; 3     try { 4       Configuration configuration = ms.getConfiguration(); 5       StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null); 6       stmt = prepareStatement(handler, ms.getStatementLog()); 7       return handler.update(stmt); 8     } finally { 9       closeStatement(stmt);10     }11 }

第6行已经准备好了Statement,第7行执行update操作并对结果进行处理并返回:

   update(Statement statement)

这里的委托delegate前面已经说过了,其真实类型是PreparedStatementHandler,update方法的实现为:

1 public int update(Statement statement) throws SQLException {2     PreparedStatement ps = (PreparedStatement) statement;3     ps.execute();4     int rows = ps.getUpdateCount();5     Object parameterObject = boundSql.getParameterObject();6     KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();7     keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);8     return rows;9 }

第3行的execute方法是PreparedStatement中的方法,execute方法执行操作,然后第4行通过getUpdateCount()方法获取本次操作更新了几条数据,作为最终的值返回给用户。

第5行的代码通过BoundSql获取参数对象,这里是MailDO对象,因为我们知道在插入场景下,开发者是有这种需求的,需要返回插入的主键id,此时会将主键id设置到MailDO中。

第6行的代码通过MappedStatement获取KeyGenerator,一个主键生成器。

第7行的代码做了一个操作完毕的后置处理:

<span style="color: #008080"> 1</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span><span style="color: #000000"> processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {</span><span style="color: #008080"> 2</span> <span style="color: #000000">    processBatch(ms, stmt, getParameters(parameter));</span><span style="color: #008080"> 3</span> <span style="color: #000000">}</span><span style="color: #008080"><br/></span>

首先将对象包装成集合类型,然后跟第2行的代码processBatch方法:

 1 public void processBatch(MappedStatement ms, Statement stmt, Collection<Object> parameters) { 2     ResultSet rs = null; 3     try { 4       rs = stmt.getGeneratedKeys(); 5       final Configuration configuration = ms.getConfiguration(); 6       final TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry(); 7       final String[] keyProperties = ms.getKeyProperties(); 8       final ResultSetMetaData rsmd = rs.getMetaData(); 9       TypeHandler<?>[] typeHandlers = null;10       if (keyProperties != null && rsmd.getColumnCount() >= keyProperties.length) {11         for (Object parameter : parameters) {12           // there should be one row for each statement (also one for each parameter)13           if (!rs.next()) {14             break;15           }16           final MetaObject metaParam = configuration.newMetaObject(parameter);17           if (typeHandlers == null) {18             typeHandlers = getTypeHandlers(typeHandlerRegistry, metaParam, keyProperties, rsmd);19           }20           populateKeys(rs, metaParam, keyProperties, typeHandlers);21         }22       }23     } catch (Exception e) {24       throw new ExecutorException("Error getting generated key or setting result to parameter object. Cause: " + e, e);25     } finally {26       if (rs != null) {27         try {28           rs.close();29         } catch (Exception e) {30           // ignore31         }32       }33     }34 }

简单说这里就是遍历集合,通过JDBC4PreparedStatement的getGeneratedKeys获取ResultSet,然后从ResultSet中使用getLong方法获取生成的主键,设置到MailDO中。完成整个操作。

最后,本文演示的是insert数据的update方法流程,前文已经说过insert、update、delete在MyBatis中都是一样的,因此update、delete也是一样的操作,这里就不再赘述了。

以上是java中insert方法、update方法、delete方法处理流程(下篇)的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
如何将Maven或Gradle用于高级Java项目管理,构建自动化和依赖性解决方案?如何将Maven或Gradle用于高级Java项目管理,构建自动化和依赖性解决方案?Mar 17, 2025 pm 05:46 PM

本文讨论了使用Maven和Gradle进行Java项目管理,构建自动化和依赖性解决方案,以比较其方法和优化策略。

如何使用适当的版本控制和依赖项管理创建和使用自定义Java库(JAR文件)?如何使用适当的版本控制和依赖项管理创建和使用自定义Java库(JAR文件)?Mar 17, 2025 pm 05:45 PM

本文使用Maven和Gradle之类的工具讨论了具有适当的版本控制和依赖关系管理的自定义Java库(JAR文件)的创建和使用。

如何使用咖啡因或Guava Cache等库在Java应用程序中实现多层缓存?如何使用咖啡因或Guava Cache等库在Java应用程序中实现多层缓存?Mar 17, 2025 pm 05:44 PM

本文讨论了使用咖啡因和Guava缓存在Java中实施多层缓存以提高应用程序性能。它涵盖设置,集成和绩效优势,以及配置和驱逐政策管理最佳PRA

如何将JPA(Java持久性API)用于具有高级功能(例如缓存和懒惰加载)的对象相关映射?如何将JPA(Java持久性API)用于具有高级功能(例如缓存和懒惰加载)的对象相关映射?Mar 17, 2025 pm 05:43 PM

本文讨论了使用JPA进行对象相关映射,并具有高级功能,例如缓存和懒惰加载。它涵盖了设置,实体映射和优化性能的最佳实践,同时突出潜在的陷阱。[159个字符]

Java的类负载机制如何起作用,包括不同的类载荷及其委托模型?Java的类负载机制如何起作用,包括不同的类载荷及其委托模型?Mar 17, 2025 pm 05:35 PM

Java的类上载涉及使用带有引导,扩展程序和应用程序类负载器的分层系统加载,链接和初始化类。父代授权模型确保首先加载核心类别,从而影响自定义类LOA

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
4 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
4 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
4 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.聊天命令以及如何使用它们
4 周前By尊渡假赌尊渡假赌尊渡假赌

热工具

适用于 Eclipse 的 SAP NetWeaver 服务器适配器

适用于 Eclipse 的 SAP NetWeaver 服务器适配器

将Eclipse与SAP NetWeaver应用服务器集成。

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

EditPlus 中文破解版

EditPlus 中文破解版

体积小,语法高亮,不支持代码提示功能

MinGW - 适用于 Windows 的极简 GNU

MinGW - 适用于 Windows 的极简 GNU

这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。