Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What is the execution process of the SQL statement of MyBatis

2025-01-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)05/31 Report--

This article mainly introduces "what is the execution process of the SQL statement of MyBatis". In the daily operation, I believe that many people have doubts about the execution process of the SQL statement of MyBatis. The editor consulted all kinds of materials and sorted out simple and easy-to-use operation methods. I hope it will be helpful to answer the doubt of "what is the execution process of the SQL statement of MyBatis?" Next, please follow the editor to study!

Introduction to the execution process of 1 SQL statement

MyBatis core executive components:

2 entry analysis performed by SQL 2.1 create a proxy object for the Mapper interface / / mode 1:User user = session.selectOne ("com.oldlu.dao.UserMapper.findUserById", 101); / / mode 2:UserMapper mapper = session.getMapper (UserMapper.class); List userList = mapper.findAll (); 2.2 execute proxy logic

Mode 1 entry analysis:

Session is of type DefaultSqlSession because the SqlSession generated by sqlSessionFactory by default is

DefaultSqlSession type.

SelectOne () calls selectList ().

/ DefaultSqlSession class public List selectList (String statement, Object parameter, RowBoundsrowBounds) {try {MappedStatement ms = configuration.getMappedStatement (statement); / / CURD operation is assigned to Excetor to handle 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 ();}}

Mode 2 entry analysis:

Get the proxy object:

/ / DefaultSqlSession class = > @ Overridepublic T getMapper (Class type) {return configuration.getMapper (type, this);} / / Configuration class = > public T getMapper (Class type, SqlSession sqlSession) {return mapperRegistry.getMapper (type, sqlSession);} / / MapperRegistry-- > apperProxyFactory.newInstance = > public T getMapper (Class type, SqlSession sqlSession) {/ / the proxy factory object final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get (type) that gets the Mapper interface from the cache / / if the Mapper interface is not registered, throw an exception if (mapperProxyFactory = = null) {throw new BindingException ("Type" + type + "is not known to theMapperRegistry.");} try {/ / [use the proxy factory to create a proxy object for the Mapper interface] return mapperProxyFactory.newInstance (sqlSession);} catch (Exception e) {throw new BindingException ("Error getting mapper instance. Cause: "+ MapperProxy mapperProxy e);}} / / MapperProxyFactory-- > generate proxy object at this time = > protected T newInstance (MapperProxy mapperProxy) {/ / Mybatis underlying is calling JDK's Proxy class to create proxy instance 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);}

Proxy object executes logic:

/ / MapperProxy = > / * * the method executed by the proxy object. After the proxy, when all Mapper methods are called, the invoke method * / public Object invoke (Object proxy, Method method, Object [] args) throwsThrowable {try {if (Object.class.equals (method.getDeclaringClass () {/ / if it is an Object method, return method.invoke (this, args) is called. } else {/ / call interface method: according to the Method object of the called interface, every method in the MapperMethodInvoker object / / apper interface gets from the cache corresponds to a MapperMethodInvoker object, and the MapperMethod in the MapperMethodInvoker object stores the corresponding SQL information and return type to complete the SQL call. Return cachedInvoker (method) .invoke (proxy, method, args, sqlSession);} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable (t) }} / * * get the MapperMethodInvoker in the cache, if not, create one, and MapperMethodInvoker internally encapsulates this MethodHandler*/private MapperMethodInvoker cachedInvoker (Method method) throws Throwable {try {return methodCache.computeIfAbsent (method, m-> {if (m.isDefault ()) {/ / if the interface is called by the default method (default method) try {if (privateLookupInMethod = = null) {return newDefaultMethodInvoker (getMethodHandleJava8 (method)) } else {return newDefaultMethodInvoker (getMethodHandleJava9 (method));}} catch (IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException e) {throw new RuntimeException (e) } else {/ / if a normal method (not a default method) is called, a PlainMethodInvoker is created and cached, where MapperMethod stores the SQL of the corresponding interface method and information such as the data type of the input and output parameters return new PlainMethodInvoker (new MapperMethod (mapperInterface,method, sqlSession.getConfiguration ());}});} catch (RuntimeException re) {Throwable cause = re.getCause (); throw cause = = null? Re: cause;}} / / MapperProxy inner class: PainMethodInvoker = > / / when cacheInvoker returns the PalinMethodInvoker instance, the instance's PlainMethodInvoker:invoke method @ Overridepublic Object invoke (Object proxy, Method method, Object [] args, SqlSessionsqlSession) throws Throwable {/ / Mybatis implements the core of the interface method: MapperMethod::execute method: return mapperMethod.execute (sqlSession, args);} / / MapperMethod = > public Object execute (SqlSessionsqlSession, Object [] args) {Object result Switch (command.getType ()) {case INSERT: {/ / resolves the args. If it is multiple parameters, the parameter is converted to Map according to the name specified by the @ Param annotation. If it is an encapsulated entity, it is not converted to 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: / / query operation 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 {/ / parse the parameter, because only one parameter can be passed in the SqlSession::selectOne method, but multiple parameters may be passed in our Mapper. / / the parameter name may be specified through @ Param annotation, so here we need to convert multiple parameters in the Mapper API method into a ParamMap, / / that is, if it is a single encapsulated entity passed in, it will be returned directly. If multiple parameters are passed in, they are actually converted to Map Object param = method.convertArgsToSqlCommandParam (args); / / you can see that the dynamic agent finally uses SqlSession to manipulate the database's 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;} / / at this point we find that we have returned to private Object executeForMany (SqlSession sqlSession, Object [] args) {List result in sqlsession. 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 Analysis of the execution process of query statements 3.1 selectOne method Analysis / / DefaultSqlSession Class = > / / selectOne@Overridepublic T selectOne (String statement, Object parameter) {/ selectOne () will call 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 operation is assigned to Excetor to handle 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 get / / CachingExecutor = > public List query (MappedStatement ms, Object parameterObject, RowBoundsrowBounds, ResultHandler resultHandler) throws SQLException {/ / get bound sql commands, such as" 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) } / / the real query operation is performed by the SimplyExecutor agent. In the query method of SimplyExecutor's parent class BaseExecutor: / / BaseExecutor class: SimplyExecutor's parent class = > @ 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 is a first-level cache. If it cannot be found, call queryFromDatabase to find 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;} / / for the first time, there is no cache, so the queryFromDatabase method is called to execute the query. Private List queryFromDatabase (...) Throws SQLException {List list; localCache.putObject (key, EXECUTION_PLACEHOLDER); try {/ / query 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 class = = > public List doQuery (...) Throws SQLException {Statement stmt = null; try {Configuration configuration = ms.getConfiguration (); StatementHandler handler = configuration.newStatementHandler (....); / / 1:SQL query parameter setting stmt = prepareStatement (handler, ms.getStatementLog ()); / / StatementHandler encapsulates Statement / / 2:SQL query operation and result set encapsulation return handler.query (stmt);} finally {closeStatement (stmt) Parameter settings / / SimplyExecutor class = = > / / [1] Parameter settings: prepareStatementprivate Statement prepareStatement (StatementHandler handler, Log statementLog) throws SQLException {Statement stmt; / / get a Connection through the getConnection method, Connection connection = getConnection (statementLog); / / call the prepare method to get a Statement stmt = handler.prepare (connection, transaction.getTimeout ()); / / set the parameter value in the SQL query * * handler.parameterize (stmt); return stmt } / / RoutingStatementHandler = = > / / PreparedStatementHandler = = > @ Overridepublic void parameterize (Statement statement) throws SQLException {parameterHandler.setParameters ((PreparedStatement) statement);} / / DefaultParameterHandler = = > the parameter is set successfully @ Overridepublic void setParameters (PreparedStatement ps) {ErrorContext.instance (). Activity ("settingparameters") .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)) { 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 ============================>

@ Overridepublic List query (Statement statement) throws SQLException {return delegate.query (statement);} / PreparedStatementHandler = = > @ Overridepublic List query (Statement statement, ResultHandler resultHandler) throws SQLException {/ / here comes the familiar PreparedStatement PreparedStatement ps = (PreparedStatement) statement; / / execute SQL query operation ps.execute (); / / the result is handed over to ResultHandler to process return resultSetHandler. HandleResultSets (ps);} / / DefaultResultSetHandler class (encapsulates the return value and encapsulates the query result into an Object object) @ Overridepublic List handleResultSets (Statement stmt) throws SQLException {ErrorContext.instance (). Activity ("handlingresults") .object (mappedStatement.getId ()); final List multipleResults = new ArrayList (); int resultSetCount = 0; ResultSetWrapper rsw = getFirstResultSet (stmt); List resultMaps = mappedStatement.getResultMaps (); int resultMapCount = resultMaps.size (); validateResultMapsCount (rsw, resultMapCount) While (rsw! = null & & resultMapCount > resultSetCount) {ResultMap resultMap = resultMaps.get (resultSetCount); handleResultSet (rsw, resultMap, multipleResults, null); rsw = getNextResultSet (stmt); cleanUpAfterHandlingResultSet (); resultSetCount++;} String [] resultSets = mappedStatement.getResultSets (); if (resultSets! = null) {while (rsw! = null & resultSetCount)

< resultSets.length) { ResultMapping parentMapping =nextResultMaps.get(resultSets[resultSetCount]); if (parentMapping != null) { String nestedResultMapId = parentMapping.getNestedResultMapId(); ResultMap resultMap =configuration.getResultMap(nestedResultMapId); handleResultSet(rsw, resultMap, null, parentMapping); } rsw = getNextResultSet(stmt); cleanUpAfterHandlingResultSet(); resultSetCount++; } } return collapseSingleResultList(multipleResults);} 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 operation is related to the logic @ Override public int update (String statement, Object parameter) {try {dirty = true; MappedStatement ms = configuration.getMappedStatement (statement) by calling the update statement / / add, delete and modify the bottom layer is 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 get / / CachingExecutor = > @ Overridepublic int update (MappedStatement ms, Object parameterObject) throwsSQLException {/ / perform additions, deletions and modifications, clear cache flushCacheIfRequired (ms); / / jump 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.");} / clear LocalCache first-level cache clearLocalCache (); / / execute 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]. Get statement and do parameter mapping stmt = prepareStatement (handler, ms.getStatementLog ()); / / [2] .handler.update () method executes specific sql instruction return handler.update (stmt);} finally {closeStatement (stmt);}} 4.3 Parameter setting / / SimplyExecutor class = = > / / [1] prepareStatementprivate Statement prepareStatement (StatementHandler handler, Log statementLog) throws SQLException {Statement stmt; Connection connection = getConnection (statementLog) / / create a statement with connection object information, and bind the timeout stmt = handler.prepare (connection, transaction.getTimeout ()); / / parameterize method to set the parameter handler.parameterize (stmt) needed for sql execution; return stmt;} / / RoutingStatementHandler = = > / / PreparedStatementHandler = = > @ Overridepublic void parameterize (Statement statement) throws SQLException {parameterHandler.setParameters ((PreparedStatement) statement) } / / DefaultParameterHandler = = > now the parameter is set successfully @ Overridepublic void setParameters (PreparedStatement ps) {ErrorContext.instance () .activity ("settingparameters") .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)) { 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 ============================>

@ Overridepublic int update (Statement statement) throws SQLException {return delegate.update (statement);} / PreparedStatementHandler = = > @ Overridepublic int update (Statement statement) throws SQLException {/ / here is the underlying JDBC PreparedStatement operation PreparedStatement ps = (PreparedStatement) statement; / / perform SQL addition, deletion and modification operation ps.execute (); / / get the number of affected rows int rows = ps.getUpdateCount (); Object parameterObject = boundSql.getParameterObject (); KeyGenerator keyGenerator = mappedStatement.getKeyGenerator () KeyGenerator.processAfter (executor, mappedStatement, ps, parameterObject); / / return the number of rows affected by return rows;} at this point, the study on "what is the execution process of the SQL statement of MyBatis" is over, hoping to solve everyone's doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report