The BatchExecutor of the Executor source code analysis in MyBatis can't understand or understand jdbc, what about the source code analysis of Mybatis? Ex see the JDK dynamic agent from the source of mybatis

In order to facilitate source code analysis, let's first come to a MyBatis demo

mybatis-mysql-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
    <properties>
        <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/gys?serverTimezone=UTC"/>
    </properties>
    <settings>
        <setting name="defaultExecutorType" value="SIMPLE"/>
    </settings>
    <!--环境配置,连接的数据库,这里使用的是MySQL-->
    <environments default="dev">
            <!-Specify the type of transaction management, here simply use Java's JDBC submission and rollback settings->
        <environment id = "dev">
            <transactionManager type="JDBC" />
            <!--dataSource 指连接源配置,POOLED是JDBC连接对象的数据源连接池的实现-->
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"></property>
                <property name="url" value="${url}"></property>
                <property name="username" value="root"></property>
                <property name="password" value="gys"></property>
            </dataSource>
        </environment>       
    </environments>
    <mappers>   
        <mapper resource="mapper/user.xml"></mapper>
    </mappers>
</configuration>

user.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="dao.IUserMapper">  

    <insert id="insertUser" parameterType="model.User">
        insert into user
        (name,age)
        values
        (#{name},#{age})
    </insert>

</mapper>

Entry method main:

 1     public static void main(String[] args) throws Exception {
 2         SqlSessionFactory sqlSessionFactory1=new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-mysql-config.xml"),"dev");
 3         SqlSession sqlSession1= sqlSessionFactory1.openSession(true);
 4         IUserMapper userMapper=sqlSession1.getMapper(IUserMapper.class);
 5         User user=new User();
 6         user.setAge(28); 7 user.setName("a"); 8 int i=userMapper.insertUser(user); 9 System.out.println("受影响的行数"+i); 10  sqlSession1.close(); 11 }

The Executor code is still a long package away from the above Demo execution code. If you analyze the Executor, you must analyze and analyze this very long package code;

How can this source code start to make people feel natural and let it go?

Forget it, let ’s do it step by step;

The first step: how to get the defaultExecutorType configuration during the build process

 

  Line 75 instantiates an XMLConfigBuilder object, which is an xml parser.

Line 78 calls the build method on line 91. The parameter of this method is a Configuration object, so the parser.parse () method must return a Configuration object;

In other words, it is the configuration file read in parser.parse () and assigned to the configuratiion object.

Parser.parse () source code:

 parseConfiguration () source code

 1 private void parseConfiguration(XNode root) {
 2     try {     
 3       propertiesElement(root.evalNode("properties"));
 4       Properties settings = settingsAsProperties(root.evalNode("settings"));
 5       loadCustomVfs(settings);
 6  loadCustomLogImpl(settings); 7 typeAliasesElement(root.evalNode("typeAliases")); 8 pluginElement(root.evalNode("plugins")); 9 objectFactoryElement(root.evalNode("objectFactory")); 10 objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); 11 reflectorFactoryElement(root.evalNode("reflectorFactory")); 12 //解析settings中的内容 13  settingsElement(settings); 14 environmentsElement(root.evalNode("environments")); 15 databaseIdProviderElement(root.evalNode("databaseIdProvider")); 16 typeHandlerElement(root.evalNode("typeHandlers")); 17 mapperElement(root.evalNode("mappers")); 18 } catch (Exception e) { 19 throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); 20  } 21 }

Based on the method name, it can be inferred that the configuration is handled; see the 13 lines of code directly.

SettingsElement () source code:

 1  private void settingsElement(Properties props) {
 2     configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
 3     configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
 4     configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
 5     configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
 6     configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false)); 7 configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false)); 8 configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true)); 9 configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true)); 10 configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false)); //这里是给执行器赋值的
11   configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE"))); 12 configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null)); 13 configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null)); 14 configuration.setDefaultResultSetType(resolveResultSetType(props.getProperty("defaultResultSetType"))); 15 configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false)); 16 configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false)); 17 configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION"))); 18 configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER"))); 19 configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString")); 20 configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true)); 21 configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage"))); 22 configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler"))); 23 configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false)); 24 configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true)); 25 configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false)); 26 configuration.setLogPrefix(props.getProperty("logPrefix")); 27 configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory"))); 28 }

Look at the 11th line of code is to give the configuration a clear defaultExecutorType value.

getProperty (key, defaultValue) has two parameters. The first parameter is the key value of the property. According to this key value, the property value is obtained. If it is not obtained, the second parameter is used as the default value. 
If <setting name = "defaultExecutorType" value = "SIMPLE" /> is not configured, use the SIMPLE value.
Speaking of which, let's talk about what parameters the defaultExecutorType has
SIMPLE: It is an ordinary executor. 
REUSE: The executor will repeat (read: bug) with prepared statements (PreparedStatements). The
BATCH executor will repeat (bug) statements and perform batch updates.

At the beginning, I saw that the official content did not know what the three meant, nor did I know whether to read chong or zhong.
Change the parameters first and run Demo to see the result.
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultExecutorType" value="REUSE"/>
After these two configurations are configured separately, you can insert a piece of data into the database normally, and the number of affected rows is 1, only
<setting name = "defaultExecutorType" value = " BATCH " /> 
The data returned by this configuration is the following strange thing; it does not insert data into the database, nor throw an exception.
Number of affected rows -2147482646
1. SIMPLE and REUSE configurations can run normally, so what is the difference? 
2. What is the strange data returned by the BATCH configuration, and why is it unsuccessful.

I haven't said what Executor does for so long.
Executor stands for executor, and it is used to schedule StatementdHandler, ParameterHandler, ResultHandler, etc. to execute the corresponding SQL.
Those who are familiar with JDBC should be familiar with the names of the above Handlers. If you forget JDBC, take a look at this blog: If you do n’t understand jdbc, what about the source code analysis of Mybatis?
Since the Ex ecutor is the total schedule, it should be called at the place of the Mapper JDK dynamic proxy in MyBatis.
If you are not clear about this dynamic proxy, you can take a look at this blog: From mybatis source code
, you can see that the JDK dynamic proxy can be traced down from the fourth line of code in the Demo, the getMapper () method.

 

 

 

 

 

 

 

 

 

 1 public Object execute(SqlSession sqlSession, Object[] args) {
 2     Object result;
 3     switch (command.getType()) {
 4       case INSERT: {//执行inset语句
 5         Object param = method.convertArgsToSqlCommandParam(args);
 6         result = rowCountResult(sqlSession.insert(command.getName(), param));
 7         break; 8  } 9 case UPDATE: {//update语句 10 Object param = method.convertArgsToSqlCommandParam(args); 11 result = rowCountResult(sqlSession.update(command.getName(), param)); 12 break; 13  } 14 case DELETE: {//执行delete语句 15 Object param = method.convertArgsToSqlCommandParam(args); 16 result = rowCountResult(sqlSession.delete(command.getName(), param)); 17 break; 18  } 19 case SELECT://执行select语句 20 if (method.returnsVoid() && method.hasResultHandler()) { 21  executeWithResultHandler(sqlSession, args); 22 result = null; 23 } else if (method.returnsMany()) { 24 result = executeForMany(sqlSession, args); 25 } else if (method.returnsMap()) { 26 result = executeForMap(sqlSession, args); 27 } else if (method.returnsCursor()) { 28 result = executeForCursor(sqlSession, args); 29 } else { 30 Object param = method.convertArgsToSqlCommandParam(args); 31 result = sqlSession.selectOne(command.getName(), param); 32 if (method.returnsOptional() 33 && (result == null || !method.getReturnType().equals(result.getClass()))) { 34 result = Optional.ofNullable(result); 35  } 36  } 37 break; 38  } 39 40 return result; 41 }

 

The calling sequence of getMapper (): 

defaultSqlSession.getMapper()==》configuration.getMapper()==>MapperRegistry.getMapper()==>MapperProxyFactory.newInstance();

According to the 46 and 47 lines of code in the last picture, combined with the calling rules of the JDK dynamic proxy, it can be inferred that the InvocationHandler interface must be implemented in MapperProxy; and the invoke method is implemented.

 1   @Override
 2   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 3     try {
 4      //调用object中的方法
 5       if (Object.class.equals(method.getDeclaringClass())) {
 6         return method.invoke(this, args);
 7       } else if (method.isDefault()) {//调用接口中的默认方法
 8         if (privateLookupInMethod == null) { 9 return invokeDefaultMethodJava8(proxy, method, args); 10 } else { 11 return invokeDefaultMethodJava9(proxy, method, args); 12  } 13  } 14 } catch (Throwable t) { 15 throw ExceptionUtil.unwrapThrowable(t); 16 } 17 // Find whether there is this method from the cache, and wrap it into a MapperMethod object 18 final MapperMethod mapperMethod = cachedMapperMethod (method); 19 // I do n’t know what to execute temporarily, I can only continue to chase down 20 return mapperMethod.execute (sqlSession, args); 21}

execute () source code:

 



 

 



Guess you like

Origin www.cnblogs.com/guoyansi19900907/p/12691459.html