MyBatis中Executor源码解析之BatchExecutor搞不懂 不了解jdbc,何谈Mybatis的源码解析? Ex 从mybatis源码看JDK动态代理

为了便于源码分析,还是先来一个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">
        <environment id="dev">
            <!--指定事务管理的类型,这里简单使用Java的JDBC的提交和回滚设置-->
            <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>

入口方法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 }

这个Executor的代码离上面Demo执行代码还有一段很长封装,如果分析Executor,就要分析分析这段很长的封装代码;

这个源码该怎么开始才能让人觉得水到渠成,顺其自然呢?

算了,还是硬着头皮一步一步来吧;

第一步:build过程中,如何获取到 defaultExecutorType配置

 

  第75行实例化一个XMLConfigBuilder对象,这是一个xml解析器。

第78行调用第91行的build方法,这个方法的参数是个Configuration对象,那么parser.parse()方法返回的一定是一个Configuration对象;

扫描二维码关注公众号,回复: 10755984 查看本文章

换句话说就是在parser.parse()中读取的配置文件,并且赋值给configuratiion对象。

parser.parse()源码:

 parseConfiguration()源码

 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 }

根据方法名大概能推断出处理的都是那些配置;直接看13行的代码吧。

settingsElement()源码:

 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 }

看看第11行代码就是给configuration明确defaultExecutorType值的。

getProperty(key,defaultValue)有两个参数,第一个参数是属性的键值,根据这个键值获取属性值,如果获取不到就用第二个参数作为默认值。
如果没有配置 <setting name="defaultExecutorType" value="SIMPLE"/>,则用SIMPLE值。
说到这就来说说defaultExecutorType有哪些参数
SIMPLE:就是普通的执行器
REUSE: 执行器会重(读:虫)用预处理语句(PreparedStatements)
BATCH 执行器将重(虫)用语句并执行批量更新。

我一开始的看到官方的内容既不知道这三个什么意思,也不知道那个字到底读chong,还是读zhong.
先改参数跑Demo看结果。
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultExecutorType" value="REUSE"/>
这两个配置分别配置后,都可以正常的往数据库中插入一条数据,并且返回影响行数是1,只有
<setting name="defaultExecutorType" value="BATCH"/>
这条配置返回的数据是下面这个奇怪的东西;也没有往数据库中插入数据,也不抛异常。
受影响的行数-2147482646
1.SIMPLE和REUSE配置都能正常运行,那么区别在哪?
2.BATCH配置返回的奇怪的数据是什么东西,为什么不成功。

说了这么久都还没有说Executor是干什么的。
Executor代表执行器,有它来调度StatementdHandler,ParameterHandler,ResultHandler等来执行对应的SQL.
对于JDBC熟悉一点的人对上面几个Handler名字应该有点熟悉。如果忘记了JDBC,看看这篇博客:不了解jdbc,何谈Mybatis的源码解析?
Ex
ecutor既然是总调度,那么它应该是在MyBatis中Mapper的JDK动态代理的地方开始调用的。
如果不清楚这个动态代理,可以看看这篇博客:从mybatis源码看JDK动态代理
可以从Demo中的第四行代码getMapper()方法往下追。

 

 

 

 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 }

getMapper()的调用顺序: 

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

根据最后一张图的46和47行代码,结合JDK动态代理的调用规则,可以推断MapperProxy中一定实现了  InvocationHandler 接口;并且实现了invoke方法。

 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 //从缓存中查找是否有该方法,并且包装成MapperMethod对象 18 final MapperMethod mapperMethod = cachedMapperMethod(method); 19 //这个暂时不知道执行的什么,只能继续往下追 20 return mapperMethod.execute(sqlSession, args); 21 }

execute()源码:



 



猜你喜欢

转载自www.cnblogs.com/guoyansi19900907/p/12691459.html
今日推荐