校园网站建设的基本条件,网站建设找哪个,商标设计注意事项,品牌网站模板一、核心流程
以下代码便是MyBatis的核心流程#xff0c;我们从该代码出发分析MyBatis的源码。 Testpublic void test2() throws Exception{// 1.获取配置文件InputStream in Resources.getResourceAsStream(mybatis-config.xml);// 2.加载解析配置文件并获取Sq…一、核心流程
以下代码便是MyBatis的核心流程我们从该代码出发分析MyBatis的源码。 Testpublic void test2() throws Exception{// 1.获取配置文件InputStream in Resources.getResourceAsStream(mybatis-config.xml);// 2.加载解析配置文件并获取SqlSessionFactory对象SqlSessionFactory factory new SqlSessionFactoryBuilder().build(in);// 3.根据SqlSessionFactory对象获取SqlSession对象SqlSession sqlSession factory.openSession();// 4.通过SqlSession中提供的 API方法来操作数据库UserMapper mapper sqlSession.getMapper(UserMapper.class);ListUser list mapper.selectUserList();for (User user : list) {System.out.println(user);}// 5.关闭会话sqlSession.close();}
二、SqlSessionFactory创建流程
一SqlSessionFactoryBuilder
SqlSessionFactoryBuilder调用了XMLConfigBuilder 的parse方法进行了解析。我们接下来去看XMLConfigBuilder类。 public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {try {// 用于解析 mybatis-config.xml同时创建了 Configuration 对象 XMLConfigBuilder parser new XMLConfigBuilder(inputStream, environment, properties);// 解析XML最终返回一个 DefaultSqlSessionFactory 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.}}}
二XMLConfigBuilder
构造方法 private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {super(new Configuration()); // 完成了Configuration的初始化ErrorContext.instance().resource(SQL Mapper Configuration);this.configuration.setVariables(props); // 设置对应的Properties属性this.parsed false; // 设置 是否解析的标志为 falsethis.environment environment; // 初始化environmentthis.parser parser; // 初始化 解析器}
我们去看看configuration里面干了什么里面主要为各个类取了别名。 parseConfiguration方法
parseConfiguration方法就是对各个具体的方法进行了封装我们任意找几个方法分析即可。
private void parseConfiguration(XNode root) {try {//issue #117 read properties first// 对于全局配置文件各种标签的解析propertiesElement(root.evalNode(properties));// 解析 settings 标签Properties settings settingsAsProperties(root.evalNode(settings));// 读取文件loadCustomVfs(settings);// 日志设置loadCustomLogImpl(settings);// 类型别名typeAliasesElement(root.evalNode(typeAliases));// 插件pluginElement(root.evalNode(plugins));// 用于创建对象objectFactoryElement(root.evalNode(objectFactory));// 用于对对象进行加工objectWrapperFactoryElement(root.evalNode(objectWrapperFactory));// 反射工具箱reflectorFactoryElement(root.evalNode(reflectorFactory));// settings 子标签赋值默认值就是在这里提供的 settingsElement(settings);// read it after objectFactory and objectWrapperFactory issue #631// 创建了数据源 environmentsElement(root.evalNode(environments));databaseIdProviderElement(root.evalNode(databaseIdProvider));typeHandlerElement(root.evalNode(typeHandlers));// 解析引用的Mapper映射器mapperElement(root.evalNode(mappers));} catch (Exception e) {throw new BuilderException(Error parsing SQL Mapper Configuration. Cause: e, e);}}
propertiesElement方法
private void propertiesElement(XNode context) throws Exception {if (context ! null) {// 创建了一个 Properties 对象后面可以用到Properties defaults context.getChildrenAsProperties();String resource context.getStringAttribute(resource);String url context.getStringAttribute(url);if (resource ! null url ! null) {// url 和 resource 不能同时存在throw new BuilderException(The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.);}// 加载resource或者url属性中指定的 properties 文件if (resource ! null) {defaults.putAll(Resources.getResourceAsProperties(resource));} else if (url ! null) {defaults.putAll(Resources.getUrlAsProperties(url));}Properties vars configuration.getVariables();if (vars ! null) {// 和 Configuration中的 variables 属性合并defaults.putAll(vars);}// 更新对应的属性信息parser.setVariables(defaults);configuration.setVariables(defaults);}}
settingsAsProperties方法 private Properties settingsAsProperties(XNode context) {if (context null) {return new Properties();}// 获取settings节点下的所有的子节点Properties props context.getChildrenAsProperties();// Check that all settings are known to the configuration classMetaClass metaConfig MetaClass.forClass(Configuration.class, localReflectorFactory);for (Object key : props.keySet()) {//if (!metaConfig.hasSetter(String.valueOf(key))) {throw new BuilderException(The setting key is not known. Make sure you spelled it correctly (case sensitive).);}}return props;}mapperElement方法
调用 Configuration的addMapper方法将接口放入MapperRegistry中。MapperRegistry是接口与接口工程的map键值对便于之后创建对应的接口。 MapperRegistry就是记录接口与工厂的关系。 addMapper方法调用了解析器的parse方法。 三XMLMapperBuilder
parse方法
public void parse() {// 总体上做了两件事情对于语句的注册和接口的注册if (!configuration.isResourceLoaded(resource)) {// 1、具体增删改查标签的解析。// 一个标签一个MappedStatement。 configurationElement(parser.evalNode(/mapper));configuration.addLoadedResource(resource);// 2、把namespace接口类型和工厂类绑定起来放到一个map。// 一个namespace 一个 MapperProxyFactory bindMapperForNamespace();}parsePendingResultMaps();parsePendingCacheRefs();parsePendingStatements();} 四创建流程图 三、SqlSession 创建流程
通过DefaultSqlSessionFactory的openSession方法来创建对应的SqlSession。在这个DefaultSqlSession对象中包括了Configuration和Executor对象 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();}}
执行的具体流程如下 四、获取代理对象流程
DefaultSqlSession调用 MapperRegistry的getMapper方法该方法拿到对应的代理工厂创建对应的代理对象。 进入newInstance方法最终我们在代码中发现代理对象是通过JDK动态代理创建返回的代理对象。而且里面也传递了一个实现了InvocationHandler接口的触发管理类。 执行的具体流程如下 五、SQL语句执行流程
由于所有的Mapper都是JDK动态代理对象所以任意的方法都是执行触发管理类MapperProxy的invoke()方法。
Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {// toString hashCode equals getClass等方法无需走到执行SQL的流程if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);} else {// 提升获取 mapperMethod 的效率到 MapperMethodInvoker内部接口 的 invoke// 普通方法会走到 PlainMethodInvoker内部类 的 invokereturn cachedInvoker(method).invoke(proxy, method, args, sqlSession);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}
}
然后进入到PlainMethodInvoker的invoke方法 Overridepublic Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {// SQL执行的真正起点return mapperMethod.execute(sqlSession, args);}
进入mapperMethod.execute()方法。在这一步根据不同的typeINSERT、UPDATE、DELETE、SELECT和返回类型1调用convertArgsToSqlCommandParam()将方法参数转换为SQL的参数。2调用sqlSession的insert()、update()、delete()、selectOne ()方法。我们以查询为例会走到selectOne()方法。 public Object execute(SqlSession sqlSession, Object[] args) {Object result;switch (command.getType()) { // 根据SQL语句的类型调用SqlSession对应的方法case INSERT: {// 通过 ParamNameResolver 处理args[] 数组 将用户传入的实参和指定参数名称关联起来Object param method.convertArgsToSqlCommandParam(args);// sqlSession.insert(command.getName(), param) 调用SqlSession的insert方法// rowCountResult 方法会根据 method 字段中记录的方法的返回值类型对结果进行转换result rowCountResult(sqlSession.insert(command.getName(), param));break;}case UPDATE: {Object param method.convertArgsToSqlCommandParam(args);result rowCountResult(sqlSession.update(command.getName(), param));break;}case DELETE: {Object param method.convertArgsToSqlCommandParam(args);result rowCountResult(sqlSession.delete(command.getName(), param));break;}case SELECT:if (method.returnsVoid() method.hasResultHandler()) {// 返回值为空 且 ResultSet通过 ResultHandler处理的方法executeWithResultHandler(sqlSession, args);result null;} else if (method.returnsMany()) {result executeForMany(sqlSession, args);} else if (method.returnsMap()) {result executeForMap(sqlSession, args);} else if (method.returnsCursor()) {result executeForCursor(sqlSession, args);} else {// 返回值为 单一对象的方法Object param method.convertArgsToSqlCommandParam(args);// 普通 select 语句的执行入口 result sqlSession.selectOne(command.getName(), param);if (method.returnsOptional() (result null || !method.getReturnType().equals(result.getClass()))) {result Optional.ofNullable(result);}}break;case FLUSH:result sqlSession.flushStatements();break;default:throw new BindingException(Unknown execution method for: command.getName());}if (result null method.getReturnType().isPrimitive() !method.returnsVoid()) {throw new BindingException(Mapper method command.getName() attempted to return null from a method with a primitive return type ( method.getReturnType() ).);}return result;}
这里来到了对外的接口的默认实现类DefaultSqlSession。selectOne()最终也是调用了selectList()。 Overridepublic T T selectOne(String statement, Object parameter) {// 来到了 DefaultSqlSession// Popular vote was to return null on 0 results and throw exception on too many.ListT 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 {return null;}}
在SelectList()中我们先根据command nameStatement ID从Configuration中拿到MappedStatement。ms里面有xml中增删改查标签配置的所有属性包括id、statementType、sqlSource、useCache、入参、出参等等。然后执行了Executor的query()方法。Executor是第二步openSession的时候创建的创建了执行器基本类型之后依次执行了二级缓存装饰和插件包装。 Overridepublic E ListE selectList(String statement, Object parameter, RowBounds rowBounds) {try {MappedStatement ms configuration.getMappedStatement(statement);// 如果 cacheEnabled true默认Executor会被 CachingExecutor装饰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();}}
BaseExecutor.query方法 Overridepublic E ListE query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)throws SQLException {Cache cache ms.getCache();// cache 对象是在哪里创建的 XMLMapperBuilder类 xmlconfigurationElement()// 由 cache 标签决定if (cache ! null) {// flushCachetrue 清空一级二级缓存 flushCacheIfRequired(ms);if (ms.isUseCache() resultHandler null) {ensureNoOutParams(ms, boundSql);// 获取二级缓存// 缓存通过 TransactionalCacheManager、TransactionalCache 管理SuppressWarnings(unchecked)ListE list (ListE) 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;}}// 走到 SimpleExecutor | ReuseExecutor | BatchExecutorreturn delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);}public E ListE query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {// 异常体系之 ErrorContextErrorContext.instance().resource(ms.getResource()).activity(executing a query).object(ms.getId());if (closed) {throw new ExecutorException(Executor was closed.);}if (queryStack 0 ms.isFlushCacheRequired()) {// flushCachetrue时即使是查询也清空一级缓存clearLocalCache();}ListE list;try {// 防止递归查询重复处理缓存queryStack;// 查询一级缓存// ResultHandler 和 ResultSetHandler的区别list resultHandler null ? (ListE) 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 #601deferredLoads.clear();if (configuration.getLocalCacheScope() LocalCacheScope.STATEMENT) {// issue #482clearLocalCache();}}return list;} private E ListE queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {ListE list;// 先占位localCache.putObject(key, EXECUTION_PLACEHOLDER);try {// 三种 Executor 的区别看doUpdate// 默认Simplelist 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;}
我们到具体的实现类SimpleExecutor中查看doQuery方法 Overridepublic E ListE doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {Statement stmt null;try {Configuration configuration ms.getConfiguration();// 注意已经来到SQL处理的关键对象 StatementHandler StatementHandler handler configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);// 获取一个 Statement对象stmt prepareStatement(handler, ms.getStatementLog());// 执行查询return handler.query(stmt, resultHandler);} finally {// 用完就关闭closeStatement(stmt);}}
在configuration.newStatementHandler()中new一个StatementHandler先得到RoutingStatementHandler。RoutingStatementHandler里面没有任何的实现是用来创建基本的StatementHandler的。这里会根据MappedStatement里面的statementType决定StatementHandler的类型。默认是PREPAREDSTATEMENT、PREPARED、CALLABLE。 StatementHandler里面包含了处理参数的ParameterHandler和处理结果集的ResultSetHandler。
这两个对象都是在上面new的时候创建的。 进入到PreparedStatementHandler中处理 Overridepublic E ListE query(Statement statement, ResultHandler resultHandler) throws SQLException {PreparedStatement ps (PreparedStatement) statement;// 到了JDBC的流程ps.execute();// 处理结果集return resultSetHandler.handleResultSets(ps);} 执行的具体流程如下