目录
启动Mybatis
Resources.getResourceAsStream
SqlSessionFactoryBuilder.build
XMLConfigBuilder.parse
DefaultSqlSessionFactory.openSession
Configuration.newExecutor
DefaultSqlSession.getMapper
MapperMethod.execute
SimpleExecutor.query
获取数据库连接
预编译SQL
参数化
执行SQL处理结果集
在了解mybatis的原理之前,一定得知道它是如何运行起来的,它运行的入口在哪里?回到最原始的时期,在不使用Spring或SpringBoot的情况下,想要使用mybatis,一般都得按如下方式编写。
public static void main(String[] args) throws Exception{
// 读取 mybatis 的配置文件为输入流
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// 通过配置文件输入流创建 SqlSession工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
// 通过SqlSession 工厂创建一个SqlSession
SqlSession sqlSession = factory.openSession();
// 通过SqlSession找到对应的 mapper 接口类
UserDao userDao = sqlSession.getMapper(UserDao.class);
// 执行sql
userDao.get("1");
}
短短五行代码就完成了mybatis的初始化和使用过程,下面就来剖析一下每行代码背后都做了什么。
这个没什么好说的,就是将mybatis-config.xml这个文件读取成流。
该方法就是创建了一个xml的解析器,然后将xml解析为Configuration对象后,设置到创建的DefaultSqlSessionFactory对象中,最后将DefaultSqlSessionFactory对象返回。
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
// 创建XMLConfigBuilder 对象
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// 调用XMLConfigBuilder.parse方法,返回一个Configuration对象
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
// 将Configuration 对象设置到DefaultSqlSessionFactory对象属性中
// 将DefaultSqlSessionFactory对象返回
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
解析xml文件,并将其中的配置设置到configuration对象中。具体xml中有哪些配置,可以到MYBATIS官网参考。
// 从父类继承的
protected final Configuration configuration;
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
// 实例化Configuration对象,并调用父类构造器设置到configuration属性中
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
// 解析xml中的节点设置到configuration属性中
private void parseConfiguration(XNode root) {
try {
propertiesElement(root.evalNode("properties")); //issue #117 read properties first
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
settingsElement(root.evalNode("settings"));
environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
private final Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
public SqlSession openSession() {
// defaultExecutorType = ExecutorType.SIMPLE;
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
// execType = ExecutorType.SIMPLE
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
// 当前使用的数据库连接环境
final Environment environment = configuration.getEnvironment();
// 这个连接使用的事务管理器
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// 开启一个事务
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 创建一个执行器
final Executor executor = configuration.newExecutor(tx, execType);
// 创建DefaultSqlSession对象并返回
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
// 未配置则使用ManagedTransactionFactory,这个事务管理器几乎什么都不做
if (environment == null || environment.getTransactionFactory() == null) {
return new ManagedTransactionFactory();
}
// 配置了则使用配置的,默认有JdbcTransactionFactory和ManagedTransactionFactory可配置
return environment.getTransactionFactory();
}
在mybatis-config.xml配置文件中,通常environments是这样配置的。这里transactionManager的类型可以是JDBC和MANAGED。
这里以JDBC为例,TranscationFactory就是JdbcTransactionFactory。方法也很简单,直接创建一个JdbcTransaction对象返回即可。
public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
return new JdbcTransaction(ds, level, autoCommit);
}
protected boolean cacheEnabled = true;
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
// executorType未传则使用SIMPLE
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
// 根据ExecutorType来创建对应的执行器
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);
}
// mybatis-config.xml里的settings中是否设置了cacheEnabled(true or false)
// 未设置默认为true
if (cacheEnabled) {
// 代理模式
// 创建CachingExecutor对象作为原执行器的代理对象
executor = new CachingExecutor(executor);
}
// 为该执行器装载插件
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
通过DefaultSqlSession.getMapper方法获取我们自定义的接口。
public class DefaultSqlSession {
public T getMapper(Class type) {
return configuration.getMapper(type, this);
}
}
public class Configuration {
protected MapperRegistry mapperRegistry = new MapperRegistry(this);
public T getMapper(Class type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
}
public class MapperRegistry {
// 在解析mybatis-config.xml时,其中的mappers标签下的mapper标签经过解析之后会存在这里
private final Map, MapperProxyFactory>> knownMappers = new HashMap, MapperProxyFactory>>();
public T getMapper(Class type, SqlSession sqlSession) {
final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);
if (mapperProxyFactory == null)
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
try {
// 通过mapperProxyFactory获取具体实例
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
}
public class MapperProxyFactory{
protected T newInstance(MapperProxy mapperProxy) {
// JDK动态代理
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
// 创建一个JDK的动态代理对象
final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
public class MapperProxy implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class mapperInterface;
private final Map methodCache;
public MapperProxy(SqlSession sqlSession, Class mapperInterface, Map methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
// 对原接口的方法调用,会触发invoke方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 调用的方法是继承自Object,则直接调用
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
// 从缓存中取MapperMethod对象
final MapperMethod mapperMethod = cachedMapperMethod(method);
// 通过MapperMethod对象进行执行
return mapperMethod.execute(sqlSession, args);
}
// MapperMethod的缓存
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
}
我们都知道,接口是不能被实例化的,只能实例化它的实现类。但是接口是可以被JDK动态代理的,这里可以很明显的看到,我们的接口类被MapperProxy给代理了,所有对原始接口类的方法调用都会执行MapperProxy的invoke方法。
在invoke方法中,如果调用的是接口继承自Object的方法,那么会直接调用;否则会根据调用的方法创建一个MapperMethod对象来调用其execute方法,这个方法才是真正干活的。
public MapperMethod(Class> mapperInterface, Method method, Configuration config) {
// 实例化SqlCommand对象,包括该方法对应的sql标签时什么类型等属性
this.command = new SqlCommand(config, mapperInterface, method);
// 实例化方法签名对象,包括方法的全部信息
this.method = new MethodSignature(config, method);
}
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
if (SqlCommandType.INSERT == command.getType()) {
// insert 语句执行insert逻辑
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
} else if (SqlCommandType.UPDATE == command.getType()) {
// update 语句执行update逻辑
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
} else if (SqlCommandType.DELETE == command.getType()) {
// delete 语句执行delete逻辑
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
} else if (SqlCommandType.SELECT == command.getType()) {
// select 语句执行select逻辑
if (method.returnsVoid() && method.hasResultHandler()) {
// 方法返回值是void,且该方法参数有ResultHandler类型
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
// 方法返回值是集合或数组
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
// 方法返回值是Map,且加有@MapKey注解
result = executeForMap(sqlSession, args);
} else {
// 方法返回值是普通类型
// 参数值
Object param = method.convertArgsToSqlCommandParam(args);
// command.getName: 接口的全限定类名 + '.' + 方法名
// 如果该方法是继承来的则使用的是父类的全限定类名
result = sqlSession.selectOne(command.getName(), param);
}
} else {
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
// sql语句执行结果为null,且方法返回值是基本类型
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
execute方法里对四种类型的SQL语句都做了相应的处理,这里仅挑选SELECT语句的返回单条结果加以分析,即selectOne的逻辑;
在执行selectOne之前,需要先将方法的执行参数进行一个解析,这样才能对应上XML中写的变量名。
public MethodSignature(Configuration configuration, Method method) throws BindingException {
...
this.hasNamedParameters = hasNamedParams(method);
this.params = Collections.unmodifiableSortedMap(getParams(method, this.hasNamedParameters));
}
// 得到参数名和参数值得绑定关系Map,仅一个有效参数时则直接返回该参数值
public Object convertArgsToSqlCommandParam(Object[] args) {
final int paramCount = params.size();
if (args == null || paramCount == 0) {
// 没有参数则返回null
return null;
} else if (!hasNamedParameters && paramCount == 1) {
// 未使用@Param注解且仅一个有效参数
return args[params.keySet().iterator().next()];
} else {
// key: paramName, value:参数值
final Map param = new ParamMap
以上代码可以看到,在XML中不仅可以使用@Param的value作为变量名,还可以使用#{0},#{1} 或者 #{param1},#{param2}来作为变量名使用。
执行selectOne,实际上还是执行的selectList,只是返回第一条结果罢了;另外还做了一个参数的包装,所以当参数时集合的时候,xml中可以直接写collection或者list作为变量名;
public class DefaultSqlSession {
public T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
// 调用selectList,返回第一条记录
List list = this.selectList(statement, parameter);
if (list.size() == 1) {
return list.get(0);
} else if (list.size() > 1) {
// 不止一条记录就抛出异常
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {
// 没有记录则返回null
return null;
}
}
public List selectList(String statement, Object parameter) {
// 设置 RowBounds.DEFAULT 调用selectList
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
public List selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
// 通过configuration获取待执行的sql语句信息
MappedStatement ms = configuration.getMappedStatement(statement);
// 执行器执行
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
// 包装参数,设置默认参数名
private Object wrapCollection(final Object object) {
if (object instanceof Collection) {
// 参数是集合则增加设置其参数名为collection
StrictMap
方法最后还是通过执行器来进行执行SQL,之前有提到openSqlSession的时候使用一个代理模式创建了CachingExector,代理对象是SimpleExecutor。
public class CachingExecutor {
private final TransactionalCacheManager tcm = new TransactionalCacheManager();
public List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
// SQL语句
BoundSql boundSql = ms.getBoundSql(parameterObject);
// 二级缓存key
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
@Override
public List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
// 二级缓存
Cache cache = ms.getCache();
if (cache != null) { // 开启了二级缓存
// 是否需要清除二级缓存
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, parameterObject, boundSql);
// 获取二级缓存
@SuppressWarnings("unchecked")
List list = (List) tcm.getObject(cache, key);
if (list == null) {
// 没有缓存则查询后放入二级缓存
list = delegate. query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
// 未开启二级缓存则直接查询
return delegate. query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
}
// SimpleExecutor的父类
public class BaseExecutor {
protected PerpetualCache localCache;
public List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List list;
try {
queryStack++;
// 从一级缓存拿值
list = resultHandler == null ? (List) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
// 没有值则从数据库中查
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
private List queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List list;
// 放置一个空对象到一级缓存
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
// 执行具体的查询逻辑, 交给子类实现
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
// 放入一级缓存
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
}
别的逻辑都先不管,只看主要逻辑。这段的主要逻辑就是,先从一级缓存拿值,能拿到就返回拿不到就查数据库之后再放到一级缓存中;其中具体的查询逻辑doQuery方法是交给了子类实现,这里的子类是SimpleExecutor;
在看doQuery方法之前,有必要回顾一下最原始的使用JDBC来连接数据库的步骤:
void jdbc() throws Exception{
// 加载驱动
Class.forName("com.mysql.jdbc.Driver");
// 获取连接
Connection connection = DriverManager.getConnection("url", "username", "password");
// sql语句参数化
String sql = "select name from t_user where id = ?";
// 预编译SQL
PreparedStatement prepareStatement = connection.prepareStatement(sql);
// 设置参数值
prepareStatement.setString(1, "123");
// 执行SQL
prepareStatement.execute();
ResultSet resultSet = prepareStatement.getResultSet();
// 处理结果集
while (resultSet.next()) {
System.out.println(resultSet.getString("name"));
}
}
以上就是JDBC执行SQL查询的过程,mybatis无非就是对这个过程进行了一系列的封装
public class SimpleExecutor {
public List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
// 拿到一个数据库连接
Connection connection = getConnection(statementLog);
// 得到Statement 对象
stmt = handler.prepare(connection, transaction.getTimeout());
// 参数化
handler.parameterize(stmt);
return stmt;
}
}
public class Configuration {
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
// 实例化RoutingStatementHandler对象
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
// 装载拦截器
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
}
public class BaseExecutor {
// 之前提过, 在XML中有两种参数可选 - JDBC,MANAGED
// 这两种分别代表了使用 JdbcTransaction 和 ManageTranscation
// 我在配置文件中使用了JDBC的方式
protected Transaction transaction;
protected Connection getConnection(Log statementLog) throws SQLException {
// 通过事务获取连接
Connection connection = transaction.getConnection();
if (statementLog.isDebugEnabled()) {
return ConnectionLogger.newInstance(connection, statementLog, queryStack);
} else {
return connection;
}
}
}
public class JdbcTransaction {
// 之前提过,在XML中可以选择POOLED和UNPOOLED
// 分别对应 PooledDataSource 和 UnpooledDataSource
// 这里以UnpooledDataSource举例, 因为比较简单
protected DataSource dataSource;
public Connection getConnection() throws SQLException {
if (connection == null) {
openConnection();
}
return connection;
}
protected void openConnection() throws SQLException {
// 从数据源拿到连接
connection = dataSource.getConnection();
if (level != null) {
connection.setTransactionIsolation(level.getLevel());
}
setDesiredAutoCommit(autoCommit);
}
}
public class UnpooledDataSource {
public Connection getConnection() throws SQLException {
return doGetConnection(username, password);
}
public Connection getConnection(String username, String password) throws SQLException {
return doGetConnection(username, password);
}
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;
}
private synchronized void initializeDriver() throws SQLException {
if (!registeredDrivers.containsKey(driver)) {
Class> driverType;
try {
// 加载数据库驱动
if (driverClassLoader != null) {
driverType = Class.forName(driver, true, driverClassLoader);
} else {
driverType = Resources.classForName(driver);
}
// DriverManager requires the driver to be loaded via the system ClassLoader.
// http://www.kfu.com/~nsayer/Java/dyn-jdbc.html
Driver driverInstance = (Driver)driverType.newInstance();
DriverManager.registerDriver(new DriverProxy(driverInstance));
registeredDrivers.put(driver, driverInstance);
} catch (Exception e) {
throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);
}
}
}
}
至此,看到了Class.forName和DriverManager.getConnection这两个方法,说明数据库已经连接上了,下一步就是预编译SQL。
public class RoutingStatementHandler {
// 代理对象
private final StatementHandler delegate;
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
// 根据SQL语句定义的statementType类决定使用哪个作为代理,如果没有设置则默认是PREPARED
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, Integer transactionTimeout) throws SQLException {
return delegate.prepare(connection, transactionTimeout);
}
// 调用代理对象方法
public void parameterize(Statement statement) throws SQLException {
delegate.parameterize(statement);
}
}
// PreparedStatementHandler父类
public class BaseStatementHandler {
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
// 实例化Statement对象
statement = instantiateStatement(connection);
setStatementTimeout(statement, transactionTimeout);
setFetchSize(statement);
// 返回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;
}
public class PreparedStatementHandler {
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() == ResultSetType.DEFAULT) {
return connection.prepareStatement(sql);
} else {
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
}
}
}
至此,有看到了Connection.reparedstatement方法,所以预编译SQL也完成了,下一步就是参数化。
public class PreparedStatementHandler {
// 参数处理器
protected final ParameterHandler parameterHandler;
// 参数化, 设置参数
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
}
public class DefaultParameterHandler{
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
List 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 | SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
}
public class BaseTypeHandler {
public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
if (parameter == null) {
if (jdbcType == null) {
throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
}
try {
ps.setNull(i, jdbcType.TYPE_CODE);
} catch (SQLException e) {
throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . "
+ "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. "
+ "Cause: " + e, e);
}
} else {
try {
// 设置非空参数, 子类实现
setNonNullParameter(ps, i, parameter, jdbcType);
} catch (Exception e) {
throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . "
+ "Try setting a different JdbcType for this parameter or a different configuration property. "
+ "Cause: " + e, e);
}
}
}
}
// 以String为例
public class StringTypeHandler extends BaseTypeHandler {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
throws SQLException {
ps.setString(i, parameter);
}
}
抛开那些复杂的逻辑处理,看到了最后一步的PreparedStatement.setString方法,虽然过程比较曲折,有对参数进行各种判断,最终选择出一个合适的类型处理器来处理参数并设值。
public class PreparedStatementHandler {
protected final ResultSetHandler resultSetHandler;
public List query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
// 执行SQL
ps.execute();
return resultSetHandler.handleResultSets(ps);
}
}
public class DefaultResultSetHandler {
public List handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List multipleResults = new ArrayList<>();
int resultSetCount = 0;
ResultSetWrapper rsw = getFirstResultSet(stmt);
// 对ResultSetWrapper 进行一系列处理, 将结果设置到multipleResults
...
return collapseSingleResultList(multipleResults);
}
private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
// 获取结果集
ResultSet rs = stmt.getResultSet();
while (rs == null) {
// move forward to get the first resultset in case the driver
// doesn't return the resultset as the first result (HSQLDB 2.1)
if (stmt.getMoreResults()) {
rs = stmt.getResultSet();
} else {
if (stmt.getUpdateCount() == -1) {
// no more results. Must be no resultset
break;
}
}
}
// 对结果集进行一次封装
return rs != null ? new ResultSetWrapper(rs, configuration) : null;
}
}
preparedStatement.execute 和 preparedStatement.getResultSet 方法都已经看到,自此JDBC操作数据库的步骤,在mybatis中都完全体现了出来,mybatis无非就是封装的更加精妙。包括最后对ResultSet的处理,根据返回的结果集封装成我们需要的对象集合进行返回。
到现在为止,mybatis的执行流程,从加载配置文件到获取Mapper对象,再到最后执行SQL语句,都已经差不多都清晰了。其中还有很多细节:如何解析XML,如何对参数对象进行取值等等问题,这一篇文章肯定是没法完全讲完了,等到以后有时间有精力了再说吧......