> Java > java지도 시간 > 본문

소스 코드 분석 및 요약 선택

巴扎黑
풀어 주다: 2017-06-26 09:32:12
원래의
1558명이 탐색했습니다.

샘플 코드

이전 기사에서는 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번째 줄은 태그가 하나의 resultMap 속성만 정의해야 하지만 여기서는 이를 가져옵니다. 는 List이며 그다지 명확하지 않습니다.

11번째 코드 줄은 확인을 수행합니다. 즉, 선택 항목에서 반환된 결과가 있지만 이에 해당하는 ResultMap 또는 ResultType이 없으면 예외가 발생합니다. 이유는 매우 간단합니다. 이 둘 중 하나는 MyBatis가 반환을 변환하는 방법을 모릅니다.

12~18행의 코드는 ResultSetWrapper의 값을 ResultMap 기반의 Java 객체로 변환하고 먼저 이를 여러 결과에 저장합니다.

20~33행의 코드는 에 해당합니다. MappedStatement가 수행될 때 MappedStatement가 보유하는 몇 가지 중요한 속성은 다음과 같습니다.

BoundSql이 뒤따릅니다. BoundSql에 저장된 가장 중요한 것은 실행될 현재 SQL 문이고 나머지에는 매개 변수 정보와 매개 변수 개체가 포함됩니다. be set, BoundSql 보유 속성은 다음과 같습니다:

最后是ParameterMapping,ParameterMapping是待设置的参数映射,存储了待设置的参数的相关信息,ParameterMapping持有的属性有:

 

MyBatis中使用到的设计模式

下面来总结一下MyBatis中使用到的设计模式,有些设计模式可能在到目前位置的文章中没有体现,但是在之后的【MyBatis源码分析】系列文章中也会体现,这里一并先列举出来:

1、建造者模式

代码示例为SqlSessionFactoryBuilder,代码片段:

 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   }
로그인 후 복사

重载了大量的build方法,可以根据参数的不同构建出不同的SqlSessionFactory。

2、抽象工厂模式

代码示例为TransactionFactory,代码片段为:

 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 }
로그인 후 복사

抽象出事物工厂,不同的事物类型实现不同的事物工厂,像这里就是Jdbc事物工厂,通过Jdbc事物工厂去返回事物接口的具体实现。

其它的像DataSourceFactory也是抽象工厂模式的实现。

3、模板模式

代码示例为BaseExecutor,代码片段:

 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;
로그인 후 복사

BaseExecutor封装好方法流程,子类例如SimpleExecutor去实现。

4、责任链模式

代码示例为InterceptorChain,代码片段为:

 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 }
로그인 후 복사

可以根据需要添加自己的Interceptor,最终按照定义的Interceptor的顺序逐一嵌套执行。

5、装饰器模式

代码示例为CachingExecutor,代码片段为:

 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 }
로그인 후 복사

给Executor添加上了缓存的功能,update与query的时候会根据用户配置先尝试操作缓存。

在MyBatis中还有很多地方使用到了装饰器模式,例如StatementHandler、Cache。

6、代理模式

代码示例为PooledConnection,代码片段为:

 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 }
로그인 후 복사

这层代理的作用主要是为了让Connection使用完毕之后从栈中弹出来。

MyBatis中的插件也是使用代理模式实现的,这个在后面会说到。

위 내용은 소스 코드 분석 및 요약 선택의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿