Mybatis編寫sql有兩種方式,即經過xml和註解,我我的比較喜歡xml配置,可是註解仍是要了解下的。而且Mybatis中xml優先於註解加載,也就是若是DAO接口中的方法有對應的xml配置,再加入註解會拋異常,若是兩個都沒配置,在調用DAO方法時再拋異常。java
Mybatis會把編寫的sql語句信息封裝成一個MappedStatement對象,加載xml中sql信息從XMLMapperBuilder#buildStatementFromContext()
開始,一路調用到MapperBuilderAssistant#addMappedStatement
完成添加,而加載註解最終也會經過MapperBuilderAssistant類。其中 this.configuration.addMappedStatement(statement);
就是最終添加到配置類中的map集合中,先無論。
sql
那麼開始加載註解從什麼地方開始呢?
一樣是從XMLMapperBuilder類中,在bindMapperForNamespace()方法下開始,完成XML加載以後被調用。
加載註解經過代碼跟蹤一直在MapperRegistry下的addMapper中。此時參數是DAO的class,首先判斷是否是接口,以及是否被添加過了。其次,開始new 一個MapperAnnotationBuilder對象開始處理接口中方法上的註解。mybatis
public <T> void addMapper(Class<T> type) { //是否是接口 if (type.isInterface()) { //是否已被添加 if (this.hasMapper(type)) { throw new BindingException("Type " + type + " is already known to the MapperRegistry."); } boolean loadCompleted = false; try { //添加 this.knownMappers.put(type, new MapperProxyFactory(type)); //開始註解解析 MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type); parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { this.knownMappers.remove(type); } } } }
MapperAnnotationBuilder的parseStatement方法中處理每個DAO層方法上的註解,其中getSqlSourceFromAnnotations返回一個SqlSource對象,爲空則方法上不存在註解信息,什麼也不作,不爲空最終生成各類須要的信息調用MapperBuilderAssistant下的addMappedStatement方法試圖添加到Configuration中的mappedStatements集合中。app
void parseStatement(Method method) { Class<?> parameterTypeClass = this.getParameterType(method); LanguageDriver languageDriver = this.getLanguageDriver(method); SqlSource sqlSource = this.getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver); if (sqlSource != null) { Options options = (Options)method.getAnnotation(Options.class); String mappedStatementId = this.type.getName() + "." + method.getName(); Integer fetchSize = null; Integer timeout = null; StatementType statementType = StatementType.PREPARED; ResultSetType resultSetType = ResultSetType.FORWARD_ONLY; SqlCommandType sqlCommandType = this.getSqlCommandType(method); boolean isSelect = sqlCommandType == SqlCommandType.SELECT; boolean flushCache = !isSelect; boolean useCache = isSelect; String keyProperty = "id"; String keyColumn = null; //-----此處省略不知道多少行代碼
那爲何要說試圖添加呢?由於最終存放MappedStatement的是一個Map,Mybatis本身實現了個靜態內部類StrictMap繼承HashMap,在put中判斷了有沒有相同的key,若是相同則拋出異常,也就是間接讓你xml和註解二選一。ide
Mybatis還有另外4個註解,也就是SelectProvider、InsertProvider、UpdateProvider、DeleteProvider,
從getSqlSourceFromAnnotations
中能夠看出,原來的Insert、Select、Update、Delete和SelectProvider、InsertProvider、UpdateProvider、DeleteProvider只能二選一。源碼分析
private SqlSource getSqlSourceFromAnnotations(Method method, Class<?> parameterType, LanguageDriver languageDriver) { try { //獲取方法上Insert、Select、Update、Delete中其中一個註解 Class<? extends Annotation> sqlAnnotationType = this.getSqlAnnotationType(method); //獲取SelectProvider、InsertProvider、UpdateProvider、DeleteProvider中其中一個註解 Class<? extends Annotation> sqlProviderAnnotationType = this.getSqlProviderAnnotationType(method); Annotation sqlProviderAnnotation; if (sqlAnnotationType != null) { if (sqlProviderAnnotationType != null) { //若是兩種註解都存在,則拋出異常 throw new BindingException("You cannot supply both a static SQL and SqlProvider to method named " + method.getName()); } else { sqlProviderAnnotation = method.getAnnotation(sqlAnnotationType); //獲取值,也就是sql語句 String[] strings = (String[])((String[])sqlProviderAnnotation.getClass().getMethod("value").invoke(sqlProviderAnnotation)); //生成SqlSource對象 return this.buildSqlSourceFromStrings(strings, parameterType, languageDriver); } } else if (sqlProviderAnnotationType != null) { sqlProviderAnnotation = method.getAnnotation(sqlProviderAnnotationType); //ProviderSqlSource實現了SqlSource return new ProviderSqlSource(this.assistant.getConfiguration(), sqlProviderAnnotation, this.type, method); } else { return null; } } catch (Exception var8) { throw new BuilderException("Could not find value method on SQL annotation. Cause: " + var8, var8); } }
public interface IUserDao { @ResultMap({"BaseResultMap"}) @SelectProvider(type = SelectSql.class,method = "createSelectSql") List<UserEntity> select(); class SelectSql{ public String createSelectSql(){ return new SQL(){{ SELECT("*"); FROM("tb_user"); }}.toString(); } } }
測試代碼測試
public static void main( String[] args ) { String resource = "mybatis-config.xml"; try { InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory build = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession2 = build.openSession(); IUserDao mapper = sqlSession2.getMapper(IUserDao.class); System.out.println(mapper.select()); } catch (IOException e) { e.printStackTrace(); } }
end....fetch