java web之mybatis(三)---Update操作执行流程

(1) 会话Session的创建

  • 通过初始化获取的SessionFactory创建会话对象
SqlSession session = sessionFactory.openSession();
  •  
  • 调用openSessionFromDataSource()方法执行创建逻辑
public SqlSession openSession() {
    return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
}
  • 在openSessionFromDataSource()方法中创建Executor实例,具体的SQL操作都是通过executor执行的

    • 首先从Configuration中获取Encironment对象,其中包含了数据源和相应的事务的配置

    • 根据Encironment对象创建事务工厂transactionFactory,进而创建事务对象transaction

    • 根据产生的事务对象创建executor执行器对象,用来执行具体的SQL操作;

    • 将executor封装到DefaultSqlSession对象中返回中

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;

    DefaultSqlSession var8;
    try {
        Environment e = this.configuration.getEnvironment();
        TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(e);
        tx = transactionFactory.newTransaction(e.getDataSource(), level, autoCommit);
        Executor executor = this.configuration.newExecutor(tx, execType);
        var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
    } catch (Exception var12) {
        this.closeTransaction(tx);
        throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var12, var12);
    } finally {
        ErrorContext.instance().reset();
    }

    return var8;
}
  • 根据传入的transaction事务实例创建executor实例,DefaultSqlSession将主要的操作交给executor执行;

    • executor有三种类型:BatchExecutor,ReuseExecutor和simpleExecutor;BatchExecutor专门用来执行批处理;ReuseExecutor会重用statement执行sqi操作;simpleExecutor只是简单执行sql语句;默认是simpleExecutor;

    • 如果开启catch缓存的话会利用装饰器模式创建CachingExecutor,CachingExecutor在查询数据库之前会先查询缓存;

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null?this.defaultExecutorType:executorType;
    executorType = executorType == null?ExecutorType.SIMPLE:executorType;
    Object executor;
    if(ExecutorType.BATCH == executorType) {
        executor = new BatchExecutor(this, transaction);
    } else if(ExecutorType.REUSE == executorType) {
        executor = new ReuseExecutor(this, transaction);
    } else {
        executor = new SimpleExecutor(this, transaction);
    }

    if(this.cacheEnabled) {
        executor = new CachingExecutor((Executor)executor);
    }

    Executor executor1 = (Executor)this.interceptorChain.pluginAll(executor);
    return executor1;
}

(2) Insert方法执行流程

1. 创建执行器Executor

  • 通过openSession()方法创建一个SqlSession的实例DefaultSqlSession作为具体SQL执行的入口,DefaultSqlSession对象中封装着属性Configuration,Executor,autoCommit等;
public int insert(String statement) {
    return this.insert(statement, (Object)null);
}
public int insert(String statement, Object parameter) {
    return this.update(statement, parameter);
}
  • insert操作是通过调用update语句进行的相关逻辑,delete操作也是通过调用update实现的具体逻辑;

  • 根据statement从configuration中获取到相应的MappedStatement对象(在初始化中已经解析加载到configuration中)

  • 对参数parameter中的集合进行处理;

public int update(String statement, Object parameter) {
    int var4;
    try {
        this.dirty = true;
        MappedStatement e = this.configuration.getMappedStatement(statement);
        var4 = this.executor.update(e, this.wrapCollection(parameter));
    } catch (Exception var8) {
        throw ExceptionFactory.wrapException("Error updating database.  Cause: " + var8, var8);
    } finally {
        ErrorContext.instance().reset();
    }

    return var4;
}
  • Executor最终将MapperStatement的执行委托给了StatementHandler;具体的执行流程:

    • 获取一个connection数据库连接;

    • 调用StatementHandler.prepare()方法获取一个statement

    • 调用StatementHandler.parameterize方法设置sql执行时候需要的参数

    • 调用StatementHandler.update()方法执行具体sql指令

public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Statement stmt = null;

    int var6;
    try {
        Configuration configuration = ms.getConfiguration();
        StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, (ResultHandler)null, (BoundSql)null);
        stmt = this.prepareStatement(handler, ms.getStatementLog());
        var6 = handler.update(stmt);
    } finally {
        this.closeStatement(stmt);
    }

    return var6;
}
  • 调用prepareStatement()方法获取statement,类似于JDBC的conn.getStatement();
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Connection connection = this.getConnection(statementLog);
    Statement stmt = handler.prepare(connection, this.transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
}

2. 获取Connection数据库连接

  • MyBatis在底层维护了一个简单的数据库连接池用来管理数据库连接,用来复用数据库连接避免连接的重复创建造成的资源浪费;

  • SimpleExecutor中的getConnection()方法最终通过调用openConnection()实现,openConnection()方法中调用dataSource中的getConnection()方法获取连接

protected void openConnection() throws SQLException {

    if (log.isDebugEnabled()) {

      log.debug("Opening JDBC Connection");

    }

    connection = dataSource.getConnection();

    if (level != null) {

      connection.setTransactionIsolation(level.getLevel());

    }

    setDesiredAutoCommit(autoCommmit);

}
  • 调用datasource中的popConnection()方法从数据库连接池中获取连接
return popConnection(dataSource.getUsername(), dataSource.getPassword()).getProxyConnection();
  • MyBatis底层维护了一个数据库连接池主要类是datasource包下的PoolDataSource和PoolConnection,通过调用popConnection()方法从数据库连接池中尝试获取连接;

  • 在数据库连接池中idleConnections用于存放空闲数据库连接,c用于存放创建完成可以使用的数据库连接;

  • 调用popConnenction()方法会尝试先从idleConnections中获取空闲连接,如果idleConnections为空,则判断创建的数据库连接是否超过连接池最大连接数,如果未超过则创建一个新连接并放入activeConnections中;如果超过则从activeConnections列表中取出第一个判断是否超时,如果超时则回滚具体操作,然后创建一个新的数据连接放入activeConnections中;如果未超时则当前线程await()等待等待被唤醒;

private PooledConnection popConnection(String username, String password) throws SQLException {
  boolean countedWait = false;
  PooledConnection conn = null;
  long t = System.currentTimeMillis();
  int localBadConnectionCount = 0;

  while (conn == null) {
    synchronized (state) {
      if (!state.idleConnections.isEmpty()) {
        // Pool has available connection
        conn = state.idleConnections.remove(0);
        if (log.isDebugEnabled()) {
          log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");
        }
      } else {
        // Pool does not have available connection
        if (state.activeConnections.size() < poolMaximumActiveConnections) {
          // Can create new connection
          conn = new PooledConnection(dataSource.getConnection(), this);
          if (log.isDebugEnabled()) {
            log.debug("Created connection " + conn.getRealHashCode() + ".");
          }
        } else {
          // Cannot create new connection
          PooledConnection oldestActiveConnection = state.activeConnections.get(0);
          long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
          if (longestCheckoutTime > poolMaximumCheckoutTime) {
            // Can claim overdue connection
            state.claimedOverdueConnectionCount++;
            state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;
            state.accumulatedCheckoutTime += longestCheckoutTime;
            state.activeConnections.remove(oldestActiveConnection);
            if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {
              try {
                oldestActiveConnection.getRealConnection().rollback();
              } catch (SQLException e) {
                log.debug("Bad connection. Could not roll back");
              }  
            }
            conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
            conn.setCreatedTimestamp(oldestActiveConnection.getCreatedTimestamp());
            conn.setLastUsedTimestamp(oldestActiveConnection.getLastUsedTimestamp());
            oldestActiveConnection.invalidate();
            if (log.isDebugEnabled()) {
              log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");
            }
          } else {
            // Must wait
            try {
              if (!countedWait) {
                state.hadToWaitCount++;
                countedWait = true;
              }
              if (log.isDebugEnabled()) {
                log.debug("Waiting as long as " + poolTimeToWait + " milliseconds for connection.");
              }
              long wt = System.currentTimeMillis();
              state.wait(poolTimeToWait);
              state.accumulatedWaitTime += System.currentTimeMillis() - wt;
            } catch (InterruptedException e) {
              break;
            }
          }
        }
      }
      if (conn != null) {
        if (conn.isValid()) {
          if (!conn.getRealConnection().getAutoCommit()) {
            conn.getRealConnection().rollback();
          }
          conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(), username, password));
          conn.setCheckoutTimestamp(System.currentTimeMillis());
          conn.setLastUsedTimestamp(System.currentTimeMillis());
          state.activeConnections.add(conn);
          state.requestCount++;
          state.accumulatedRequestTime += System.currentTimeMillis() - t;
        } else {
          if (log.isDebugEnabled()) {
            log.debug("A bad connection (" + conn.getRealHashCode() + ") was returned from the pool, getting another connection.");
          }
          state.badConnectionCount++;
          localBadConnectionCount++;
          conn = null;
          if (localBadConnectionCount > (poolMaximumIdleConnections + 3)) {
            if (log.isDebugEnabled()) {
              log.debug("PooledDataSource: Could not get a good connection to the database.");
            }
            throw new SQLException("PooledDataSource: Could not get a good connection to the database.");
          }
        }
      }
    }

  }

  if (conn == null) {
    if (log.isDebugEnabled()) {
      log.debug("PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");
    }
    throw new SQLException("PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");
  }

  return conn;
}
  • 如果idleConnections列表为空且activeConnections列表长度小于连接池最大连接数,则创建新的PoolConnection对象;具体的数据库连接的创建逻辑在doGetConnection()方法中,从dataSource取出数据库配置信息创建相应的数据库连接;
private Connection doGetConnection(String username, String password) throws SQLException {
  Properties props = new Properties();
  if (driverProperties != null) {
    props.putAll(driverProperties);
  }
  if (username != null) {
    props.setProperty("user", username);
  }
  if (password != null) {
    props.setProperty("password", password);
  }
  return doGetConnection(props);
}

private Connection doGetConnection(Properties properties) throws SQLException {
  initializeDriver();
  Connection connection = DriverManager.getConnection(url, properties);
  configureConnection(connection);
  return connection;
}

3. 为Connection生成代理对象

  • 创建新的PoolConnection对象,生成connection的代理
public PooledConnection(Connection connection, PooledDataSource dataSource) {
  this.hashCode = connection.hashCode();
  this.realConnection = connection;
  this.dataSource = dataSource;
  this.createdTimestamp = System.currentTimeMillis();
  this.lastUsedTimestamp = System.currentTimeMillis();
  this.valid = true;
  this.proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this);
}
  • 对Connection生成JDK动态代理,PooledConnection本身就是实现了invocationHandler接口,在调用Connection的方法时候会通过invoke()方法直接调用生成的代理类的方法;

  • 在代理数据库连接类中会判断调用方法是否为close(),则会调用pushConnection()代理原来的close()方法;

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  String methodName = method.getName();
  if (CLOSE.hashCode() == methodName.hashCode() && CLOSE.equals(methodName)) {
    dataSource.pushConnection(this);
    return null;
  } else {
    try {
      if (!Object.class.equals(method.getDeclaringClass())) {
        // issue #579 toString() should never fail
        // throw an SQLException instead of a Runtime
        checkConnection();
      }
      return method.invoke(realConnection, args);
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }
}
  • pushConnection()方法主要处理数据库连接关闭问题,当数据库连接用完后从activeConnections列表中移除connection,同时判断空闲列表idleConnections中的连接数量对否少于最大空闲数量,如果少于将connection放入idleConnections;如果大于最大空闲连接数目则调用原connection的close()方法,使连接失效;
protected void pushConnection(PooledConnection conn) throws SQLException {

  synchronized (state) {
    state.activeConnections.remove(conn);
    if (conn.isValid()) {
      if (state.idleConnections.size() < poolMaximumIdleConnections && conn.getConnectionTypeCode() == expectedConnectionTypeCode) {
        state.accumulatedCheckoutTime += conn.getCheckoutTime();
        if (!conn.getRealConnection().getAutoCommit()) {
          conn.getRealConnection().rollback();
        }
        PooledConnection newConn = new PooledConnection(conn.getRealConnection(), this);
        state.idleConnections.add(newConn);
        newConn.setCreatedTimestamp(conn.getCreatedTimestamp());
        newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());
        conn.invalidate();
        if (log.isDebugEnabled()) {
          log.debug("Returned connection " + newConn.getRealHashCode() + " to pool.");
        }
        state.notifyAll();
      } else {
        state.accumulatedCheckoutTime += conn.getCheckoutTime();
        if (!conn.getRealConnection().getAutoCommit()) {
          conn.getRealConnection().rollback();
        }
        conn.getRealConnection().close();
        if (log.isDebugEnabled()) {
          log.debug("Closed connection " + conn.getRealHashCode() + ".");
        }
        conn.invalidate();
      }
    } else {
      if (log.isDebugEnabled()) {
        log.debug("A bad connection (" + conn.getRealHashCode() + ") attempted to return to the pool, discarding connection.");
      }
      state.badConnectionCount++;
    }
  }
}

4. 利用生成的Connection对象创建statement

  • 根据上文生成的数据库连接创建Statement实例对象;
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
  Statement stmt;
  Connection connection = getConnection(statementLog);
  stmt = handler.prepare(connection, transaction.getTimeout());
  handler.parameterize(stmt);
  return stmt;
}
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
  return delegate.prepare(connection, transactionTimeout);
}
  • prepare()方法调用instantiateStatement()方法从connection中获取Statement实例,并将超时时间绑定
@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);
  }
}
  • 从boundSql中获取待执行的sql,调用connection.preparement(sql)生成Statement对象
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
  String sql = boundSql.getSql();
  if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
    String[] keyColumnNames = mappedStatement.getKeyColumns();
    if (keyColumnNames == null) {
      return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
    } else {
      return connection.prepareStatement(sql, keyColumnNames);
    }
  } else if (mappedStatement.getResultSetType() != null) {
    return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
  } else {
    return connection.prepareStatement(sql);
  }
}

5. 对生成的Statement实例进行参数设置

  • 通过connection实例获取statement后需要进行参数设置
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
  Statement stmt;
  Connection connection = getConnection(statementLog);
  stmt = handler.prepare(connection, transaction.getTimeout());
  handler.parameterize(stmt);
  return stmt;
}
@Override
public void parameterize(Statement statement) throws SQLException {
  delegate.parameterize(statement);
}
@Override
public void parameterize(Statement statement) throws SQLException {
  parameterHandler.setParameters((PreparedStatement) statement);
}
  • 从boundSql中获取参数列表parameterMapping,调用setParameter()方法进行参数设置;
@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);
        }
      }
    }
  }
}

6. 执行更新操作

  • 完成statement的获取和参数设置,接下来执行statement;
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {

    Statement stmt = null;

    try {

      Configuration configuration = ms.getConfiguration();

      StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);

      stmt = prepareStatement(handler, ms.getStatementLog());

      return handler.update(stmt);

    } finally {

      closeStatement(stmt);

    }

}
@Override
public int update(Statement statement) throws SQLException {
  return delegate.update(statement);
}
  • 调用statement.execute()方法执行数据库操作,并调用processAfter()方法处理后置操作;
@Override
public int update(Statement statement) throws SQLException {
  PreparedStatement ps = (PreparedStatement) statement;
  ps.execute();
  int rows = ps.getUpdateCount();
  Object parameterObject = boundSql.getParameterObject();
  KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
  keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
  return rows;
}

(3) 总结

MyBatis具体执行SQL的流程:1.根据statement字符串从configuration中获取对应的mappedStatement;2.根据获取的mappedStatement创建相应的Statement实例;3.根据传入的参数对statement实例进行参数设置;4.执行statement并执行后置操作;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值