为了便于源码分析,还是先来一个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对象;
换句话说就是在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的源码解析?
Executor既然是总调度,那么它应该是在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()源码: