Mybatis 执行流程
1. 基本的使用过程
public static void testAttribute() throws IOException { //1,读取 mybatis-config.xml 文件 InputStream stream = Resources.getResourceAsStream("mybatis-config.xml"); //2,构件 sqlSessionFactory SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(stream); //3,打开 sqlSession SqlSession sqlSession = factory.openSession(); //4,获取 Mapper 接口对象 TestMapper mapper = sqlSession.getMapper(TestMapper.class); //mapper.attribute$Where$Test(null, "张三"); Test test = new Test(); test.setType("测试"); mapper.like$Test("长", test); }
2. 读取资源
通过 IO 来读取配置,读取配置资源
/* * Returns a resource on the classpath as a Stream object * * @param resource The resource to find * @return The resource * @throws java.io.IOException If the resource cannot be found or read */ public static InputStream getResourceAsStream(String resource) throws IOException { return getResourceAsStream(null, resource); } /* * Returns a resource on the classpath as a Stream object * * @param loader The classloader used to fetch the resource * @param resource The resource to find * @return The resource * @throws java.io.IOException If the resource cannot be found or read */ public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException { InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader); if (in == null) { throw new IOException("Could not find resource " + resource); } return in; }
3. 解析配置,创建 SqlSessionFactory
调用 SqlSessionFactoryBuilder 的 build 方法创建 SqlSessionFactory。
3.1 解析配置
第一步先创建 XMLConfigBuilder 实例,调用 parse 方法来解析配置。
public Configuration parse() { if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; parseConfiguration(parser.evalNode("/configuration")); return configuration; } private void parseConfiguration(XNode root) { try { // 分步骤解析 //issue #117 read properties first // 1.properties propertiesElement(root.evalNode("properties")); // 2.类型别名 typeAliasesElement(root.evalNode("typeAliases")); // 3.插件 pluginElement(root.evalNode("plugins")); // 4.对象工厂 objectFactoryElement(root.evalNode("objectFactory")); // 5.对象包装工厂 objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); // 6.设置 settingsElement(root.evalNode("settings")); // read it after objectFactory and objectWrapperFactory issue #631 // 7.环境 environmentsElement(root.evalNode("environments")); // 8.databaseIdProvider databaseIdProviderElement(root.evalNode("databaseIdProvider")); // 9.类型处理器 typeHandlerElement(root.evalNode("typeHandlers")); // 10.映射器 mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } }
3.2 解析 xxxMapper.xml 配置
XMLConfigBuilder 类中 parseConfiguration() 最后一步调用 mapperElement() 来解析 mapper 配置文件。
private void mapperElement(XNode parent) throws Exception { if (parent != null) { for (XNode child : parent.getChildren()) { if ("package".equals(child.getName())) { // 10.4自动扫描包下所有映射器 String mapperPackage = child.getStringAttribute("name"); configuration.addMappers(mapperPackage); } else { String resource = child.getStringAttribute("resource"); String url = child.getStringAttribute("url"); String mapperClass = child.getStringAttribute("class"); if (resource != null && url == null && mapperClass == null) { // 10.1使用类路径 ErrorContext.instance().resource(resource); InputStream inputStream = Resources.getResourceAsStream(resource); // 映射器比较复杂,调用XMLMapperBuilder // 注意在for循环里每个mapper都重新new一个XMLMapperBuilder,来解析 XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); mapperParser.parse(); } else if (resource == null && url != null && mapperClass == null) { // 10.2使用绝对url路径 ErrorContext.instance().resource(url); InputStream inputStream = Resources.getUrlAsStream(url); // 映射器比较复杂,调用XMLMapperBuilder XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments()); mapperParser.parse(); } else if (resource == null && url == null && mapperClass != null) { // 10.3使用java类名 Class<?> mapperInterface = Resources.classForName(mapperClass); // 直接把这个映射加入配置 configuration.addMapper(mapperInterface); } else { throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); } } } } }
最终通过 configuration.addMapers() 方法将解析完成的 mapper 存放到一个 hashMap 中,是个代理。
configuration.addMapers():
public <T> void addMapper(Class<T> type) { mapperRegistry.addMapper(type); }
MapperRegistry.addMapper()
//用来存放 private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>(); public <T> void addMapper(Class<T> type) { if (type.isInterface()) { if (hasMapper(type)) { throw new BindingException("Type " + type + " is already known to the MapperRegistry."); } boolean loadCompleted = false; try { knownMappers.put(type, new MapperProxyFactory<T>(type)); // It's important that the type is added before the parser is run // otherwise the binding may automatically be attempted by the // mapper parser. If the type is already known, it won't try. MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } }
3.3 创建 SqlSessionFactory
根据解析好的配置文件,生成 sqlSessionFactory 对象,默认使用 DefaultSqlSessionFactory 实现。
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); 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. } } } public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); }
4. 创建 SqlSession 对象
通过 SqlSessionFactory.OperSession() 开启一个 sqlSession。
@Override public SqlSession openSession() { return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false); } 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(); } }
4.1. 创建 Transaction 事务
创建一个事务工厂,然后开启一个事务(根据配置文件)
private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) { if (environment == null || environment.getTransactionFactory() == null) { return new ManagedTransactionFactory(); } return environment.getTransactionFactory(); }
默认创建的是 JdbcTransaction
@Override public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) { return new JdbcTransaction(ds, level, autoCommit); }
4.2 创建 Executor 执行器
// 产生执行器 public Executor newExecutor(Transaction transaction, ExecutorType executorType) { executorType = executorType == null ? defaultExecutorType : executorType; // 这句再做一下保护,囧,防止粗心大意的人将defaultExecutorType设成null? executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; // 然后就是简单的3个分支,产生3种执行器BatchExecutor/ReuseExecutor/SimpleExecutor 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); } // 如果要求缓存,生成另一种CachingExecutor(默认就是有缓存),装饰者模式,所以默认都是返回CachingExecutor if (cacheEnabled) { executor = new CachingExecutor(executor); } // 此处调用插件,通过插件可以改变Executor行为 executor = (Executor) interceptorChain.pluginAll(executor); return executor; }
4.3 返回一个 SqlSesson 对象
return new DefaultSqlSession(configuration, executor, autoCommit); //DefaultSqlSession 构造方法 public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) { this.configuration = configuration; this.executor = executor; this.dirty = false; this.autoCommit = autoCommit; }
5. 获取 mapper
创建会话后,通过会话对象session获取mapper。其中会委派给Configuration对象获取。(就是从 3.2 过程中放到的那个 hashMap 去取)
@Override public <T> T getMapper(Class<T> type) { return configuration.getMapper(type, this); }
Configuration又会委派给映射器注册机MapperRegistry去获取。
public <T> T getMapper(Class<T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); }
MapperRegistry中通过MapperProxyFactory创建代理mapper的。
@SuppressWarnings("unchecked") // 返回代理类 public <T> T getMapper(Class<T> type, SqlSession sqlSession) { // 获取mapper代理工厂 final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type); // 在map中找不到则表示没有将mapper类注册进来,抛出BindingException if (mapperProxyFactory == null) { throw new BindingException("Type " + type + " is not known to the MapperRegistry."); } try { // 使用工厂进行创建一个实例,本质上是通过代理模式创建了一个代理类,创建过程中出现异常抛出BindingException return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException("Error getting mapper instance. Cause: " + e, e); } }
MapperProxyFactory最终交由JDK动态代理类Proxy创建mapper代理类的。
@SuppressWarnings("unchecked") protected T newInstance(MapperProxy<T> mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader() , new Class[] { mapperInterface }, mapperProxy); } public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession , mapperInterface, methodCache); return newInstance(mapperProxy); }
6. 执行 mapper 的方法
通过 mapper 调用实现方法,实际是调用代理的方法。
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } else if (isDefaultMethod(method)) { return invokeDefaultMethod(proxy, method, args); } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } final MapperMethod mapperMethod = cachedMapperMethod(method); return mapperMethod.execute(sqlSession, args); }
实际内容在 mapperMethod.execute()
public Object execute(SqlSession sqlSession, Object[] args) { Object result; switch (command.getType()) { case INSERT: { Object param = method.convertArgsToSqlCommandParam(args); 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()) { 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); result = sqlSession.selectOne(command.getName(), param); } 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; }
到这里实际执行的是 sqlSession 提供的方法,以 insert 为例,继续向下:
DefaultSqlSession.insert()
@Override public int insert(String statement, Object parameter) { return update(statement, parameter); } @Override public int update(String statement, Object parameter) { try { dirty = true; MappedStatement ms = configuration.getMappedStatement(statement); //这里继续执行 executor 代理方法 return executor.update(ms, wrapCollection(parameter)); } catch (Exception e) { throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
BaseExecutor.update()
@Override public int update(MappedStatement ms, Object parameter) throws SQLException { ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId()); if (closed) { throw new ExecutorException("Executor was closed."); } //清空缓存 clearLocalCache(); return doUpdate(ms, parameter); }
SimpleExecutor.doUpdate()
@Override public int doUpdate(MappedStatement ms, Object parameter) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null); stmt = prepareStatement(handler, ms.getStatementLog()); // 最终是一个statement进行处理 return handler.update(stmt); } finally { closeStatement(stmt); } } private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; Connection connection = getConnection(statementLog); stmt = handler.prepare(connection, transaction.getTimeout()); handler.parameterize(stmt); return stmt; }
PreparedStatementHandler.update()
@Override 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; }
底层用的就是PreparedStatement的execute()方法了
参考文档
本文作者:primaryC
本文链接:https://www.cnblogs.com/cnff/p/17952372
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步