StatementHandler-Mybatis source series

Updates github address: I fly

StatementHandler Interface

StatementHandler portion Mybatis encapsulates most basic operation of the database connection. Because, no matter how packaging, ultimately we are to use JDBC and database dealing.
Code when first connecting to the database as we learn java as written below ::

import java.sql.*;

public class FirstExample {
   // JDBC driver name and database URL
   static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";  
   static final String DB_URL = "jdbc:mysql://localhost/emp";

   //  Database credentials
   static final String USER = "root";
   static final String PASS = "123456";

   public static void main(String[] args) {
   Connection conn = null;
   Statement stmt = null;
   try{
      //STEP 2: Register JDBC driver
      Class.forName("com.mysql.jdbc.Driver");

      //STEP 3: Open a connection
      System.out.println("Connecting to database...");
      conn = DriverManager.getConnection(DB_URL,USER,PASS);

      //STEP 4: Execute a query
      System.out.println("Creating statement...");
      stmt = conn.createStatement();
      String sql;
      sql = "SELECT id, first, last, age FROM Employees";
      ResultSet rs = stmt.executeQuery(sql);

      //STEP 5: Extract data from result set
      while(rs.next()){
         //Retrieve by column name
         int id  = rs.getInt("id");
         int age = rs.getInt("age");
         String first = rs.getString("first");
         String last = rs.getString("last");

         //Display values
         System.out.print("ID: " + id);
         System.out.print(", Age: " + age);
         System.out.print(", First: " + first);
         System.out.println(", Last: " + last);
      }
      //STEP 6: Clean-up environment
      rs.close();
      stmt.close();
      conn.close();
   }catch(SQLException se){
      //Handle errors for JDBC
      se.printStackTrace();
   }catch(Exception e){
      //Handle errors for Class.forName
      e.printStackTrace();
   }finally{
      //finally block used to close resources
      try{
         if(stmt!=null)
            stmt.close();
      }catch(SQLException se2){
      }// nothing we can do
      try{
         if(conn!=null)
            conn.close();
      }catch(SQLException se){
         se.printStackTrace();
      }//end finally try
   }//end try
   System.out.println("There are so thing wrong!");
}//end main
}//end FirstExample

For StatementHandler it is to code the following encapsulation and abstraction, the ability to interact with the database, and to provide the upper application Mybatis

StatementHandler Interface:

public interface StatementHandler {
  // 从connection中获取statement
  Statement prepare(Connection connection, Integer transactionTimeout)
      throws SQLException;
  // 对sql进行设置参数
  void parameterize(Statement statement)
      throws SQLException;
  // 批量执行
  void batch(Statement statement)
      throws SQLException;
  // 执行预编译后的sql语句(update,delete,insert)
  int update(Statement statement)
      throws SQLException;
  // 执行查询sql
  <E> List<E> query(Statement statement, ResultHandler resultHandler)
      throws SQLException;
  // 使用游标执行查询sql
  <E> Cursor<E> queryCursor(Statement statement)
      throws SQLException;
  // 获取执行SQL语句的封装类BoundSql
  BoundSql getBoundSql();
  // 参数处理器
  ParameterHandler getParameterHandler();
}

Look at the interface implementation class relations following:
8ce28d16d5aa09bb3eb535a3370bc481.png

Base Statement Trades

BaseStatementHandlerAs the successor to StatementHandlerthe presence of the abstract class interface.

public abstract class BaseStatementHandler implements StatementHandler {

  protected final Configuration configuration;
  protected final ObjectFactory objectFactory;
  protected final TypeHandlerRegistry typeHandlerRegistry;
  protected final ResultSetHandler resultSetHandler;
  protected final ParameterHandler parameterHandler;

  protected 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);
  }

  @Override
  public BoundSql getBoundSql() {
    return boundSql;
  }

  @Override
  public ParameterHandler getParameterHandler() {
    return parameterHandler;
  }

  @Override
  public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
      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();
  }

}

It maintains internal core fields:

  • parameterHandler action is to replace the placeholder function parameter sql
  • sql resultSetHandler mapping result into the result set objects
  • sql executor execution of the abstract, see the following detailed

Only implements three interface methods:

  • getBoundSql
  • getParameterHandler
  • wherein prepare prepare implemented using a template model, subclasses must implement the instantiateStatementmethod of the parent class to complete preparethe template method.

In the constructor of BaseStatementHandler, we can see a call generateKeys code that generates the primary key and key related.

Key placed on the primary key and the key when the key is inserted to obtain the primary data involves the following configuration:

  • selectKey
  • useGeneratedKeys is set to true primary key automatically generated
  • keyProperty which is designated primary key attribute (javaBean's).

Support for automatic generation of database record in the primary key, such as: MySQL, SQL Server, this time provided useGeneratedKeys parameter is true, the record can be added after performing the acquired primary key ID database automatically generated, combined keyProperty specify the primary key.
example:

  <insert id="saveMsg" parameterType="cn.com.tt.e.nano.Notice"
        useGeneratedKeys="true" keyProperty="msgId">
        insert into notice(msg_type,title,content,rec_time,send_time,user_id,deleted,viewed)
        values(#{msgType,jdbcType=INTEGER},#{title,jdbcType=VARCHAR},#{content,jdbcType=VARCHAR},
               #{recTime,jdbcType=BIGINT},#{sendTime,jdbcType=BIGINT},#{userId,jdbcType=VARCHAR},
               #{deleted,jdbcType=TINYINT},#{viewed,jdbcType=INTEGER})
    </insert>

If you are using selectKey, you can set the order attribute AFTER.
example:

<insert id="insertAndgetkey" parameterType="com.soft.mybatis.model.User">
        <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
            SELECT LAST_INSERT_ID()
        </selectKey>
        insert into t_user (username,password,create_date) values(#{username},#{password},#{createDate})
    </insert>

For Oracle databases, when to use auto-increment field, or Sequence you will need to use a unique value such as incoming uuid outside. Is also used selectKey, set the order as before.
example:

<insert id="insert"  parameterType="com.lzumetal.mybatis.entity.Employee">
    <selectKey keyProperty="id" resultType="long" order="BEFORE">
    SELECT SEQ_ADMIN.NEXTVAL FROM DUAL
    </selectKey>
    INSERT INTO tbl_employee(id, name, age, create_time) 
    VALUES(#{id}, #{name}, #{age}, #{createTime})
</insert>
KeyGenerator

KeyGenerator implement the interface Jdbc3KeyGeneratorare: SelectKeyGenerator, ,NoKeyGenerator

useGeneratedKeysSet in settings configuration file relevant source in MappedStatementthe org.apache.ibatis.mapping.MappedStatement.Builder#Buildermiddle:

mappedStatement.keyGenerator = configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;

The default is NoKeyGenerator, if the configuration useGeneratedKeys=trueand operation of the insert is used Jdbc3KeyGenerator.

useGeneratedKeysProvision of the relevant source code mapper file:

if (configuration.hasKeyGenerator(keyStatementId)) {
  keyGenerator = configuration.getKeyGenerator(keyStatementId);
} else {
  keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
      configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
      ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
}

If the set selectKeyis used SelectKeyGenerator(this part may be seen more down), useGeneratedKeysthe logic as before.

selectKeyTab orderrespectively corresponding to KeyGeneratorthe processBeforemethod and processAftermethods.
The BaseStatementHandler in generateKeys method mentioned earlier is triggered place processBefore, is the only one place.

  protected void generateKeys(Object parameter) {
    KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
    ErrorContext.instance().store();
    keyGenerator.processBefore(executor, mappedStatement, null, parameter);
    ErrorContext.instance().recall();
  }

In org.apache.ibatis.executor.statement.StatementHandler#updatethe various implementations can be seen in the implementation of processAfterthe code, such as SimpleStatementHandler code:

  public 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;
  }
ParameterHandler

Then look at the previously mentioned ParameterHandler, it is to function in a dynamic placeholder sql replaced arguments.
Its implementation is DefaultParameterHandler:

public class DefaultParameterHandler implements ParameterHandler {

  private final TypeHandlerRegistry typeHandlerRegistry;

  private final MappedStatement mappedStatement;
  private final Object parameterObject;
  private final BoundSql boundSql;
  private final Configuration configuration;

  public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    this.mappedStatement = mappedStatement;
    this.configuration = mappedStatement.getConfiguration();
    this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
    this.parameterObject = parameterObject;
    this.boundSql = boundSql;
  }

  @Override
  public Object getParameterObject() {
    return parameterObject;
  }

  @Override
  public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    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 {
            MetaObject metaObject = configuration.newMetaObject(parameterObject);
            value = metaObject.getValue(propertyName);
          }
          TypeHandler typeHandler = parameterMapping.getTypeHandler();
          JdbcType jdbcType = parameterMapping.getJdbcType();
          if (value == null && jdbcType == null) {
            jdbcType = configuration.getJdbcTypeForNull();
          }
          try {
            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);
          }
        }
      }
    }
  }

}

SimpleStatementHandler类

SimpleStatementHandler is BaseStatementHandler subclass Statement used to complete the operation of the database, so there will be a placeholder in Sql, parameterizeis empty implementation.
query method:

  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    String sql = boundSql.getSql();
    statement.execute(sql);
    return resultSetHandler.<E>handleResultSets(statement);
  }

The final step to convert the data into a java object database, the subsequent deployments.
update method:

  public 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;
  }

Wherein the front master key processing code has been explained, from the above two code snippets deepest point of view has been dug, and ultimately call java.sql.*the api.

PreparedStatementHandler

PreparedStatementHandler use PreparedStatement implementation.

  public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.<E> handleCursorResultSets(ps);
  }
    public void parameterize(Statement statement) throws SQLException {
    parameterHandler.setParameters((PreparedStatement) statement);
  }

RoutingStatementHandler

At the name you can guess this is the route used, see its constructor:

  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());
    }

  }

Therefore, the specific use which StatementHandleris () is determined by MappedStatement.getStatementType

CallableStatementHandler

Dependence CallableStatement, used to call a stored procedure.

Guess you like

Origin www.cnblogs.com/killbug/p/11080996.html
Recommended