JDBC 提供了三种 Statement 接口,分别是 Statement 、 PreparedStatement 和
CallableStatement。他们的关系如下:
- Statement 接口提供了执行 SQL,获取执行结果等基本功能。
- PreparedStatement 在此基础上,对IN类型的参数提供了支持。使得我们可以使用运行时参数替换 SQL 中的问号?占位符,而不用手动拼接 SQL。
- CallableStatement 则是在PreparedStatement基础上,对OUT类型的参数提供了支持,该种类型的参数用于保存存储过程输出的结果。
1 2 3 4 5 6 7 8 9 10 11 12
| // -☆- SimpleExecutor private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; // 获取数据库连接 Connection connection = getConnection(statementLog); // 创建 Statement, stmt = handler.prepare(connection, transaction.getTimeout()); // 为 Statement 设置 IN 参数 handler.parameterize(stmt); return stmt; }
|
上面代码的逻辑比较简单,总共包含三个步骤。如下:
- 获取数据库连接
- 创建 Statement
- 为 Statement 设置 IN 参数
MyBatis 并未没有在 getConnection 方法中直接调用 JDBC DriverManager的getConnection 方法获取获取连接,而是通过数据源获取连接。MyBatis提供了两种基于JDBC接口的数据源,
分别为 PooledDataSource和UnpooledDataSource。创建或获取数据库连接的操作最终是由这
两个数据源执行。
PreparedStatement 的创建过程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| // -☆- PreparedStatementHandler public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException { Statement statement = null; try { // 创建 Statement statement = instantiateStatement(connection); // 设置超时和 FetchSize setStatementTimeout(statement, transactionTimeout); setFetchSize(statement); return statement; } catch (SQLException e) { closeStatement(statement); throw e; } catch (Exception e) { closeStatement(statement); throw new ExecutorException("……"); } }
protected Statement instantiateStatement(Connection connection) throws SQLException { String sql = boundSql.getSql(); // 根据条件调用不同的 prepareStatement 方法创建 PreparedStatement 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); } }
|
运行时参数被设置到 SQL 中的过程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| // -☆- PreparedStatementHandler public void parameterize(Statement statement) throws SQLException { // 通过参数处理器 ParameterHandler 设置运行时参数到 PreparedStatement 中 parameterHandler.setParameters((PreparedStatement) statement); }
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 void setParameters(PreparedStatement ps) { // 从 BoundSql 中获取 ParameterMapping 列表,每个 ParameterMapping // 与原始 SQL 中的 #{xxx} 占位符一一对应 List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); if (parameterMappings != null) { for (int i = 0; i < parameterMappings.size(); i++) { ParameterMapping parameterMapping=parameterMappings.get(i); // 检测参数类型,排除掉 mode 为 OUT 类型的 parameterMapping if (parameterMapping.getMode() != ParameterMode.OUT) { Object value; // 获取属性名 String propertyName = parameterMapping.getProperty(); // 检测 BoundSql 的 additionalParameters 是否包含 propertyName if (boundSql.hasAdditionalParameter(propertyName)) { value=boundSql.getAdditionalParameter(propertyName); } else if (parameterObject == null) { value = null; // 检测运行时参数是否有相应的类型解析器 } else if (typeHandlerRegistry.hasTypeHandler( parameterObject.getClass())) { // 若运行时参数的类型有相应的类型处理器 TypeHandler,则将 // parameterObject 设为当前属性的值。 value = parameterObject; } else { // 为用户传入的参数 parameterObject 创建元信息对象 MetaObject metaObject = configuration.newMetaObject(parameterObject); // 从用户传入的参数中获取 propertyName 对应的值 value = metaObject.getValue(propertyName); } // ---------------------分割线--------------------- TypeHandler typeHandler = parameterMapping.getTypeHandler(); JdbcType jdbcType = parameterMapping.getJdbcType(); if (value == null && jdbcType == null) { // 此处 jdbcType = JdbcType.OTHER jdbcType = configuration.getJdbcTypeForNull(); } try { // 由类型处理器 typeHandler 向 ParameterHandler 设置参数 typeHandler.setParameter(ps, i + 1, value, jdbcType); } catch (TypeException e) { throw new TypeException(...); } catch (SQLException e) { throw new TypeException(...); } } } } } }
|
- 分割线以上的大段代码用于获取#{xxx}占位符属性所对应的运行时参数。
- 分割线以下的代码则是获取#{xxx}占位符属性对应的TypeHandler,并在最后通过TypeHandler将运行时参数值设置到 PreparedStatement 中