Mybatis的一次简单查询过程
最近学习mybatis的知识,本篇文章简要的分析了一次查询过程,不废话,直接上代码
@Before
public void prepare() {
String resource = "mybatis-config.xml";
// InputStream inputStream =
// this.getClass().getClassLoader().getResourceAsStream(resource);//锟斤拷取
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);//1.解析配置文件
}
sqlSessionFactory的build(InputStream)主要调用parseConfiguration()方法,该方法代码如下
private void parseConfiguration(XNode root) {
//通过观察源码可知这里的root其实是mybatis.xml的<configuration>原节点
//下面的每一行是针对配置文件中的每种标签进行处理
//例如(1)处对别名处理器进行处理,调用TypeAliasRegistry的regist()er方法进行注册
//其他代码类似,最终将配置中的信息全部注入到单例对象configuration中
try {
propertiesElement(root.evalNode("properties")); //issue #117 read properties first
typeAliasesElement(root.evalNode("typeAliases"));(1)
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);
}
}
最后实际返回的是DefaultSqlSessionFactory对象,其中有一个configuration对象
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;//工厂类中注入了配置信息对象
}
到此为止,程序为我们生成了SqlSessionFactory对象,接下来我们执行一个简单的sq查询,看看mybatis是如何执行的
@Test
public void test04() {
SqlSession SqlSession = null;
try {
SqlSession = SqlSessionFactoryUtil.openSqlSession();//(a)
UserMapper userMapper = SqlSession.getMapper(UserMapper.class);//(b)
User user = new User("whp404","123",new Date(),Sex.MALE);//(c)
userMapper.insertUser(user);
SqlSession.commit();
} catch (Exception e) {
System.out.println(e.getMessage());
SqlSession.rollback();
} finally {
if (SqlSession != null) {
SqlSession.close();
}
}
}
我们将程序划分为三步(a)(b)(c)
步骤(a)
返回sqlsession对象(sqlsession对象是一个接口,实际上会返回默认DefaultSqlSession)
sqlsession对象有四大对象
- Executor代表执行器,有它来调度StatementHandler,ParameterHandler,ResultHandler对象
默认实现类型为SimpleExecutor
- StatementHandler 使用JDBC原始API statement来执行sql语句,起到承上启下的作用
- ParameterHandler 进行sql参数的处理
- ResultHandler 对数据库返回的对象封装返回处理
通过(a)步骤最终返回一个sqlsession初始化好的对象 -
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); 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(); } }
需要注意的是Executor对象生成过程中, executor = (Executor) interceptorChain.pluginAll(executor)这句代码是对Executor对象进行动态代理(mybatis插件只要实现Interceptor接口即可),
这样我们就可以对Executor对象进行改写,加上我们想要的逻辑,其余StatementHandler,ParameterHandler,ResultHandler对象也都是按照类似思路进行包装,实现自定义插件逻辑public Executor newExecutor(Transaction transaction, ExecutorType executorType) { executorType = executorType == null ? defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor 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 (cacheEnabled) { executor = new CachingExecutor(executor); } executor = (Executor) interceptorChain.pluginAll(executor);//生成插件调用链 return executor; } ======================================================== public interface Interceptor { Object intercept(Invocation invocation) throws Throwable;//插件的业务逻辑 Object plugin(Object target); void setProperties(Properties properties);//插件的参数初始化 }
步骤(b)
主要是返回具体业务类的Mapper代理,我的理解就是使得Mapper接口与xml文件产生联系
@SuppressWarnings("unchecked") public <T> T getMapper(Class<T> type, SqlSession sqlSession) { final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type); if (mapperProxyFactory == null) throw new BindingException("Type " + type + " is not known to the MapperRegistry."); try { return mapperProxyFactory.newInstance(sqlSession);//返回Usermapper接口的代理 } catch (Exception e) { throw new BindingException("Error getting mapper instance. Cause: " + e, e); }
}
步骤(c)
下图中其实就是利用(b)步骤生成的接口UserMapper代理去执行查询方法时的情况,实际上是会用mapperMethod执行execute(..)方法去执行查询语句
继续debug跟踪,最后SimpleExecutor执行查询的核心方法,并封装返回结果,查询到此为止