Mybais 源码分析四 StatementHandle

StatementHandler负责处理Mybatis与JDBC之间Statement的交互,而JDBC中的Statement是负责与数据库进行交互的对象 。statementHandler有3种类型 SimpleStatementHandler,preparedStatementhandler,callableStatementHandler,我们可以在xml映射文件中配置

	<select id="mapkeyTest" resultType="map" parameterType="_int" statementType="STATEMENT">
		select * from user where id in (18,7)
	</select>

节点的属性statementType 有3个值 STATEMENT,PREPARED 或 CALLABLE 中的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。

1.StatementHandler接口

/**
 * 	StatementHandler接口,定义了一些基本功能
 * */
public interface StatementHandler {
	  Statement prepare(Connection connection)
		      throws SQLException;

		  void parameterize(Statement statement)
		      throws SQLException;

		  void batch(Statement statement)
		      throws SQLException;

		  int update(Statement statement)
		      throws SQLException;

		  <E> List<E> query(Statement statement, ResultHandler resultHandler)
		      throws SQLException;

		  BoundSql getBoundSql();

		  ParameterHandler getParameterHandler();
}

2.mybatis取得statementhandler : 在Executor中通过全局配置文件类Configuration.newStatementHandler取得statementHandler

  public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }

通过一个RoutingStatementHandler对象来取得StatementHandler

/**
 * 	路由StatementHandler,实现了StatementHandler,负责根据节点上配置的statementType类型来分发到不同的statementhandler实例上去
 * */
public class RoutingStatementHandler implements StatementHandler {
	
	//委托的目标实例
	private final StatementHandler delegate;

	//根据配置的statementType 来构建不同类型的StatementHandler
	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());
		}

	}

	public Statement prepare(Connection connection) throws SQLException {
		return delegate.prepare(connection);
	}

	public void parameterize(Statement statement) throws SQLException {
		delegate.parameterize(statement);
	}

	public void batch(Statement statement) throws SQLException {
		delegate.batch(statement);
	}

	public int update(Statement statement) throws SQLException {
		return delegate.update(statement);
	}

	public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
		return delegate.<E>query(statement, resultHandler);
	}

	public BoundSql getBoundSql() {
		return delegate.getBoundSql();
	}

	public ParameterHandler getParameterHandler() {
		return delegate.getParameterHandler();
	}
}

RoutingStatementHandler 是一个处理分发的对象,根据statementType的不同,调用不同的StatementHandler。其本身不执行逻辑,都调用目标StatementHandler的对应方法

3.BaseStatementHandler

/**
 * 	实现了StatementHandler的基础抽象类,实现了一些基本的功能,但最终的执行sql的编译功能由其子类实现
 * */
public abstract class BaseStatementHandler implements StatementHandler {
	
	//核心的配置文件映射类
	protected final Configuration configuration;
	
	//用于构建结果集所映射的javabean
	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;
		
		//typeHandler,objectFactroy都是需要在全局配置文件中配置的,自然从configuration中取得
		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);
	}

	public BoundSql getBoundSql() {
		return boundSql;
	}

	public ParameterHandler getParameterHandler() {
		return parameterHandler;
	}
	
	//根据Connection取得Statement
	public Statement prepare(Connection connection) throws SQLException {
		ErrorContext.instance().sql(boundSql.getSql());
		Statement statement = null;
		try {
			//更具<select|update>等节点上配置的依赖驱动(ResultSetType属性)取得statement对象
			statement = instantiateStatement(connection);
			//设置查询,修改操作过期时间<select|update>等节点(timeout属性配置),可以全局配置文件中配置defaultStatementTimeout	
			setStatementTimeout(statement);
			//这是一个给驱动的提示,尝试让驱动程序每次批量返回的结果行数和这个设置值相等<select|update>等节点(fetchSize属性配置)
			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 c) throws SQLException;
	
	/**
	 * 	设置过期时间
	 * */
	protected void setStatementTimeout(Statement stmt) throws SQLException {
		Integer timeout = mappedStatement.getTimeout();
		Integer defaultTimeout = configuration.getDefaultStatementTimeout();
		if (timeout != null) {
			stmt.setQueryTimeout(timeout);
		} else if (defaultTimeout != null) {
			stmt.setQueryTimeout(defaultTimeout);
		}
	}
}

BaseStatementHandler 其功能基本就是取得Statement对象,并且为statement的行为进行配置

simpleStatementHandler

public class SimpleStatementHandler extends BaseStatementHandler {

	public SimpleStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter,
			RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
		super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
	}
	
	//更新操作
	public int update(Statement statement) throws SQLException {
		//取得sql
		String sql = boundSql.getSql();
		//取得参数
		Object parameterObject = boundSql.getParameterObject();
		//取得配置的主键,这个一般用于能支持自动生成主键的数据库,mysql,sqlServer,不能用于oracle
		KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
		//用于记录更新成功的条数
		int rows;
		//statement执行sql返回值
		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;
	}
	
	//添加批量执行的sql
	public void batch(Statement statement) throws SQLException {
		String sql = boundSql.getSql();
		statement.addBatch(sql);
	}
	
	//执行查询
	public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
		String sql = boundSql.getSql();
		statement.execute(sql);
		return resultSetHandler.<E>handleResultSets(statement);
	}

	/**
	 * 根据ResultSetType配置取得statement实例
	 * 
	 * ResultSet.TYPE_FORWORD_ONLY 结果集的游标只能向下滚动。
	 * ResultSet.TYPE_SCROLL_INSENSITIVE 结果集的游标可以上下移动,当数据库变化时,当前结果集不变。
	 * ResultSet.TYPE_SCROLL_SENSITIVE 返回可滚动的结果集,当数据库变化时,当前结果集同步改变
	 * */
	protected Statement instantiateStatement(Connection connection) throws SQLException {
		if (mappedStatement.getResultSetType() != null) {
			return connection.createStatement(mappedStatement.getResultSetType().getValue(),
					ResultSet.CONCUR_READ_ONLY);
		} else {
			return connection.createStatement();
		}
	}

	public void parameterize(Statement statement) throws SQLException {
		// N/A
	}

}

PreparedStatementHandler



public class PreparedStatementHandler extends BaseStatementHandler {

  public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
  }

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

  public void batch(Statement statement) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.addBatch();
  }

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

  //取得预编译对象,
  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);
    }
  }

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

}

CallableStatementHandler


public class CallableStatementHandler extends BaseStatementHandler {

  public CallableStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
  }

  public int update(Statement statement)
      throws SQLException {
    CallableStatement cs = (CallableStatement) statement;
    cs.execute();
    int rows = cs.getUpdateCount();
    Object parameterObject = boundSql.getParameterObject();
    KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
    keyGenerator.processAfter(executor, mappedStatement, cs, parameterObject);
    resultSetHandler.handleOutputParameters(cs);
    return rows;
  }

  public void batch(Statement statement)
      throws SQLException {
    CallableStatement cs = (CallableStatement) statement;
    cs.addBatch();
  }

  public <E> List<E> query(Statement statement, ResultHandler resultHandler)
      throws SQLException {
    CallableStatement cs = (CallableStatement) statement;
    cs.execute();
    List<E> resultList = resultSetHandler.<E>handleResultSets(cs);
    resultSetHandler.handleOutputParameters(cs);
    return resultList;
  }

  protected Statement instantiateStatement(Connection connection) throws SQLException {
    String sql = boundSql.getSql();
    if (mappedStatement.getResultSetType() != null) {
      return connection.prepareCall(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    } else {
      return connection.prepareCall(sql);
    }
  }

  public void parameterize(Statement statement) throws SQLException {
    registerOutputParameters((CallableStatement) statement);
    parameterHandler.setParameters((CallableStatement) statement);
  }
	//注册model = out的属性
  private void registerOutputParameters(CallableStatement cs) throws SQLException {
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    for (int i = 0, n = parameterMappings.size(); i < n; i++) {
      ParameterMapping parameterMapping = parameterMappings.get(i);
      if (parameterMapping.getMode() == ParameterMode.OUT || parameterMapping.getMode() == ParameterMode.INOUT) {
        if (null == parameterMapping.getJdbcType()) {
          throw new ExecutorException("The JDBC Type must be specified for output parameter.  Parameter: " + parameterMapping.getProperty());
        } else {
          if (parameterMapping.getNumericScale() != null && (parameterMapping.getJdbcType() == JdbcType.NUMERIC || parameterMapping.getJdbcType() == JdbcType.DECIMAL)) {
            cs.registerOutParameter(i + 1, parameterMapping.getJdbcType().TYPE_CODE, parameterMapping.getNumericScale());
          } else {
            if (parameterMapping.getJdbcTypeName() == null) {
              cs.registerOutParameter(i + 1, parameterMapping.getJdbcType().TYPE_CODE);
            } else {
              cs.registerOutParameter(i + 1, parameterMapping.getJdbcType().TYPE_CODE, parameterMapping.getJdbcTypeName());
            }
          }
        }
      }
    }
  }

}

MyBatis是一个开源的持久化框架,用于将数据库操作和Java代码进行解耦。它提供了许多方便的功能和特性,而MyBatis插件则是用于扩展和定制MyBatis功能的组件。 MyBatis插件允许你在MyBatis的执行过程中拦截并修改SQL语句、参数和结果。你可以使用插件来实现一些自定义的逻辑,比如日志记录、性能监控、动态SQL增强等。 要创建一个MyBatis插件,你需要实现Intercepto接口,并覆盖其中的intercept方法。该方法会在MyBatis执行SQL语句前后被调用,你可以在其中编写自己的逻辑。同时,你还需要在插件类上添加@Intercepts注解,指定要拦截的目标方法。 以下是一个示例代码,展示了如何创建一个简单的MyBatis插件: ```java @Intercepts({ @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}), @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}) }) public class MyPlugin implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { // 在执行SQL前的逻辑 System.out.println("Before executing SQL"); // 执行原有的SQL语句 Object result = invocation.proceed(); // 在执行SQL后的逻辑 System.out.println("After executing SQL"); return result; } @Override public Object plugin(Object target) { // 将插件应用到目标对象上 return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { // 设置插件属性 } } ``` 在以上示例中,我们使用@Intercepts注解指定了要拦截的目标方法,这里选择了Executor类的query和update方法。然后在intercept方法中,我们添加了执行SQL前后的逻辑。最后,使用Plugin.wrap方法将插件应用到目标对象上。 当你完成了插件的开发后,需要在MyBatis配置文件中进行配置: ```xml <plugins> <plugin interceptor="com.example.MyPlugin"> <!-- 设置插件属性 --> <property name="key" value="value"/> </plugin> </plugins> ``` 以上是一个简单的MyBatis插件的实现示例,你可以根据自己的需求进行定制和扩展。希望对你有所帮助!如果你还有其他问题,可以继续提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值