샘플 코드
이전 기사에서는 MyBatis의 경우 삽입, 업데이트 및 삭제가 그룹이라고 말했습니다. MyBatis의 경우 모두 업데이트이므로 선택은 그룹입니다. MyBatis의 경우 선택이라고 말합니다.
이 기사에서는 select의 구현 프로세스를 연구합니다. 샘플 코드는
1 public void testSelectOne() { 2 System.out.println(mailDao.selectMailById(8)); 3 }
selectMailById 메서드의 구현은 다음과 같습니다.
1 public Mail selectMailById(long id) {2 SqlSession ss = ssf.openSession();3 try {4 return ss.selectOne(NAME_SPACE + "selectMailById", id);5 } finally {6 ss.close();7 }8 }
우리는 MyBatis에서 제공하는 select가 두 개라는 것을 알고 있습니다. 메소드: selectList 및 selectOne. 그러나 이 기사에서는 selectOne 메소드만 분석해야 하며 그 이유는 나중에 설명합니다.
selectOne 메소드 흐름
먼저 SqlSession의 selectOne 메소드 흐름을 살펴보세요. 이 메소드는 DefaultSqlSession에 있습니다:
1 public <T> T selectOne(String statement, Object parameter) { 2 // Popular vote was to return null on 0 results and throw exception on too many. 3 List<T> list = this.<T>selectList(statement, parameter); 4 if (list.size() == 1) { 5 return list.get(0); 6 } else if (list.size() > 1) { 7 throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size()); 8 } else { 9 return null;10 }11 }
다음은 제가 selectOne과 s라고 부르는 이유입니다. 선택목록 메소드만 분석하면 됩니다. selectList 메소드가 충분한 이유는MyBatis의 모든 selectOne 작업은 결국 selectList 작업으로 변환되기 때문입니다 이는 작업이 완료된 후 결과 집합의 수를 판단하는 것에 지나지 않습니다. 결과 세트 수가 1개를 초과하면 오류가 보고됩니다.
그런 다음 3행에서 selectList의 코드 구현을 살펴보겠습니다. 이 메서드는 DefaultSqlSession에도 있습니다:
1 public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { 2 try { 3 MappedStatement ms = configuration.getMappedStatement(statement); 4 return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); 5 } catch (Exception e) { 6 throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); 7 } finally { 8 ErrorContext.instance().reset(); 9 }10 }
3행에서 MappedStatement를 가져오는 것에 대해서는 언급하지 않겠습니다. 쿼리 구현을 따르겠습니다. 4번 줄의 Executor 메서드. 데코레이터 패턴은 SimpleExecutor에 캐싱 기능을 추가하는 데 사용됩니다. 코드는 CachingExecutor에 있습니다.
1 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {2 BoundSql boundSql = ms.getBoundSql(parameterObject);3 CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);4 return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);5 }
2번 줄의 코드는 BoundSql의 내용을 위에서 언급했습니다. , 그리고 요약도 있습니다.
라인 3의 코드는 입력 매개변수를 기반으로 캐시 키를 구축합니다.
라인 4의 코드는 쿼리 작업을 실행합니다. 코드 구현을 살펴보세요. 코드는 CachingExecutor에도 있습니다.
1 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) 2 throws SQLException { 3 Cache cache = ms.getCache(); 4 if (cache != null) { 5 flushCacheIfRequired(ms); 6 if (ms.isUseCache() && resultHandler == null) { 7 ensureNoOutParams(ms, parameterObject, boundSql); 8 @SuppressWarnings("unchecked") 9 List<E> list = (List<E>) tcm.getObject(cache, key);10 if (list == null) {11 list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);12 tcm.putObject(cache, key, list); // issue #578 and #11613 }14 return list;15 }16 }17 return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);18 }
캐시에 대한 구성 및 참조가 없으므로 다음에서 판단합니다. 4행은 실행되지 않고 4행의 판단이 실행됩니다. 코드는 SimpleExecutor의 상위 클래스인 BaseExecutor에 있습니다. 소스 코드는 다음과 같이 구현됩니다. 16이 여기서 실행되고 queryFromDatabase 메소드는 다음과 같이 구현됩니다.
1 public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { 2 ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId()); 3 if (closed) { 4 throw new ExecutorException("Executor was closed."); 5 } 6 if (queryStack == 0 && ms.isFlushCacheRequired()) { 7 clearLocalCache(); 8 } 9 List<E> list;10 try {11 queryStack++;12 list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;13 if (list != null) {14 handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);15 } else {16 list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);17 }18 } finally {19 queryStack--;20 }21 if (queryStack == 0) {22 for (DeferredLoad deferredLoad : deferredLoads) {23 deferredLoad.load();24 }25 // issue #60126 deferredLoads.clear();27 if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {28 // issue #48229 clearLocalCache();30 }31 }32 return list;33 }
코드는 5행으로 이동하고 마지막으로 duQuery 메소드를 실행합니다.
1 private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { 2 List<E> list; 3 localCache.putObject(key, EXECUTION_PLACEHOLDER); 4 try { 5 list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); 6 } finally { 7 localCache.removeObject(key); 8 } 9 localCache.putObject(key, list);10 if (ms.getStatementType() == StatementType.CALLABLE) {11 localOutputParameterCache.putObject(key, parameter);12 }13 return list;14 }코드를 확인하세요. 4~6번째 줄은 이전 업데이트와 동일하므로 자세한 내용은 다루지 않겠습니다. 핸들러라는 인상을 받은 친구들은 그것이 preparedStatementHandler라는 것을 기억해야 하며, 다음 부분에서는 업데이트와의 차이점과 쿼리 방법이 어떻게 되는지를 분석할 것입니다. ReadyStatementHandler가 구현되었습니다.
PreparedStatementHandler의 쿼리 메서드 구현
PreparedStatementHandler의 쿼리 메서드 끝을 따르며 최종 구현은 다음과 같습니다.
1 public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { 2 Statement stmt = null; 3 try { 4 Configuration configuration = ms.getConfiguration(); 5 StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); 6 stmt = prepareStatement(handler, ms.getStatementLog()); 7 return handler.<E>query(stmt, resultHandler); 8 } finally { 9 closeStatement(stmt);10 }11 }
3번째 줄을 참조하여 쿼리 작업을 수행하세요. 4호선 코드는 결과 집합을 처리하고 결과 집합을 List로 변환합니다. handlerResultSets 메서드는 다음과 같이 구현됩니다.
1 public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {2 PreparedStatement ps = (PreparedStatement) statement;3 ps.execute();4 return resultSetHandler.<E> handleResultSets(ps);5 }
이 방법을 요약해 보겠습니다.
7번째 코드 줄은 preparedStatement의 getResultSet 메서드를 통해 ResultSet을 가져오고 ResultSet을 ResultSetWrapper로 래핑합니다. ResultSetWrapper는 ResultSet을 포함하는 것 외에도 행 및 열 이름과 각 부분의 해당 열을 정의합니다. 데이터베이스에서 반환하는 데이터입니다. 또한, 가장 중요한 것은 TypeHandlerRegister(유형 프로세서, 모든 매개변수는 TypeHandler를 통해 설정됨)도 포함한다는 것입니다.
코드의 9번째 줄은
11번째 코드 줄은 확인을 수행합니다. 즉, 선택 항목에서 반환된 결과가 있지만 이에 해당하는 ResultMap 또는 ResultType이 없으면 예외가 발생합니다. 이유는 매우 간단합니다. 이 둘 중 하나는 MyBatis가 반환을 변환하는 방법을 모릅니다.
12~18행의 코드는 ResultSetWrapper의 값을 ResultMap 기반의 Java 객체로 변환하고 먼저 이를 여러 결과에 저장합니다.
20~33행의 코드는
라인 35의 코드는 크기에 따라 multipleResults를 변환합니다. size=1인 경우 요소 0을 가져와 강제로 List
일반적으로 이 방법은 데이터베이스에서 반환된 결과를 기반으로 이를 사용자 정의 ResultMap으로 캡슐화하는 프로세스는 기본적으로 문제가 없습니다. 여기서 유일한 문제는 여러 결과를 정의하고 최종적으로 판단해야 하는 이유입니다. multipleResults의 크기를 기준으로 하고 최종 결과를 분할했지만 아직 완전히 이해하지 못했습니다. 이 부분은 MyBatis 애플리케이션이 심화됨에 따라 향후 작업에서 학습하도록 남겨두겠습니다. ㅋㅋㅋ 가장 중요한 것은 얻은 데이터가 어디에서 사용되는지입니다. 따라서 여기서는 핵심에 어떤 내용이 담겨 있는지 요약합니다. 마이바티스의 클래스.
첫 번째는 SqlSessionFactory입니다. 기본값은 DefaultSqlSessionFactory입니다. SqlSessionFactory는 매번 SqlSession을 여는 데 사용합니다.
구성 정보가 마지막으로 저장됩니다. , 대부분의 속성, 특히 부울 값은
구성된 데이터베이스 환경 정보를 저장하는 Environment가 있습니다. 여러 개를 지정할 수 있지만 결국에는 Environment가 보유한 몇 가지 중요한 속성만 사용할 수 있습니다.
MappedStatement는 매퍼 파일의
BoundSql이 뒤따릅니다. BoundSql에 저장된 가장 중요한 것은 실행될 현재 SQL 문이고 나머지에는 매개 변수 정보와 매개 변수 개체가 포함됩니다. be set, BoundSql 보유 속성은 다음과 같습니다:
最后是ParameterMapping,ParameterMapping是待设置的参数映射,存储了待设置的参数的相关信息,ParameterMapping持有的属性有:
MyBatis中使用到的设计模式 下面来总结一下MyBatis中使用到的设计模式,有些设计模式可能在到目前位置的文章中没有体现,但是在之后的【MyBatis源码分析】系列文章中也会体现,这里一并先列举出来: 1、建造者模式 代码示例为SqlSessionFactoryBuilder,代码片段: 重载了大量的build方法,可以根据参数的不同构建出不同的SqlSessionFactory。 2、抽象工厂模式 代码示例为TransactionFactory,代码片段为: 抽象出事物工厂,不同的事物类型实现不同的事物工厂,像这里就是Jdbc事物工厂,通过Jdbc事物工厂去返回事物接口的具体实现。 其它的像DataSourceFactory也是抽象工厂模式的实现。 3、模板模式 代码示例为BaseExecutor,代码片段: BaseExecutor封装好方法流程,子类例如SimpleExecutor去实现。 4、责任链模式 代码示例为InterceptorChain,代码片段为: 可以根据需要添加自己的Interceptor,最终按照定义的Interceptor的顺序逐一嵌套执行。 5、装饰器模式 代码示例为CachingExecutor,代码片段为: 给Executor添加上了缓存的功能,update与query的时候会根据用户配置先尝试操作缓存。 在MyBatis中还有很多地方使用到了装饰器模式,例如StatementHandler、Cache。 6、代理模式 代码示例为PooledConnection,代码片段为: 这层代理的作用主要是为了让Connection使用完毕之后从栈中弹出来。 MyBatis中的插件也是使用代理模式实现的,这个在后面会说到。 1 public SqlSessionFactory build(Reader reader) { 2 return build(reader, null, null); 3 } 4 5 public SqlSessionFactory build(Reader reader, String environment) { 6 return build(reader, environment, null); 7 } 8 9 public SqlSessionFactory build(Reader reader, Properties properties) {10 return build(reader, null, properties);11 }12 13 public SqlSessionFactory build(Reader reader, String environment, Properties properties) {14 try {15 XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);16 return build(parser.parse());17 } catch (Exception e) {18 throw ExceptionFactory.wrapException("Error building SqlSession.", e);19 } finally {20 ErrorContext.instance().reset();21 try {22 reader.close();23 } catch (IOException e) {24 // Intentionally ignore. Prefer previous error.25 }26 }27 }
1 public class JdbcTransactionFactory implements TransactionFactory { 2 3 @Override 4 public void setProperties(Properties props) { 5 } 6 7 @Override 8 public Transaction newTransaction(Connection conn) { 9 return new JdbcTransaction(conn);10 }11 12 @Override13 public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {14 return new JdbcTransaction(ds, level, autoCommit);15 }16 }
1 protected abstract int doUpdate(MappedStatement ms, Object parameter) 2 throws SQLException; 3 4 protected abstract List<BatchResult> doFlushStatements(boolean isRollback) 5 throws SQLException; 6 7 protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) 8 throws SQLException; 9 10 protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)11 throws SQLException;
1 public class InterceptorChain { 2 3 private final List<Interceptor> interceptors = new ArrayList<Interceptor>(); 4 5 public Object pluginAll(Object target) { 6 for (Interceptor interceptor : interceptors) { 7 target = interceptor.plugin(target); 8 } 9 return target;10 }11 12 public void addInterceptor(Interceptor interceptor) {13 interceptors.add(interceptor);14 }15 16 public List<Interceptor> getInterceptors() {17 return Collections.unmodifiableList(interceptors);18 }19 20 }
1 public class CachingExecutor implements Executor { 2 3 private Executor delegate; 4 private TransactionalCacheManager tcm = new TransactionalCacheManager(); 5 6 public CachingExecutor(Executor delegate) { 7 this.delegate = delegate; 8 delegate.setExecutorWrapper(this); 9 }10 11 ...12 }
1 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 2 String methodName = method.getName(); 3 if (CLOSE.hashCode() == methodName.hashCode() && CLOSE.equals(methodName)) { 4 dataSource.pushConnection(this); 5 return null; 6 } else { 7 try { 8 if (!Object.class.equals(method.getDeclaringClass())) { 9 // issue #579 toString() should never fail10 // throw an SQLException instead of a Runtime11 checkConnection();12 }13 return method.invoke(realConnection, args);14 } catch (Throwable t) {15 throw ExceptionUtil.unwrapThrowable(t);16 }17 }18 }
위 내용은 소스 코드 분석 및 요약 선택의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!