Mybatis 시리즈 심층 설명(10)---SQL 실행 프로세스 분석(소스 코드)
최근 너무 바빠서 블로그 업데이트를 계속할 시간이 없었습니다. 오늘은 바쁜 일정을 잠시 쉬고 마이바티스 학습 여정을 이어갔습니다. 처음 9개의 기사에서는 mybatis의 구성 및 사용 방법을 소개했으므로 이번 기사에서는 mybatis의 소스 코드에 들어가 mybatis의 실행 과정을 분석해 보겠습니다.
1. SqlSessionFactory 및 SqlSession.
이전 장에서 mybatis를 소개하고 사용함으로써 누구나 SqlSession의 중요성을 표면적으로 인식할 수 있습니다. 우리는 모두 SqlSession을 통해 SQL 문을 실행합니다(참고: 이는 표면적으로만 설명한 것이므로 실제 상황은 나중에 설명합니다). 그럼 먼저 SqlSession을 얻는 방법을 살펴보겠습니다.
(1) 먼저 SqlSessionFactoryBuilder는 mybatis의 구성 파일을 읽고, 그런 다음 DefaultSqlSessionFactory를 빌드합니다. 소스 코드는 다음과 같습니다.
/** * 一系列的构造方法最终都会调用本方法(配置文件为Reader时会调用本方法,还有一个InputStream方法与此对应) * @param reader * @param environment * @param properties * @return */ public SqlSessionFactory build(Reader reader, String environment, Properties properties) { try { //通过XMLConfigBuilder解析配置文件,解析的配置相关信息都会封装为一个Configuration对象 XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties); //这儿创建DefaultSessionFactory对象 return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { reader.close(); } catch (IOException e) { // Intentionally ignore. Prefer previous error. } } } public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); }
(2) SqlSessionFactory를 얻은 후 SqlSessionFactory를 통해 SqlSession 객체를 얻을 수 있습니다. 소스 코드는 다음과 같습니다.
/** * 通常一系列openSession方法最终都会调用本方法 * @param execType * @param level * @param autoCommit * @return */ private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { //通过Confuguration对象去获取Mybatis相关配置信息, Environment对象包含了数据源和事务的配置 final Environment environment = configuration.getEnvironment(); final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); //之前说了,从表面上来看,咱们是用sqlSession在执行sql语句, 实际呢,其实是通过excutor执行, excutor是对于Statement的封装 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(); } }
위 단계를 통해 SqlSession 개체를 얻었습니다. 다음 단계는 수행해야 하는 모든 작업을 수행하는 것입니다(물론 SQL 문 실행 등 다른 작업을 수행할 수 있음). 위 내용을 읽고나서 우리가 예전에 썼던 Demo도 떠올려보자,
아아아아정말 이렇죠?
SqlSession도 얻었습니다. SqlSession에서 일련의 select..., insert..., update..., delete... 메소드를 호출하여 CRUD 작업을 쉽게 수행할 수 있습니다네. 그게 다야? 그렇다면 우리가 구성한 매핑 파일은 어디에 있습니까? 걱정하지 마세요. 계속 읽어보세요.
2. 날카로운 도구 MapperProxy:
mybatis에서는 MapperProxy를 통해 dao를 동적으로 프록시합니다. 즉, 우리가 작성한 dao에서 메서드를 실행할 때 해당 mapperProxy가 실제로 프록시 역할을 합니다. 그래서 MapperProxy 개체를 가져오는 방법을 살펴보겠습니다.
(1) SqlSession을 통해 Configuration에서 를 가져옵니다. 소스 코드는 다음과 같습니다.
SqlSessionFactory sessionFactory = null; String resource = "mybatis-conf.xml"; try { //SqlSessionFactoryBuilder读取配置文件 sessionFactory = new SqlSessionFactoryBuilder().build(Resources .getResourceAsReader(resource)); } catch (IOException e) { e.printStackTrace(); } //通过SqlSessionFactory获取SqlSessionSqlSession sqlSession = sessionFactory.openSession();
(2) SqlSession은 부담을 Configuration에 덤프하고 다음에는 Configuration을 살펴보겠습니다. 소스 코드는 다음과 같습니다.
/** * 什么都不做,直接去configuration中找, 哥就是这么任性 */ @Override public <T> T getMapper(Class<T> type) { return configuration.<T>getMapper(type, this); }
(3) 구성에서는 이 뜨거운 감자를 원하지 않고 MapperRegistry에 덤프합니다. MapperRegistry를 살펴보세요. 소스코드는 다음과 같습니다:
/** * 烫手的山芋,俺不要,你找mapperRegistry去要 * @param type * @param sqlSession * @return */ public <T> T getMapper(Class<T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); }
(4)MapperProxyFactory是个苦B的人,粗活最终交给它去做了。咱们看看源码:
/** * 别人虐我千百遍,我待别人如初恋 * @param mapperProxy * @return */ @SuppressWarnings("unchecked") protected T newInstance(MapperProxy<T> mapperProxy) { //动态代理我们写的dao接口 return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); }
通过以上的动态代理,咱们就可以方便地使用dao接口啦, 就像之前咱们写的demo那样:
UserDao userMapper = sqlSession.getMapper(UserDao.class); User insertUser = new User();
这下方便多了吧, 呵呵, 貌似mybatis的源码就这么一回事儿啊。
别急,还没完, 咱们还没看具体是怎么执行sql语句的呢。
3. Excutor:
接下来,咱们才要真正去看sql的执行过程了。
上面,咱们拿到了MapperProxy, 每个MapperProxy对应一个dao接口, 那么咱们在使用的时候,MapperProxy是怎么做的呢? 源码奉上:
MapperProxy:
/** * MapperProxy在执行时会触发此方法 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (Object.class.equals(method.getDeclaringClass())) { try { return method.invoke(this, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } final MapperMethod mapperMethod = cachedMapperMethod(method); //二话不说,主要交给MapperMethod自己去管 return mapperMethod.execute(sqlSession, args); }
MapperMethod:
/** * 看着代码不少,不过其实就是先判断CRUD类型,然后根据类型去选择到底执行sqlSession中的哪个方法,绕了一圈,又转回sqlSession了 * @param sqlSession * @param args * @return */ public Object execute(SqlSession sqlSession, Object[] args) { Object result; if (SqlCommandType.INSERT == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.insert(command.getName(), param)); } else if (SqlCommandType.UPDATE == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.update(command.getName(), param)); } else if (SqlCommandType.DELETE == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.delete(command.getName(), param)); } else if (SqlCommandType.SELECT == command.getType()) { 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 { Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); } } else { 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了, 那么咱们就看看SqlSession的CRUD方法了,为了省事,还是就选择其中的一个方法来做分析吧。这儿,咱们选择了selectList方法:
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { try { MappedStatement ms = configuration.getMappedStatement(statement); //CRUD实际上是交给Excetor去处理, excutor其实也只是穿了个马甲而已,小样,别以为穿个马甲我就不认识你嘞! 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(); } }
然后,通过一层一层的调用,最终会来到doQuery方法, 这儿咱们就随便找个Excutor看看doQuery方法的实现吧,我这儿选择了SimpleExecutor:
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); stmt = prepareStatement(handler, ms.getStatementLog()); //StatementHandler封装了Statement, 让 StatementHandler 去处理 return handler.<E>query(stmt, resultHandler); } finally { closeStatement(stmt); } }
接下来,咱们看看StatementHandler 的一个实现类 PreparedStatementHandler(这也是我们最常用的,封装的是PreparedStatement), 看看它使怎么去处理的:
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { //到此,原形毕露, PreparedStatement, 这个大家都已经滚瓜烂熟了吧 PreparedStatement ps = (PreparedStatement) statement; ps.execute(); //结果交给了ResultSetHandler 去处理 return resultSetHandler.<E> handleResultSets(ps); }
到此, 一次sql的执行流程就完了。 我这儿仅抛砖引玉,建议有兴趣的去看看Mybatis3的源码。
好啦,本次就到此结束啦,最近太忙了, 又该忙去啦。
以上就是深入浅出Mybatis系列(十)---SQL执行流程分析(源码篇)的内容,更多相关内容请关注PHP中文网(www.php.cn)!

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











HQL과 SQL은 Hibernate 프레임워크에서 비교됩니다. HQL(1. 객체 지향 구문, 2. 데이터베이스 독립적 쿼리, 3. 유형 안전성), SQL은 데이터베이스를 직접 운영합니다(1. 데이터베이스 독립적 표준, 2. 복잡한 실행 파일) 쿼리 및 데이터 조작).

"OracleSQL의 나눗셈 연산 사용법" OracleSQL에서 나눗셈 연산은 일반적인 수학 연산 중 하나입니다. 데이터 쿼리 및 처리 중에 나누기 작업은 필드 간의 비율을 계산하거나 특정 값 간의 논리적 관계를 도출하는 데 도움이 될 수 있습니다. 이 문서에서는 OracleSQL의 나누기 작업 사용법을 소개하고 구체적인 코드 예제를 제공합니다. 1. OracleSQL의 두 가지 분할 연산 방식 OracleSQL에서는 두 가지 방식으로 분할 연산을 수행할 수 있습니다.

Oracle과 DB2는 일반적으로 사용되는 관계형 데이터베이스 관리 시스템으로, 각각 고유한 SQL 구문과 특성을 가지고 있습니다. 이 기사에서는 Oracle과 DB2의 SQL 구문을 비교 및 차이점을 설명하고 구체적인 코드 예제를 제공합니다. 데이터베이스 연결 Oracle에서는 다음 문을 사용하여 데이터베이스에 연결합니다. CONNECTusername/password@database DB2에서 데이터베이스에 연결하는 문은 다음과 같습니다. CONNECTTOdataba

MyBatis 동적 SQL 태그 해석: Set 태그 사용법에 대한 자세한 설명 MyBatis는 풍부한 동적 SQL 태그를 제공하고 데이터베이스 작업 명령문을 유연하게 구성할 수 있는 탁월한 지속성 계층 프레임워크입니다. 그 중 Set 태그는 업데이트 작업에서 매우 일반적으로 사용되는 UPDATE 문에서 SET 절을 생성하는 데 사용됩니다. 이 기사에서는 MyBatis에서 Set 태그의 사용법을 자세히 설명하고 특정 코드 예제를 통해 해당 기능을 보여줍니다. Set 태그란 무엇입니까? Set 태그는 MyBati에서 사용됩니다.

해결 방법: 1. 로그인한 사용자에게 데이터베이스에 액세스하거나 운영할 수 있는 충분한 권한이 있는지 확인하고 해당 사용자에게 올바른 권한이 있는지 확인하십시오. 2. SQL Server 서비스 계정에 지정된 파일에 액세스할 수 있는 권한이 있는지 확인하십시오. 3. 지정된 데이터베이스 파일이 다른 프로세스에 의해 열렸거나 잠겼는지 확인하고 파일을 닫거나 해제한 후 쿼리를 다시 실행하십시오. .관리자로 Management Studio를 실행해 보세요.

데이터베이스 기술 경쟁: Oracle과 SQL의 차이점은 무엇입니까? 데이터베이스 분야에서 Oracle과 SQL Server는 매우 존경받는 관계형 데이터베이스 관리 시스템입니다. 둘 다 관계형 데이터베이스 범주에 속하지만 둘 사이에는 많은 차이점이 있습니다. 이 기사에서는 Oracle과 SQL Server의 차이점과 실제 애플리케이션에서의 기능 및 장점을 자세히 살펴보겠습니다. 우선, Oracle과 SQL Server 사이에는 구문에 차이가 있습니다.

MyBatis 1차 캐시에 대한 자세한 설명: 데이터 액세스 효율성을 향상시키는 방법은 무엇입니까? 개발 과정에서 효율적인 데이터 액세스는 항상 프로그래머의 초점 중 하나였습니다. MyBatis와 같은 지속성 계층 프레임워크의 경우 캐싱은 데이터 액세스 효율성을 향상시키는 주요 방법 중 하나입니다. MyBatis는 두 가지 캐싱 메커니즘을 제공합니다: 첫 번째 수준 캐시와 두 번째 수준 캐시는 기본적으로 활성화됩니다. 이 기사에서는 MyBatis 1단계 캐시의 메커니즘을 자세히 소개하고 독자의 이해를 돕기 위해 구체적인 코드 예제를 제공합니다.

MyBatis 캐싱 메커니즘 분석: 1단계 캐시와 2단계 캐시의 차이점 및 적용 MyBatis 프레임워크에서 캐싱은 데이터베이스 작업 성능을 효과적으로 향상시킬 수 있는 매우 중요한 기능입니다. 그중 1단계 캐시와 2단계 캐시는 MyBatis에서 일반적으로 사용되는 두 가지 캐싱 메커니즘입니다. 이 기사에서는 1차 수준 캐시와 2차 수준 캐시의 차이점과 적용을 자세히 분석하고 설명할 구체적인 코드 예제를 제공합니다. 1. 레벨 1 캐시 레벨 1 캐시는 로컬 캐시라고도 하며 기본적으로 활성화되어 있으며 끌 수 없습니다. 첫 번째 수준 캐시는 SqlSes입니다.
