Heim  >  Artikel  >  Java  >  Der Verarbeitungsablauf der Einfügemethode, Aktualisierungsmethode und Löschmethode in Java (Teil 2)

Der Verarbeitungsablauf der Einfügemethode, Aktualisierungsmethode und Löschmethode in Java (Teil 2)

怪我咯
怪我咯Original
2017-06-25 10:02:125870Durchsuche

newStatementHandler-Analyse der Konfiguration

Die doUpdate-Methode von SimpleExecutor wurde oben analysiert:

 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 }

Als ich die newStatementHandler-Methode in Zeile 5 in den letzten zwei Tagen noch einmal las, stellte ich fest, dass die oben in dieser Methode analysierte Methode zu kurz ist. Gehen wir die Implementierung der newStatementHandler-Methode durch Methode ist:

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 }

Es ist nicht interessant, den Code in Zeile 3 zum Plug-in hinzuzufügen. Was tatsächlich von der StatementHandler-Schnittstelle instanziiert wird, ist RoutingStatementHandler. Die Konstruktionsmethode wird wie folgt implementiert:

 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 ist auch eine Implementierung des Dekoratormodus, der implementiert die StatementHandler-Schnittstelle und enthält den Referenzdelegaten der StatementHandler-Schnittstelle. Der StatementType ist hier PREPARED, daher wird das Urteil in Zeile 7 ausgeführt, um PreparedStatementHandler zu instanziieren. Der Instanziierungsprozess ist:

 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 }

Der entscheidende Punkt hier BoundSql, das über MappedStatement abgerufen werden kann, ist in BoundSql gespeichert:

  1. Das Parameterobjekt selbst

  2. Parameterliste

  3. Auszuführende SQL-Anweisung

Einige auf MyBatis basierende sekundäre Entwicklungsframeworks rufen normalerweise die SQL-Anweisungen in BoundSql ab, ändern sie und setzen sie in BoundSql zurück.

Anweisung generieren

Der Prozess der Verbindungsgenerierung wurde oben beschrieben, und zwar hier Artikel geht weiter Schauen Sie, lassen Sie uns zuerst noch einmal die PrepareStatement-Methode von SimpleExecutor veröffentlichen:

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 }

Dann generiert der Code in Zeile 4 eine Anweisung. Der Code in Zeile 4 wird implementiert als :

  Statement prepare(Connection connection, Integer transactionTimeout)

delegate Das obige ist die dekorierte Rolle im Dekoratormuster. Sein Schnittstellentyp ist StatementHandler und sein tatsächlicher Typ ist PreparedStatementHandler Anfang bestanden. Schauen Sie sich die Implementierung der Prepare-Methode an:

 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 }

Der Code in Zeile 6 legt das Abfrage-Timeout fest und der Code in Zeile 7 legt die empfangenen Daten fest Ich werde nicht weiter auf die Größe eingehen. Schauen wir uns die Implementierung der Methode „instantiateStatement“ in Zeile 6 an:

 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 }

Zeile 2, erhalte die Realität Wert vonboundSql Der erste Teil der SQL-Anweisung wurde analysiert. Nachdem Sie die SQL-Anweisung erhalten haben, führen Sie die Beurteilung in den Zeilen 3 und 5 aus. Hier ist der Code, mit dem wir vertraut sind, um die Anweisung über die PreparedStatement-Methode abzurufen. Ihr wahrer Typ ist com.mysql.jdbc.JDBC4PreparedStatement. Dies ist eine Unterklasse von PreparedStatement.

Einstellung der Anweisungsparameter

Nachdem Sie die Anweisung erhalten haben, besteht der nächste Schritt darin, sie festzulegen Sehen Sie sich die Parameter an, schauen Sie sich den Code zum Festlegen von Parametern an oder kehren Sie zur Methode „prepareStatement“ von SimpleExecutor zurück:

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 }

und dem Code in Zeile 5:

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

Weiter mit dem Code in Zeile 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也是一样的操作,这里就不再赘述了。

Das obige ist der detaillierte Inhalt vonDer Verarbeitungsablauf der Einfügemethode, Aktualisierungsmethode und Löschmethode in Java (Teil 2). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn