Let me show you how Mybatis annotation works

There are two ways for Mybatis to write sql, i.e. through xml and annotation. I prefer xml configuration, but annotation needs to be understood. In addition, xml in Mybatis takes precedence over annotation loading, that is, if the method in DAO interface has corresponding xml configuration, adding annotation will throw exception. If neither is configured, throw exception when calling DAO method.

Source code analysis
1, sql to MappedStatement in XML

Mybatis will package the prepared sql statement information into a MappedStatement object, and load the sql information in xml from
Xmlmapperbuilder ා buildstatementfromcontext(), call MapperBuilderAssistant ා addmappedstatement all the way to finish adding, and the loading annotation will finally pass MapperBuilderAssistant class. this.configuration.addMappedStatement(statement); is finally added to the map collection in the configuration class, regardless of the first.

2, Annotation to MappedStatement

So where do I start loading annotations?
It also starts from the XMLMapperBuilder class under the bindMapperForNamespace() method and is called after the XML load is completed.
Loading annotations is always in addMapper under MapperRegistry through code tracing. At this time, the parameter is DAO class. First, judge whether the interface is added or not. Second, start a MapperAnnotationBuilder object to start processing annotations on Methods in the interface.

 public <T> void addMapper(Class<T> type) {
 	 //Is it an interface
     if (type.isInterface()) {
     	//Has it been added
         if (this.hasMapper(type)) {
             throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
         }
         boolean loadCompleted = false;
         try {
         	//Add to
             this.knownMappers.put(type, new MapperProxyFactory(type));
             //Start annotation parsing
             MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type);
             parser.parse();
             loadCompleted = true;
         } finally {
             if (!loadCompleted) {
                 this.knownMappers.remove(type);
             }
         }
     }
 }

The parseStatement method of MapperAnnotationBuilder processes the annotations on each DAO layer method. getSqlSourceFromAnnotations returns a SqlSource object. If it is empty, there is no annotation information on the method. If it is not empty, all kinds of required information will be generated. Call the addMappedStatement method under MapperBuilderAssistant to try to add it to the map in Configuration In the pedstatements collection.

 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;
         //-----Don't know how many lines of code are omitted here

So why try to add? Because the final storage of MappedStatement is a Map. Mybatis implements a static internal class, StrictMap, which inherits HashMap. In put, it judges whether there is the same key. If there is the same key, it throws an exception, that is, you can choose one of xml and annotation.

3, sqlProviderAnnotation

Mybatis also has four other annotations, namely SelectProvider, InsertProvider, UpdateProvider and DeleteProvider,
From getSqlSourceFromAnnotations, we can see that the original Insert, Select, Update, Delete and SelectProvider, InsertProvider, UpdateProvider and DeleteProvider can only choose one of them.

private SqlSource getSqlSourceFromAnnotations(Method method, Class<?> parameterType, LanguageDriver languageDriver) {
    try {
    	//Get one of the notes in Insert, Select, Update and Delete on the method
        Class<? extends Annotation> sqlAnnotationType = this.getSqlAnnotationType(method);
        //Get one of the comments in SelectProvider, InsertProvider, UpdateProvider and DeleteProvider
        Class<? extends Annotation> sqlProviderAnnotationType = this.getSqlProviderAnnotationType(method);
        Annotation sqlProviderAnnotation;
        if (sqlAnnotationType != null) {
            if (sqlProviderAnnotationType != null) {
            //If both annotations exist, an exception is thrown
                throw new BindingException("You cannot supply both a static SQL and SqlProvider to method named " + method.getName());
            } else {
            	
                sqlProviderAnnotation = method.getAnnotation(sqlAnnotationType);
                //Get value, that is, sql statement
                String[] strings = (String[])((String[])sqlProviderAnnotation.getClass().getMethod("value").invoke(sqlProviderAnnotation));				
                //Generate SqlSource object
                return this.buildSqlSourceFromStrings(strings, parameterType, languageDriver);
            }
        } else if (sqlProviderAnnotationType != null) {
            sqlProviderAnnotation = method.getAnnotation(sqlProviderAnnotationType);
            //ProviderSqlSource implements 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);
    }
}
4, Use of SQL ProviderAnnotation
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();
          }
     }
}

Test code

 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...

Published 16 original articles, won praise 7, visited 5895
Private letter follow

Tags: xml SQL Mybatis

Posted on Fri, 31 Jan 2020 12:31:16 -0800 by Eckstra