MyBatis的SQL语句执行过程是什么
发表于:2025-01-16 作者:千家信息网编辑
千家信息网最后更新 2025年01月16日,这篇文章主要介绍"MyBatis的SQL语句执行过程是什么",在日常操作中,相信很多人在MyBatis的SQL语句执行过程是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家
千家信息网最后更新 2025年01月16日MyBatis的SQL语句执行过程是什么
这篇文章主要介绍"MyBatis的SQL语句执行过程是什么",在日常操作中,相信很多人在MyBatis的SQL语句执行过程是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"MyBatis的SQL语句执行过程是什么"的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
1 SQL语句的执行过程介绍
MyBatis核心执行组件:
2 SQL执行的入口分析
2.1 为Mapper接口创建代理对象
// 方式1:User user = session.selectOne("com.oldlu.dao.UserMapper.findUserById", 101);// 方式2:UserMapper mapper = session.getMapper(UserMapper.class);ListuserList = mapper.findAll();
2.2 执行代理逻辑
方式1入口分析:
session是DefaultSqlSession类型的,因为sqlSessionFactory默认生成的SqlSession是
DefaultSqlSession类型。
selectOne()会调用selectList()。
// DefaultSqlSession类publicList selectList(String statement, Object parameter, RowBoundsrowBounds) { try { MappedStatement ms = configuration.getMappedStatement(statement); // CURD操作是交给Excetor去处理的 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(); }}
方式2入口分析:
获取代理对象:
//DefaultSqlSession类 ====================>@OverridepublicT getMapper(Class type) { return configuration.getMapper(type, this); }// Configuration类 ====================>public T getMapper(Class type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession);}//MapperRegistry ----> apperProxyFactory.newInstance ====================>public T getMapper(Class type, SqlSession sqlSession) { //从缓存中获取该Mapper接口的代理工厂对象 final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory )knownMappers.get(type); //如果该Mapper接口没有注册过,则抛异常 if (mapperProxyFactory == null) { throw new BindingException("Type " + type + " is not known to theMapperRegistry."); } try { //【使用代理工厂创建Mapper接口的代理对象】 return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException("Error getting mapper instance. Cause: " + e,e); }}//MapperProxyFactory --->此时生成代理对象 ====================>protected T newInstance(MapperProxy mapperProxy) { //Mybatis底层是调用JDK的Proxy类来创建代理实例 return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), newClass[] { mapperInterface }, mapperProxy);}public T newInstance(SqlSession sqlSession) { final MapperProxy mapperProxy = new MapperProxy<>(sqlSession,mapperInterface, methodCache); return newInstance(mapperProxy);}
代理对象执行逻辑:
//MapperProxy ====================>/**代理对象执行的方法,代理以后,所有Mapper的方法调用时,都会调用这个invoke方法*/public Object invoke(Object proxy, Method method, Object[] args) throwsThrowable { try { if (Object.class.equals(method.getDeclaringClass())) { //如果是Object方法,则调用方法本身 return method.invoke(this, args); } else { //调用接口方法:根据被调用接口的Method对象,从缓存中获取MapperMethodInvoker对象 //apper接口中的每一个方法都对应一个MapperMethodInvoker对象,而MapperMethodInvoker对象里面的MapperMethod保存着对应的SQL信息和返回类型以完成SQL调用 ... return cachedInvoker(method).invoke(proxy, method, args, sqlSession); }} catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t);}}/**获取缓存中MapperMethodInvoker,如果没有则创建一个,而MapperMethodInvoker内部封装这一个MethodHandler*/private MapperMethodInvoker cachedInvoker(Method method) throws Throwable { try { return methodCache.computeIfAbsent(method, m -> { if (m.isDefault()) { //如果调用接口的是默认方法(default方法) try { if (privateLookupInMethod == null) { return newDefaultMethodInvoker(getMethodHandleJava8(method)); } else { return newDefaultMethodInvoker(getMethodHandleJava9(method)); } } catch (IllegalAccessException | InstantiationException |InvocationTargetException | NoSuchMethodException e) { throw new RuntimeException(e); } } else { //如果调用的普通方法(非default方法),则创建一个PlainMethodInvoker并放入缓存,其中MapperMethod保存对应接口方法的SQL以及入参和出参的数据类型等信息 return new PlainMethodInvoker(new MapperMethod(mapperInterface,method, sqlSession.getConfiguration())); } }); } catch (RuntimeException re) { Throwable cause = re.getCause();throw cause == null ? re : cause; }}// MapperProxy内部类: PainMethodInvoker ====================>// 当cacheInvoker返回了PalinMethodInvoker实例之后,紧接着调用了这个实例的PlainMethodInvoker:invoke方法@Overridepublic Object invoke(Object proxy, Method method, Object[] args, SqlSessionsqlSession) throws Throwable { //Mybatis实现接口方法的核心: MapperMethod::execute方法: return mapperMethod.execute(sqlSession, args);}// MapperMethod ====================>public Object execute(SqlSession sqlSession, Object[] args) { Object result; switch (command.getType()) { case INSERT: { // 将args进行解析,如果是多个参数则,则根据@Param注解指定名称将参数转换为Map,如果是封装实体则不转换 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 { //解析参数,因为SqlSession::selectOne方法参数只能传入一个,但是我们Mapper中可能传入多个参数, //有可能是通过@Param注解指定参数名,所以这里需要将Mapper接口方法中的多个参数转化为一个ParamMap, //也就是说如果是传入的单个封装实体,那么直接返回出来;如果传入的是多个参数,实际上都转换成了Map Object param = method.convertArgsToSqlCommandParam(args); //可以看到动态代理最后还是使用SqlSession操作数据库的 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 methodwith a primitive return type (" + method.getReturnType() + ")."); } return result;}// 此时我们发现: 回到了sqlsession中privateObject executeForMany(SqlSession sqlSession, Object[] args) { List result; Object param = method.convertArgsToSqlCommandParam(args); if (method.hasRowBounds()) { RowBounds rowBounds = method.extractRowBounds(args); result = sqlSession.selectList(command.getName(), param, rowBounds); } else { result = sqlSession.selectList(command.getName(), param); } // ... return result;}
3 查询语句的执行过程分析
3.1 selectOne方法分析
// DefaultSqlSession类 ===============>// selectOne@OverridepublicT selectOne(String statement, Object parameter) { // //selectOne()会调用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 bereturned by selectOne(), but found: " + list.size()); } else { return null; }}// selectListpublic List selectList(String statement, Object parameter, RowBoundsrowBounds) { try { MappedStatement ms = configuration.getMappedStatement(statement); // CURD操作是交给Excetor去处理的 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(); }}
3.2 sql获取
// CachingExecutor ===============>publicList query(MappedStatement ms, Object parameterObject, RowBoundsrowBounds, ResultHandler resultHandler) throws SQLException { // 获取绑定的sql命令,比如"SELECT * FROM xxx" BoundSql boundSql = ms.getBoundSql(parameterObject); CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql); return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);}@Overridepublic List query(MappedStatement ms, Object parameterObject, RowBoundsrowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { Cache cache = ms.getCache(); if (cache != null) { flushCacheIfRequired(ms); if (ms.isUseCache() && resultHandler == null) { ensureNoOutParams(ms, 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);}//真正执行query操作的是SimplyExecutor代理来完成的,SimplyExecutor的父类BaseExecutor的query方法中:// BaseExecutor类:SimplyExecutor的父类 =================>@Overridepublic List query(MappedStatement ms, Object parameter, RowBoundsrowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throwsSQLException { ErrorContext.instance().resource(ms.getResource()).activity("executing aquery").object(ms.getId()); if (closed) { throw new ExecutorException("Executor was closed."); } if (queryStack == 0 && ms.isFlushCacheRequired()) { clearLocalCache(); } List list; try { queryStack++; //localCache是一级缓存,如果找不到就调用queryFromDatabase从数据库中查找 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(); } deferredLoads.clear(); if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { clearLocalCache(); }} return list;}//第一次,没有缓存,所以会调用queryFromDatabase方法来执行查询。private List queryFromDatabase(...) 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;}// SimpleExecutor类 ============================>public List doQuery(...) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); StatementHandler handler = configuration.newStatementHandler(....); // 1:SQL查询参数的设置 stmt = prepareStatement(handler, ms.getStatementLog()); // StatementHandler封装了Statement // 2:SQL查询操作和结果集的封装 return handler. query(stmt); } finally { closeStatement(stmt); }}
3.3 参数设置
// SimplyExecutor类 ============================>// 【1】 参数设置: prepareStatementprivate Statement prepareStatement(StatementHandler handler, Log statementLog)throws SQLException { Statement stmt; // 通过getConnection方法来获取一个Connection, Connection connection = getConnection(statementLog); // 调用prepare方法来获取一个Statement stmt = handler.prepare(connection, transaction.getTimeout()); // 设置SQL查询中的参数值 *** handler.parameterize(stmt); return stmt;}// RoutingStatementHandler ============================>// PreparedStatementHandler ============================>@Overridepublic void parameterize(Statement statement) throws SQLException { parameterHandler.setParameters((PreparedStatement) statement);}// DefaultParameterHandler ============================> 此时参数设置成功@Overridepublic void setParameters(PreparedStatement ps) { ErrorContext.instance().activity("settingparameters").object(mappedStatement.getParameterMap().getId()); ListparameterMappings = 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)) { 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 formapping....."); } } } }}
3.4 SQL执行和结果集的封装
// RoutingStatementHandler ============================>@OverridepublicList query(Statement statement) throws SQLException { return delegate. query(statement);}// PreparedStatementHandler ============================>@Overridepublic List query(Statement statement, ResultHandler resultHandler)throws SQLException { // 这里就到了熟悉的PreparedStatement了 PreparedStatement ps = (PreparedStatement) statement; // 执行SQL查询操作 ps.execute(); // 结果交给ResultHandler来处理 return resultSetHandler. handleResultSets(ps);}// DefaultResultSetHandler类(封装返回值,将查询结果封装成Object对象)@Overridepublic List
4 更新语句的执行过程分析
xecutor 的 update 方法分析
insert、update 和 delete 操作都会清空一二级缓存
doUpdate 方法
PreparedStatementHandler 的 update 方法
默认是创建PreparedStatementHandler,然后执行prepareStatement方法。
执行结果为受影响行数
执行更新语句的SQL
4.1 sqlsession增删改方法分析
// DefaultSqlSession ===============>@Override public int insert(...) { return update(statement, parameter);} @Override public int update(String statement) { return update(statement, null);} @Override public int delete(...) { return update(....);}// insert 、delete操作是通过调用update语句进行的相关逻辑 @Override public int update(String statement, Object parameter) { try { dirty = true; MappedStatement ms = configuration.getMappedStatement(statement); // 增删改 最终底层都是 update return executor.update(ms, wrapCollection(parameter)); } catch (Exception e) { throw ExceptionFactory.wrapException("Error updating database. Cause: " +e, e); } finally { ErrorContext.instance().reset(); }}
4.2 sql获取
// CachingExecutor ===============>@Overridepublic int update(MappedStatement ms, Object parameterObject) throwsSQLException { // 执行增删改,清除缓存 flushCacheIfRequired(ms); // 跳转BaseExecutor return delegate.update(ms, parameterObject);}// BaseExecutor ===============>@Overridepublic int update(MappedStatement ms, Object parameter) throws SQLException { ErrorContext.instance().resource(ms.getResource()).activity("executing anupdate").object(ms.getId()); if (closed) { throw new ExecutorException("Executor was closed."); } // 清除 LocalCache 一级缓存 clearLocalCache(); //执行 doUpdate return doUpdate(ms, parameter);}// SimpleExecutor ===============>// doUpdate@Overridepublic int doUpdate(MappedStatement ms, Object parameter) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); StatementHandler handler = configuration.newStatementHandler(...); // 【1】.获取statement,并进行参数映射 stmt = prepareStatement(handler, ms.getStatementLog()); // 【2】.handler.update()方法执行具体sql指令 return handler.update(stmt); } finally { closeStatement(stmt); }}
4.3 参数设置
// SimplyExecutor类 ============================>//【1】 prepareStatementprivate Statement prepareStatement(StatementHandler handler, Log statementLog)throws SQLException { Statement stmt; Connection connection = getConnection(statementLog); // 使用connection对象信息创建statement,并将超时时间绑定 stmt = handler.prepare(connection, transaction.getTimeout()); // parameterize方法设置sql执行时候需要的参数 handler.parameterize(stmt); return stmt;}// RoutingStatementHandler ============================>// PreparedStatementHandler ============================>@Overridepublic void parameterize(Statement statement) throws SQLException { parameterHandler.setParameters((PreparedStatement) statement);}// DefaultParameterHandler ============================> 此时参数设置成功@Overridepublic void setParameters(PreparedStatement ps) { ErrorContext.instance().activity("settingparameters").object(mappedStatement.getParameterMap().getId()); ListparameterMappings = 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)) { 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 formapping....."); } } } }}
4.4 SQL执行
// RoutingStatementHandler ============================> @Override public int update(Statement statement) throws SQLException { return delegate.update(statement);}// PreparedStatementHandler ============================>@Overridepublic int update(Statement statement) throws SQLException { // 这里就是底层JDBC的PreparedStatement 操作了 PreparedStatement ps = (PreparedStatement) statement; // 执行SQL增删改操作 ps.execute(); // 获取影响的行数 int rows = ps.getUpdateCount(); Object parameterObject = boundSql.getParameterObject(); KeyGenerator keyGenerator = mappedStatement.getKeyGenerator(); keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject); // 返回影响的行数 return rows;}
到此,关于"MyBatis的SQL语句执行过程是什么"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!
方法
参数
对象
代理
接口
语句
缓存
查询
分析
封装
过程
结果
多个
方式
类型
学习
信息
入口
实例
底层
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
现在网络技术吃香有哪些
网络安全小组培训目的
加密中转服务器
数据库依赖unix操作系统
梦三国连接不上服务器
双路服务器主板内存条怎么插
项目管理系统数据库设计
软件开发找不到客户
系统要带数据库吗
成都地铁软件开发
db2 数据库实例
管理台云服务器
浙江信息网络技术服务
秀数据库中使用图书文献传递
北京大玩家网络技术有限公司
描述软件开发原型模型的阶段
佳木斯向阳区企业服务器在哪
软件开发师的入行资质
广州巡检软件开发
数据库和java建立连接
网络安全法实名制实施时间
当今数据库代表的公司
营口商城分销软件开发多少钱
gps 服务器地址
网络安全知识资料背景
英国服务器如何搭建
数据湖与分布式数据库
c读取文件中的数据库
武昌专业的软件开发公司
一家之主互联网科技有限公司