Mybatis之源码剖析

Mybatis源码剖析

1、传统方式源码剖析

源码剖析-初始化

InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// 初始化工作的开始
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);

进入源码剖析:

// 1. 最初调用的build
public SqlSessionFactory build(InputStream inputStream) {
 	// 调用了重载方法
 	return build(inputStream, null, null);
 }
// 2. 调用重载方法
public SqlSessionFactory build(InputStream inputStream, String environment,
Properties properties) {
 	try {
 		// XMLConfigBuilder是专门解析mybatis的配置文件的类
		XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment,
			properties);
		// 这里又调用了一个重载方法,parser.parse()的返回值是Configuration对象
		return build(parser.parse());
	} catch (Exception e) {
 		throw ExceptionFactory.wrapException("Error building SqlSession.", e)
	}
}

Mybatis在初始化的时候,会将mybatis的配置信息全部加载到内存中,使用org.apache.ibatis.session.Configuration实例来维护,下面进入对配置文件解析部分:

首先对Configuration对象进行介绍:

Configuration对象结构和xml配置文件的对象几乎相同。 回顾一下xml中的配置标签有哪些: properties(属性),settings(设置),typeAliases(类型别名),typeHandlers(类型处理器),objectFactory(对象工厂),mappers(映射器)等Configuration也有对应的对象属性来封装它们,也就是说初始化配置文件的本质就是创建Configuration对象,将解析的xml数据封装到Configuration内部属性中

/**
 *  解析xml成configuration对象
 */
 public Configuration parse() {
 // 若已解析出,抛出BuilderException异常
 if (parsed) {
 	throw new BuilderException("Each XMLConfigBuilder can only be used
		once.");
 }
 // 标记已更新
 parsed = true;
 // 解析 XML configuration节点属性
 parseConfiguration(parser.evalNode("/configuration"));
 return configuration;
 }
 /**
 * 解析xml
 */
 private void parseConfiguration(XNode root) {
 try {
 	 // 解析<properties /> 标签
 	 propertiesElement(root.evalNode("properties"));
 	 // 解析 <settings /> 标签
	 Properties settings = settingsAsProperties(root.evalNode("settings"));
	 // 添加自定义VFS实现类
	 loadCustomVfs(settings);
	 // 解析 <typeAliases /> 标签
	 typeAliasesElement(root.evalNode("typeAliases"));
	 // 解析 <plugins /> 标签
	 pluginElement(root.evalNode("plugins"));
	 // 解析 <objectFactory /> 标签
	 objectFactoryElement(root.evalNode("objectFactory"));
	 // 解析 <objectWrapperFactory />标签
	 objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
	 // 解析<reflectorFactory />标签
	 reflectorFactoryElement(root.evalNode("reflectorFactory"));
	 // 赋值 <settings /> 到 Configuration 属性
	 settingsElement(settings);
	 // 解析 <environments />标签
	 environmentsElement(root.evalNode("environments"));
	 // 解析 <databaseIdProvider />标签
	 databaseIdProviderElement(root.evalNode("databaseIdProvider"));
	 // 解析<typeHandlers />标签
	 typeHandlerElement(root.evalNode("typeHandlers"));
	 // 解析 <mappers /> 标签
	 mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
 	throw new BuilderException("Error parsing SQL MapperConfiguration. Cause: " + e, e);
 }
 }

MappedStatement:

作用:MappedStatement与Mapper配置文件中的一个select/update/insert/delete节点相对应。mapper中配置的标签都被封装到了此对象中,主要用途描述一条SQL语句。初始化过程:回顾开始介绍的加载配置文件的过程,会对mybatis-config.xml中的各个标签都进行解析。其中mappers标签用来引入mapper.xml文件或者配置mapper接口的目录。

<select id="getUser" resultType="user" >
 select * from user where id=#{id}
 </select>

这样的一个select标签会在初始化配置文件时被解析成一个MappedStatement对象,然后存储在Configuration对象中mappedStatements属性中,mappedStatements是一个HashMap,存储时key=全限定类名 + 方法名,value=对应的MappedStatement对象。

  • 在configuration中对应的属性为
Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>
("Mapped Statements collection")
  • 在XMLConfigBuilder中的处理:
 private void parseConfiguration(XNode root) {
 	try {
 		// 这里省略其他标签的处理
 		mapperElement(root.evalNode("mappers"));
 	} catch (Exception e) {
 		throw new BuilderException("Error parsing SQL Mapper Configuration.
			Cause: " + e, e);
 	}
 }

到此对xml配置文件的解析就结束了,回到步骤2中调用重载build方法

// 5. 调用重载方法
public SqlSessionFactory build(Configuration config) {
 //创建了DefaultSqlSessionFactory对象,传入Configuration对象
 return new DefaultSqlSessionFactory(config);
 }

源码剖析-执行SQL流程

SqlSession是一个接口,它有两个实现类:DefaultSqlSession(默认)和SqlSessionManager(已弃用)
SqlSession是Mybatis中用于和数据库交互的顶层类,通常将它与ThreadLocal绑定,一个会话使用一个SqlSession,并且在使用完毕后需要close
public class DefaultSqlSession implements SqlSession {
 private final Configuration configuration;
 private final Executor executor;
 // SqlSession中两个重要的参数,configuration与初始化时相同,executor为执行器。

Executor

Executor是一个接口,常用三个实现类:
BatchExecutor	重用语句并执行批量更新
ReuseExecutor	重用预处理语句prepared statements
SimpleExecutor	普通执行器,默认
SqlSession sqlSession = factory.openSession();
List<User> list = sqlSession.selectList("com.my.mapper.UserMapper.getUserByName");

获取sqlSession

 //6. 进入selSession方法
 public SqlSession openSession() {
 	//getDefaultExecutorType()传递的是SimpleExecutor
 	return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
 }
 //7. 进入openSessionFromDataSource̶
 //ExecutorType 为Executor的类型 TransactionIsolationLevel为事务隔离级别,autoCommit是否开启事务
 //openSession的多个重载方法可以指定获得的SeqSession的Executor类型和事务的处理
 private SqlSession openSessionFromDataSource(ExecutorType execType,
 TransactionIsolationLevel level, boolean autoCommit) {
 	Transaction tx = null;
 	try {
 		final Environment environment = configuration.getEnvironment();
 		final TransactionFactory transactionFactory =
			getTransactionFactoryFromEnvironment(environment);
 		tx = transactionFactory.newTransaction(environment.getDataSource(),
			level, autoCommit);
 		//根据参数创建指定类型 Executor
 		final Executor executor = configuration.newExecutor(tx, execType);
 		//返回的是DefaultSqlSession
 		return new DefaultSqlSession(configuration, executor, autoCommit);
 	} catch (Exception e) {
 		closeTransaction(tx); // may have fetched a connection so lets call close()
}

执行sqlSession中的api

//8.进入selectList方法,多个重载方法
public <E> List<E> selectList(String statement) {
 	return this.selectList(statement, null);
}
public <E> List<E> selectList(String statement, Object parameter) {
 	return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
public <E> List<E> selectList(String statement, Object parameter, RowBounds
rowBounds) {
 try {
    //根据传入的全限定类名+方法名从映射的map中取出MappedStatement对象
 	MappedStatement ms = configuration.getMappedStatement(statement);
 	//调用Executor方法中的处理
 	//RowBounds用来逻辑分页
 	// wrapCollection(parameter)用来修饰集合或者数组参数
 	return executor.query(ms, wrapCollection(parameter), rowBounds,
		Executor.NO_RESULT_HANDLER);
 } catch (Exception e) {
 	throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
 } finally {
 	ErrorContext.instance().reset();
 }

源码剖析-Executor

继续源码中的步骤,进入executor.query()方法

//此方法在SimpleExecutor的父类BaseExecutor中实现
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds
rowBounds, ResultHandler resultHandler) throws SQLException {
 	//根据传入的参数动态获取SQL语句,最后返回用BoundSql对象表示
	BoundSql boundSql = ms.getBoundSql(parameter);
 	//为本次查询创建缓存的Key
 	CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
 	return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
//进入query重载方法中
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds
rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
	ErrorContext.instance().resource(ms.getResource()).activity("executing a
		query").object(ms.getId());
 	if (closed) {
 		throw new ExecutorException("Executor was closed.");
 	}
 	if (queryStack == 0 && ms.isFlushCacheRequired()) {
		clearLocalCache();
 	}
 	List<E> list;
 	try {
 		queryStack++;
 		list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
 		if (list != null) {
 			handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
     	} else {
 			// 如果缓存中没有本次查找的值,那么在数据库中查询
 			list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key,
				boundSql);
 		}
 	} finally {
 		queryStack--;
 	}
 	if (queryStack == 0) {
 		for (DeferredLoad deferredLoad : deferredLoads) {
 			deferredLoad.load();
 		}
 		// issue #601
 		deferredLoads.clear();
 		if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
 			// issue #482
 			clearLocalCache();
 		}
 	}
 	return list;
 }
//从数据库查询
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter,
RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql
boundSql) throws SQLException {
	List<E> list;
 	localCache.putObject(key, EXECUTION_PLACEHOLDER);
 	try {
 		// 查询的方法
 		list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
 	} finally {
 		localCache.removeObject(key);
 	}
 	// 将查询结果放入缓存
 	localCache.putObject(key, list);
 	if (ms.getStatementType() == StatementType.CALLABLE) {
 		localOutputParameterCache.putObject(key, parameter);
 	}
 	return list;
}
// SimpleExecutor实现父类中的doQuery抽象方法
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds
rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
	Statement stmt = null;
 	try {
 		Configuration configuration = ms.getConfiguration();
     	// 传入参数创建StatementHanlder对象来执行查询
 		StatementHandler handler = configuration.newStatementHandler(wrapper,
			ms, parameter, rowBounds, resultHandler, boundSql);
		// 创建jdbc中的statement对象
 		stmt = prepareStatement(handler, ms.getStatementLog());
 		// StatementHandler进行处理
 		return handler.query(stmt, resultHandler);
 	} finally {
 		closeStatement(stmt);
 	}
}
// 创建Statement的方法                                                                
private Statement prepareStatement(StatementHandler handler, Log statementLog)
throws SQLException {
	Statement stmt;
 	//条代码中的getConnection通过重重调用最后会调用openConnection方法,从连接池中获取连接
 	Connection connection = getConnection(statementLog);
 	stmt = handler.prepare(connection, transaction.getTimeout());
 	handler.parameterize(stmt);
 	return stmt;
}
                                                                
//从连接池中获取连接方法
protected void openConnection() throws SQLException {
	if (log.isDebugEnabled()) {
 		log.debug("Opening JDBC Connection");
	}
 	//从连接池中获取连接
 	connection = dataSource.getConnection();
 	if (level != null) {
 		connection.setTransactionIsolation(level.getLevel());
 	}
}

上述的Executor.query()经过多次转折,最终会创建一个StatementHandler对象,然后将必要的参数传递给StatementHandler,使用StatementHandler来完成对数据库的查询,返回List结果集。

从上面的代码可以看出,Executor的作用和功能是:

1、根据传递的参数,完成SQL语句的动态解析,生成BoundSql对象,供StatementHandler使用;
2、为查询创建缓存,以提高性能;
3、创建JDBC的Statement连接对象,传递给StatementHandler对象,返回List拆线呢结果。

源码剖析-StatementHandler

StatementHandler主要完成两个工作:

  • 对于JDBC的PrepareStatement类型的对象,创建的过程中,我们使用的是SQL语句字符串会包含若干个?占位符,我们其后对?占位符进行赋值。StatementHandler通过parameterize(statement)方法对Statement进行设值;
  • StatementHandler通过List query(Statement statement, ResultHandler resultHnadler)方法来完成执行Statement,和将Statement对象返回的ResultSet封装成List:

进入到StatemnetHandler的parameterize(statement) 方法的实现:

public void parameterize(Statement statement) throws SQLException {
 	// 使用ParameterHandler来完成对Statement的设值꧊
 	parameterHandler.setParameters((PreparedStatement) statement);
}
/**
*
*ParameterHandler类的setParameters(PreparedStatement ps) 实现
* 对某一个Statement进行设置参数
*/
public void setParameters(PreparedStatement ps) throws SQLException {
	ErrorContext.instance().activity("settingparameters")
     .object(mappedStatement.getParameterMap().getId());
 	List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
 	if (parameterMappings != null) {
 		for (int i = 0; i < parameterMappings.size(); i++) {
 			ParameterMapping parameterMapping = parameterMappings.get(i);
 			if (parameterMapping.getMode() != ParameterMode.OUT) {
 				Object value;
 				String propertyName = parameterMapping.getProperty();
 					if (boundSql.hasAdditionalParameter(propertyName)) {
 						value = boundSql.getAdditionalParameter(propertyName);
 					} else if (parameterObject == null) {
 						value = null;
 					} else if
						(typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())){
 						value = parameterObject;
 					} else {
 						MetaObject metaObject =
							configuration.newMetaObject(parameterObject);
 						value = metaObject.getValue(propertyName);
 					}

 //每一个Mapping都有一个TypeHandler,根据TypeHandler来对preparedStatement进行设置参数
 					TypeHandler typeHandler = parameterMapping.getTypeHandler();
 					JdbcType jdbcType = parameterMapping.getJdbcType();
 					if (value == null && jdbcType == null) {
                        jdbcType = configuration.getJdbcTypeForNull(); 
                        // 设置参数
                    }
            }
        }
    }
}

从上述代码可以看到,StatementHandler的parameterize(Statement)方法调用了ParameterHandler的setParameters(statement) 方法,ParameterHandler的setParameters(Statement)方法负责根据我们输入的参数,对statement对象的?占位符进行赋值。

进入StatementHandler的List query(Statement statement, ResultHandler resultHandler)方法的实现:

public <E> List<E> query(Statement statement, ResultHandler resultHandler)
throws SQLException {
	// 1.调用preparedStatemnt̶execute()方法,然后将resultSetԻ交给ResultSetHandler处理
 	PreparedStatement ps = (PreparedStatement) statement;
 	ps.execute();
 	//	2 ֵ使用ResultHandler来处理ResultSet
 	return resultSetHandler.<E> handleResultSets(ps);
} 

从上述代码我们可以看出,StatementHandler的List query(Statement statement, ResultHandler resultHandler)方法的实现,是调用了ResultSetHandler的handleResultSets(Statement)方法。

ResultSetHandler的handleResultSets(Statement)方法会将Statement语句执行生成的resultSet结果集转换成List结果集

public List<Object> handleResultSets(Statement stmt) throws SQLException {
	ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
 	//多个 ResultSet 的结果集合,每个 ResultSet 对应的一个 Object 对象 
	// 实际上每个Object 都是 List<Object> 对象
 	// 在不考虑存储过程的多个 ResultSet 的情况,普通的查询,实际上就是一个 ResultSet ҅
	// 也就是说,multipleResults 最多就一个元素
 	final List<Object> multipleResults = new ArrayList<>();
 	int resultSetCount = 0;
 	// 获得首个 ResultSet 对象,并封装成 ResultSetWrapper 对象
 	ResultSetWrapper rsw = getFirstResultSet(stmt);
 	// ឴获得 ResultMap 数组
 	// 在不考虑存储过程的多个 ResultSet的情况,普通的拆线呢,实际上就一个 ResultSet ҅
	// 也就是说,resultMaps 就一个元素
 	List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
 	validateResultMapsCount(rsw, resultMapCount); // 校验
 	while (rsw != null && resultMapCount > resultSetCount) {
 		// ឴获得 ResultMap 对象
 		ResultMap resultMap = resultMaps.get(resultSetCount);
 		// 处理 ResultSet ҅将结果添加到 multipleResults 中
 		handleResultSet(rsw, resultMap, multipleResults, null);
 		// ឴处理下一个 ResultSet 对象,并封装成 ResultSetWrapper 对象
 		rsw = getNextResultSet(stmt);
 		// 清理
 		cleanUpAfterHandlingResultSet();
 		// resultSetCount ++
 		resultSetCount++;
 }
 // 因为 `mappedStatement.resultSets` 只在存储过程中使用,暂时不考虑,忽略即可
 String[] resultSets = mappedStatement.getResultSets();
 if (resultSets != null) {
 	while (rsw != null && resultSetCount < resultSets.length) {
 		ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
 		if (parentMapping != null) {
 			String nestedResultMapId = parentMapping.getNestedResultMapId();
 			ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
 			handleResultSet(rsw, resultMap, null, parentMapping);
 		}
 		rsw = getNextResultSet(stmt);
 		cleanUpAfterHandlingResultSet();
 		resultSetCount++;
 	}
 }
 	// 如果是 multipleResults 单元素,则首元素返回
 	return collapseSingleResultList(multipleResults);
 }

2、Mapper代理方式

回顾下写法:

public static void main(String[] args) {
	// 前三步都相同
    InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
 	SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
 	SqlSession sqlSession = factory.openSession();

 	//这里不再调用SqlSession 的api҅,而是获得了接口对象,调用接口中的方法
 	UserMapper mapper = sqlSession.getMapper(UserMapper.class);
 	List<User> list = mapper.getUserByName("tom");
}

思考一个问题,通常Mapper接口我们都没有实现的方法却可以使用,是为什么呢?

答案就是动态代理

开始之前介绍下Mybatis初始化时对接口的处理:MapperRegistry是Configuration中的一个属性,它内部维护一个HashMap用于存放mapper接口的工厂类,每个接口对应一个工厂类。mappers中可以配置接口的包路径,或者某个具体的接口类。

<mappers>
 <mapper class="com.my.mapper.UserMapper"/>
 <package name="com.my.mapper"/>
</mappers>
  • 当解析mappers标签时,它会判断解析到的是mapper配置文件时,会再将对应配置文件中的增删改查标签一一封装成MappedStatement对象,存入mappedStatement对象中,当判断解析接口时,会建此接口对应的MapperProxyFactory对象,存入HashMap中,key=接口字节码对象,value=此接口对应的MapperProxyFactory对象。

源码剖析:getMapper()

进入SqlSession.getMapper(UserMapper.class)中

//DefaultSqlSession中的getMapper
public <T> T getMapper(Class<T> type) {
	return configuration.<T>getMapper(type, this);
}
//configuration中的给getMapper
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
	return mapperRegistry.getMapper(type, sqlSession);
}
//MapperRegistry中的getMapper
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
	//从MapperRegistry中的HashMap中拿MapperProxyFactory
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>)
		knownMappers.get(type);
 	if (mapperProxyFactory == null) {
 		throw new BindingException("Type " + type + " is not known to the" +
			"MapperRegistry.");
 	}
 	try {
 		// 通过动态代理工厂生成示例
 		return mapperProxyFactory.newInstance(sqlSession);
 	} catch (Exception e) {
 		throw new BindingException("Error getting mapper instance. Cause: " + e,
			e);
 	}
}
//MapperProxyFactory类中的newInstance方法
public T newInstance(SqlSession sqlSession) {
	// 创建了JDK动态代理Handler类
 	final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession,
		mapperInterface, methodCache);
 	// 调用了重载方法
 	return newInstance(mapperProxy);
}
//MapperProxy类,实现了InvocationHandler接口
public class MapperProxy<T> implements InvocationHandler, Serializable {

 	//省略部分源码
 	private final SqlSession sqlSession;
 	private final Class<T> mapperInterface;
 	private final Map<Method, MapperMethod> methodCache;

 	// 构造传入SqlSession҅,说明每个session代理对象不同
 	public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface,
	Map<Method, MapperMethod> methodCache) {
 		this.sqlSession = sqlSession;
 		this.mapperInterface = mapperInterface;
 		this.methodCache = methodCache;
 	}

 	//省略部分源码
}

源码剖析-invoke()

在动态代理返回了示例后,我们就可以直接调用mapper类中的方法了,但代理对象调用方法,执行是在MapperProxy中的invoke方法中

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
	try {
 		// 如果是 Object 定义的方法直接调用
 		if (Object.class.equals(method.getDeclaringClass())) {
 			return method.invoke(this, args);
 		} else if (isDefaultMethod(method)) {
 			return invokeDefaultMethod(proxy, method, args);
 		}
 	} catch (Throwable t) {
 		throw ExceptionUtil.unwrapThrowable(t);
 	}
 	// 获得 MapperMethod 对象
 	final MapperMethod mapperMethod = cachedMapperMethod(method);
 	// 重点在这,MapperMethod๋ 最终调用了执行的方法
 	return mapperMethod.execute(sqlSession, args);
}

进入execute方法:

public Object execute(SqlSession sqlSession, Object[] args) {
	Object result;
 	//判断mapper中的方法类型,最终调用的还是SqlSession中的方法
 	switch (command.getType()) {
 		case INSERT: {
 			// 转换参数
 			Object param = method.convertArgsToSqlCommandParam(args);
 			// 执行 INSERT 操作
 			// 转换 rowCount
 			result = rowCountResult(sqlSession.insert(command.getName(), param));
 			break;
 		}
 		case UPDATE: {
 			// 转换参数
 			Object param = method.convertArgsToSqlCommandParam(args);
 			// 转换 rowCount
 			result = rowCountResult(sqlSession.update(command.getName(), param));
 			break;
 		}
 		case DELETE: {
 			// 转换参数
 			Object param = method.convertArgsToSqlCommandParam(args);
 			// 转换 rowCount
 			result = rowCountResult(sqlSession.delete(command.getName(), param));
			break;
 		}
 		case SELECT: {
 			// 无返回,并且有 ResultHandler 方法参数,则将查询的结果,提交给 ResultHandler 进行处理
 			if (method.returnsVoid() && method.hasResultHandler()) {
 				executeWithResultHandler(sqlSession, args);
 				result = null;
 				// 执行查询返回列表
 			} else if (method.returnsMany()) {
 				result = executeForMany(sqlSession, args);
 				// 执行查询返回Map
 			} else if (method.returnsMap()) {
 				result = executeForMap(sqlSession, args);
 				// 执行查询返回Cursor
 			} else if (method.returnsCursor()) {
 				result = executeForCursor(sqlSession, args);
 				// 执行查询返回单个参数
 			} else {
 				// 转换参数
 				Object param = method.convertArgsToSqlCommandParam(args);
                // 查询单条
 				result = sqlSession.selectOne(command.getName(), param);
 				if (method.returnsOptional() && (result == null ||
					!method.getReturnType().equals(result.getClass()))) {
 					result = Optional.ofNullable(result);
 				}
 			}
 			break;
        }
		case FLUSH:
 			result = sqlSession.flushStatements();
 			break;
 			default:
 			throw new BindingException("Unknown execution method for: " +
				command.getName());
 	}
 	// 返回结果为 null ҅并且返回类型为基本类型,BindingException 异常
 	if (result == null && method.getReturnType().isPrimitive() &&
			!method.returnsVoid()) {
 		throw new BindingException("Mapper method '" + command.getName()
 			+ " attempted to return null from a method with a primitive
		return type (" + method.getReturnType() + ").");
 	}
 	// 返回结果
 	return result;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值