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