mysql 处理update源码_【MyBatis源码分析】insert方法、update方法、delete方法处理流程(下篇)...

Configuration的newStatementHandler分析

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

1 public int doUpdate(MappedStatement ms, Object parameter) throwsSQLException {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 returnhandler.update(stmt);8 } finally{9 closeStatement(stmt);10 }11 }

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

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

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

1 publicRoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {2

3 switch(ms.getStatementType()) {4 caseSTATEMENT:5 delegate = newSimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);6 break;7 casePREPARED:8 delegate = newPreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);9 break;10 caseCALLABLE:11 delegate = newCallableStatementHandler(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 protectedBaseStatementHandler(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 statement

11 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中存储了几个重要的内容:

参数对象本身

参数列表

待执行的SQL语句

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

生成Statement

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

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

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

1 public Statement prepare(Connection connection, Integer transactionTimeout) throwsSQLException {2 returndelegate.prepare(connection, transactionTimeout);3 }

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

1 public Statement prepare(Connection connection, Integer transactionTimeout) throwsSQLException {2 ErrorContext.instance().sql(boundSql.getSql());3 Statement statement = null;4 try{5 statement =instantiateStatement(connection);6 setStatementTimeout(statement, transactionTimeout);7 setFetchSize(statement);8 returnstatement;9 } catch(SQLException e) {10 closeStatement(statement);11 throwe;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) throwsSQLException {2 String sql =boundSql.getSql();3 if (mappedStatement.getKeyGenerator() instanceofJdbc3KeyGenerator) {4 String[] keyColumnNames =mappedStatement.getKeyColumns();5 if (keyColumnNames == null) {6 returnconnection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);7 } else{8 returnconnection.prepareStatement(sql, keyColumnNames);9 }10 } else if (mappedStatement.getResultSetType() != null) {11 returnconnection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);12 } else{13 returnconnection.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) throwsSQLException {2 Statement stmt;3 Connection connection =getConnection(statementLog);4 stmt =handler.prepare(connection, transaction.getTimeout());5 handler.parameterize(stmt);6 returnstmt;7 }

跟第5行的代码:

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

继续跟第2行的代码:

1 public voidsetParameters(PreparedStatement ps) {2 ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());3 List 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 params

11 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) throwsSQLException {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中的一个方法:

1 public void setNonNullParameter(PreparedStatement ps, inti, Integer parameter, JdbcType jdbcType)2 throwsSQLException {3 ps.setInt(i, parameter);4 }

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

执行更新操作并处理结果

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

1 public int doUpdate(MappedStatement ms, Object parameter) throwsSQLException {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 returnhandler.update(stmt);8 } finally{9 closeStatement(stmt);10 }11 }

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

1 public int update(Statement statement) throwsSQLException {2 returndelegate.update(statement);3 }

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

1 public int update(Statement statement) throwsSQLException {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 returnrows;9 }

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

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

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

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

1 public voidprocessAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {2 processBatch(ms, stmt, getParameters(parameter));3 }

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

1 public void processBatch(MappedStatement ms, Statement stmt, Collectionparameters) {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 //ignore

31 }32 }33 }34 }

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值