>  기사  >  Java  >  Java의 insert 메소드, update 메소드, delete 메소드의 처리 흐름 (Part 2)

Java의 insert 메소드, update 메소드, delete 메소드의 처리 흐름 (Part 2)

怪我咯
怪我咯원래의
2017-06-25 10:02:125869검색

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 }

세 번째 줄의 코드는 흥미롭지 않습니다. 두 번째 줄의 코드를 살펴보세요. 실제로 인스턴스화된 것은 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는 또한 데코레이터 모드를 구현합니다. 그리고 StateHandler 인터페이스 참조 위임을 보유합니다. 여기의StatementType은 PREPARED이므로 7행의 판단이 실행되어 ReadyStatementHandler를 인스턴스화합니다. 인스턴스화 프로세스는 다음과 같습니다.

 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 보조 개발을 기반으로 하는 일부 프레임워크는 일반적으로 Get SQL 문 BoundSql에서 이를 수정하고 BoundSql로 재설정합니다.

GenerateStatement

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 라인 4의 코드는

  Statement prepare(Connection connection, Integer transactionTimeout)

delegate로 구현됩니다. 위는 데코레이터 모드의 데코레이팅된 역할이며 실제 유형은 ReadyStatementHandler입니다. 이미 분석되었습니다. 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을 통해 명령문을 얻는 데 익숙한 코드입니다. preparedStatement의 실제 유형은 com.mysql.jdbc.JDBC4PreparedStatement입니다. 이는 ReadyStatement의 하위 클래스입니다.

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 메소드의 처리 흐름 (Part 2)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.