Mybatis原理初探
生活随笔
收集整理的這篇文章主要介紹了
Mybatis原理初探
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
開篇略談
談到Mybatis,對于我們猿們來說是熟悉不過了。但是有沒有興趣去探一下其實現原理呢?是的,請往下看?↓? come on...
Mybatis綜述
Mybatis一個數據持久層輕量級框架,回顧我們原始的開發即沒有持久層框架的年代。話不多說上代碼?↓
Connection con = DriverManager.getConnection(url, "...", "..."); // 首先我們得獲得一個數據庫連接 Statement stmt = con.createStatement(); // 不管你是獲得statement還是preparedStatement,總之在項目越來越大得時候這些代碼會有點累贅// PreparedStatement prestmt = con.prepareStatement(...);
? 而我們得Mybatis則將其封裝了起來,構成持久層框架,我們只要按照它的規定去配置就可以了,而無需在關注上面得代碼,只需關注sql就行了,接下來入正題
Mybatis源碼解析
提到Mybatis我們總會想起那個耳熟能詳的東西sqlSession,是的,這是Mybatis的核心所在,sqlSession是由單例sqlSessionFactory創建而來的,而sqlSessionFactory又是由sqlSessionFactoryBuilder創建而來的,因此sqlSession得老祖宗就是它。在這里不管它的父輩,我們就來研究一下sqlSession的默認defaultSqlSession,多說無益看代碼
// 可以看到 DefaultSqlSession 實現了SqlSession,這很容易讓我們想起模板方法模式,請往下看 public class DefaultSqlSession implements SqlSession {// 下面是一些變量private Configuration configuration; // 這個東西是核心關注點也是此次重點講解點,它包含了Mybatis的所有配置信息,我們此次所要研究的就是configuration是如何將sql獲取到的private Executor executor;private boolean autoCommit;private boolean dirty;private List<Cursor<?>> cursorList;// 以下是對SqlSession方法的實現 @Overridepublic <T> T selectOne(String statement) {return this.<T>selectOne(statement, null);}@Overridepublic <T> T selectOne(String statement, Object parameter) {// Popular vote was to return null on 0 results and throw exception on too many.List<T> list = this.<T>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 be returned by selectOne(), but found: " + list.size());} else {return null;}}// 這個是獲取configuration的mapper的,也就是獲取sql語句的,這是重點講解的 @Overridepublic <T> T getMapper(Class<T> type) {return configuration.<T>getMapper(type, this);}public <T> void addMapper(Class<T> type) {
mapperRegistry.addMapper(type);
} // 獲取數據庫連接 @Override public Connection getConnection() { try { return executor.getTransaction().getConnection(); } catch (SQLException e) { throw ExceptionFactory.wrapException("Error getting a new connection. Cause: " + e, e); } } } // 而SqlSession又繼承了Closeable,很明顯Closeable就是關閉connection的,這里就不詳細說明 public interface SqlSession extends Closeable { <T> T selectOne(String statement); <T> T selectOne(String statement, Object parameter); .......此處省略,詳情可自己看源碼 }
以上是DefaultSqlSession的方法介紹,此次要從addMapper方法入手弄明白Mybatis它是如何獲取我們寫的SQL語句的,come on
// 以下是mapperRegistry public class MapperRegistry {public <T> void addMapper(Class<T> type) {if (type.isInterface()) {if (hasMapper(type)) {throw new BindingException("Type " + type + " is already known to the MapperRegistry.");}boolean loadCompleted = false;try {knownMappers.put(type, new MapperProxyFactory<T>(type));// It's important that the type is added before the parser is run// otherwise the binding may automatically be attempted by the// mapper parser. If the type is already known, it won't try.MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);parser.parse(); // 這是使用了MapperAnnotationBuilder的parse方法進行了解析,那么它是如何解析的呢,解析的又是什么?loadCompleted = true;} finally {if (!loadCompleted) {knownMappers.remove(type);}}}}..... 其他代碼就省略了 }接下來我們看它是如何解析的
public class MapperAnnotationBuilder {public void parse() {String resource = type.toString();if (!configuration.isResourceLoaded(resource)) { // 首先會判斷這個接口即Mapper是否加載過了loadXmlResource(); // 這是對mapper的xml文件進行解析configuration.addLoadedResource(resource); // 加載進去 assistant.setCurrentNamespace(type.getName());parseCache();parseCacheRef();Method[] methods = type.getMethods(); // 獲得當前Mapper接口的所有方法for (Method method : methods) {try {// issue #237if (!method.isBridge()) {parseStatement(method); // 對方法進行解析 }} catch (IncompleteElementException e) {configuration.addIncompleteMethod(new MethodResolver(this, method));}}}parsePendingMethods();} } // 這個方法也是MapperAnotationBuilder的 void parseStatement(Method method) {Class<?> parameterTypeClass = getParameterType(method); // 獲得參數類型LanguageDriver languageDriver = getLanguageDriver(method); SqlSource sqlSource = getSqlSourceFromAnnotations(method, // 這里是獲取sqlSource parameterTypeClass, languageDriver);if (sqlSource != null) {Options options = method.getAnnotation(Options.class);final String mappedStatementId = type.getName() + "." + method.getName();Integer fetchSize = null;Integer timeout = null;StatementType statementType = StatementType.PREPARED;ResultSetType resultSetType = ResultSetType.FORWARD_ONLY;SqlCommandType sqlCommandType = getSqlCommandType(method);boolean isSelect = sqlCommandType == SqlCommandType.SELECT;boolean flushCache = !isSelect;boolean useCache = isSelect;KeyGenerator keyGenerator;String keyProperty = "id";String keyColumn = null;if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {// first check for SelectKey annotation - that overrides everything elseSelectKey selectKey = method.getAnnotation(SelectKey.class);if (selectKey != null) {keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver);keyProperty = selectKey.keyProperty();} else if (options == null) {keyGenerator = configuration.isUseGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;} else {keyGenerator = options.useGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;keyProperty = options.keyProperty();keyColumn = options.keyColumn();}} else {keyGenerator = NoKeyGenerator.INSTANCE;}if (options != null) {if (FlushCachePolicy.TRUE.equals(options.flushCache())) {flushCache = true;} else if (FlushCachePolicy.FALSE.equals(options.flushCache())) {flushCache = false;}useCache = options.useCache();fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; //issue #348timeout = options.timeout() > -1 ? options.timeout() : null;statementType = options.statementType();resultSetType = options.resultSetType();}String resultMapId = null;ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);if (resultMapAnnotation != null) {String[] resultMaps = resultMapAnnotation.value();StringBuilder sb = new StringBuilder();for (String resultMap : resultMaps) {if (sb.length() > 0) {sb.append(",");}sb.append(resultMap);}resultMapId = sb.toString();} else if (isSelect) {resultMapId = parseResultMap(method);}assistant.addMappedStatement(mappedStatementId,sqlSource,statementType,sqlCommandType,fetchSize,timeout,// ParameterMapIDnull,parameterTypeClass,resultMapId,getReturnType(method),resultSetType,flushCache,useCache,// TODO gcode issue #577false,keyGenerator,keyProperty,keyColumn,// DatabaseIDnull,languageDriver,// ResultSetsoptions != null ? nullOrEmpty(options.resultSets()) : null);}}private SqlSource getSqlSourceFromAnnotations(Method method, Class<?> parameterType, LanguageDriver languageDriver) {try {Class<? extends Annotation> sqlAnnotationType = getSqlAnnotationType(method); // 這里會判斷方法是否使用了select等注解Class<? extends Annotation> sqlProviderAnnotationType = getSqlProviderAnnotationType(method); // 是否使用了provider注解if (sqlAnnotationType != null) {if (sqlProviderAnnotationType != null) {throw new BindingException("You cannot supply both a static SQL and SqlProvider to method named " + method.getName());}Annotation sqlAnnotation = method.getAnnotation(sqlAnnotationType);final String[] strings = (String[]) sqlAnnotation.getClass().getMethod("value").invoke(sqlAnnotation);return buildSqlSourceFromStrings(strings, parameterType, languageDriver);} else if (sqlProviderAnnotationType != null) {Annotation sqlProviderAnnotation = method.getAnnotation(sqlProviderAnnotationType);return new ProviderSqlSource(assistant.getConfiguration(), sqlProviderAnnotation);}return null;} catch (Exception e) {throw new BuilderException("Could not find value method on SQL annotation. Cause: " + e, e);}} private SqlSource getSqlSourceFromAnnotations(Method method, Class<?> parameterType, LanguageDriver languageDriver) {try {Class<? extends Annotation> sqlAnnotationType = getSqlAnnotationType(method);Class<? extends Annotation> sqlProviderAnnotationType = getSqlProviderAnnotationType(method);if (sqlAnnotationType != null) {if (sqlProviderAnnotationType != null) {throw new BindingException("You cannot supply both a static SQL and SqlProvider to method named " + method.getName());}Annotation sqlAnnotation = method.getAnnotation(sqlAnnotationType);final String[] strings = (String[]) sqlAnnotation.getClass().getMethod("value").invoke(sqlAnnotation);return buildSqlSourceFromStrings(strings, parameterType, languageDriver);} else if (sqlProviderAnnotationType != null) {Annotation sqlProviderAnnotation = method.getAnnotation(sqlProviderAnnotationType);return new ProviderSqlSource(assistant.getConfiguration(), sqlProviderAnnotation);}return null;} catch (Exception e) {throw new BuilderException("Could not find value method on SQL annotation. Cause: " + e, e);}}從上面兩個方法我們可以解釋為什么我們使用provider和直接在mapper接口方法上加select語句會有效
?
總結
以上是生活随笔為你收集整理的Mybatis原理初探的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 前端日拱一卒D9——ES6笔记之基础篇
- 下一篇: HaProxy介绍,安装及配置