1.前言-背景介绍
上节我们处理了参数处理器,本节我们处理结果集处理器,之前我们写了一个DefaultResultSetHandler,我们把返回结果获取对象,填充值什么的写到了一起,流程没有进行解耦,并且只接收了Object的对象值类型,需要多元化,所以本节也要解决按返回类型从结果集中获取对应的结果类型值。
2.设计说明
2.1.结果映射ResultMap
我们要得到ResultType和ResultMap(本节先处理ResultType)才能知道用户最终要的是什么对象,当然定位还是要靠namespace,这时候我们需要一个类ResultMap结果映射类,内容有id(namespace+其他内容拼接)以及结果类型,还有具体结果的映射字段信息以及定义已映射字段如下第二个图片
我们看到ResultMap里有ResultMapping,这就是结果集映射里每一个字段信息(本节暂时没用上,只做定义),可以看到定义了属性,列名,java类型,jdbc类型,类型处理器
2.2 MapperBuilderAssistant助手类
为什么加这个类呢?
刚才我们说除了结果的映射构建需要namespce,其实前面章节参数也需要用到这里,而参数映射结果集都需要映射,有一些共通性,所以我们新增加一个类MapperBuilderAssistant(构建mapper助手)去帮助我们构建Mapper映射,目前参数和结果都在此类映射处理。
2.3 ResultSetHandler结果集处理
结果集处理器按字面理解就是将JDBC得到的结果集数据在此处理,最后得到返回对象结果,想要返回配置的对象而且有值,就必须解析xml中的resultType并实例化对象,并从JDBC数据集里获取属性名称,根据属性名称反射设置值并将有值的返回对象存储到结果处理器里并返回,大致的设计业务就是这样处理的。
结果包装器ResultSetWrapper
结果包装器的设计理念是想要将结果集的一些基本信息的逻辑处理到此类里,如JDBC的结果集呀,属性名称啊,jdbc类型,已映射的属性数据、未映射的属性等等处理,为结果集处理器提供数据支持。
结果上下文DefaultResultContext
结果上下文设计这个主要是每一行数据对象要存储到此类全局变量里。
结果处理器DefaultResultHandler
结果处理器设计为将每一个上下文的对象结果数据存储到List里,就可支持多个数据了。
3.UML类图
依旧和参数处理器的思路一样,分为构建处理周期以及使用周期,构建是从解析XML配置开始的,使用则是执行了数据库操作以后我们要用到处理结果的一系列流程。
大家可以看下,涉及新增的类不多,也都比较简单可以看看,其中UML中的关系MapperStatement和XmlStatementBuilder属于聚合关系,聚合关系就是比依赖要强一些的关系,整体与部分,MapperStatement就是XmlStatementBuilder的一部分协助处理映射功能的。
而ResultMapping和ResultMap就是组合关系,组合关系则要比聚合关系关联性还要强,组合则是整体与部分不分开,同根同源,同生命周期,这里呢resultMapping与ResultMap就是一个生命周期,有了ResultMapping才有ResultMap。
4.代码实现
4.1 解析构建结果映射
4.1.1 结果映射属性封装
ResultMap,结果映射,属性关于namespce和返回结果类型以及返回具体的属性信息
public class ResultMap {
private String id;
private Class<?> type;
private List<ResultMapping> resultMappings;
private Set<String> mappedColumns;
public ResultMap() {
}
public static class Builder {
private ResultMap resultMap = new ResultMap();
public Builder(Configuration configuration, String id, Class<?> type, List<ResultMapping> resultMappings) {
resultMap.id = id;
resultMap.type = type;
resultMap.resultMappings = resultMappings;
}
public ResultMap build() {
resultMap.mappedColumns = new HashSet<>();
return resultMap;
}
}
public String getId() {
return id;
}
public Class<?> getType() {
return type;
}
public List<ResultMapping> getResultMappings() {
return resultMappings;
}
public Set<String> getMappedColumns() {
return mappedColumns;
}
}
ResultMapping,具体的映射属性数据,暂时内容还没有用到,现在代码先写上
/**
* @Author df
* @Date 2023/3/28 12:38
* @Version 1.0
* 结果映射的每一个字段的信息
*/
public class ResultMapping {
private Configuration configuration;
private String property;
private String column;
private Class<?> javaType;
private JdbcType jdbcType;
private TypeHandler<?> typeHandler;
ResultMapping() {
}
public static class Builder {
private ResultMapping resultMapping = new ResultMapping();
}
}
4.1.2 绑定映射结果
MapperBuilderAssistant,协助XmlStatementBuilder去处理Mapper的映射,这样在XmlStatementBuilder就可以去掉映射这部分代码,专心处理解析,只需调用映射方法即可,其中映射返回结果的数据在setStatementResultMap方法中,最后得到的ResultMap对象存储到MappedStatement里
public class MapperBuilderAssistant extends BaseBuilder {
private String currentNamespace;
private String resource;
public MapperBuilderAssistant(Configuration configuration, String resource) {
super(configuration);
this.resource = resource;
}
public String getCurrentNamespace() {
return currentNamespace;
}
public void setCurrentNamespace(String currentNamespace) {
this.currentNamespace = currentNamespace;
}
/**
* 将当前namespace和id字符串进行拼接
* */
public String applyCurrentNamespace(String base, Boolean isReferent) {
if (base == null)
return null;
if (isReferent) {
if (base.contains(".")) return base;
}
return currentNamespace + "." + base;
}
/**
* 添加映射器语句
*/
public MappedStatement addMapperStatement(
String id,
SqlSource sqlSource,
SqlCommandType sqlCommandType,
Class<?> parameterType,
String resultMap,
Class<?> resultType,
LanguageDriver lang
) {
id = applyCurrentNamespace(id, false);
MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlCommandType, sqlSource, resultType);
setStatementResultMap(resultMap, resultType, statementBuilder);
MappedStatement mappedStatement = statementBuilder.build();
configuration.addMappedStatement(mappedStatement);
return mappedStatement;
}
// 封装ResultMap对象
private void setStatementResultMap(
String resultMap,
Class<?> resultType,
MappedStatement.Builder statementBuilder
) {
resultMap = applyCurrentNamespace(resultMap, true);
List<ResultMap> resultMaps = new ArrayList<>();
if (resultMap != null) {
// 暂时先不处理resultMap情况
} else if (resultType != null) {
// 适配一下resultType也封装处理为ResultMap
ResultMap.Builder inlineResultMapBuilder = new ResultMap.Builder(configuration,
statementBuilder.id() + "-Inline",
resultType,
new ArrayList());
resultMaps.add(inlineResultMapBuilder.build());
}
statementBuilder.resultMaps(resultMaps);
}
}
4.1.3 调用映射助手类
XMLStatementBuilder,类里引用MapperBuilderAssistant并且构造方法参数传入赋值,这样解析完数据直接调用助手类进行绑定。
public class XMLStatementBuilder extends BaseBuilder {
private MapperBuilderAssistant builderAssistant;
// 省略其他
public XMLStatementBuilder(Configuration configuration, MapperBuilderAssistant builderAssistant, Element element) {
super(configuration);
this.element = element;
this.builderAssistant = builderAssistant;
}
// 开始解析select标签的id,参数,返回值,并调用语言驱动器
public void parseStatementNode() {
// 省略其他
// 调用MapperBuilderAssistant助手类帮助完成mapper的绑定映射
builderAssistant.addMapperStatement(
id,
sqlSource,
sqlCommandType,
parameterTypeClass,
resultMap,
resultTypeClass,
langDriver);
}
}
4.1.4 创建映射助手类
XMLMapperBuilder修改的地方,一个是namespce的处理,一个是获取到MapperBuilderAssistant传递到XmlStatementBuilder中,修改点大致如下
public class XMLMapperBuilder extends BaseBuilder {
// 省略...
// 映射器构建助手
private MapperBuilderAssistant builderAssistant;
public XMLMapperBuilder(Document document, Configuration configuration, String resource) {
super(configuration);
// 省略...
// 创建构建mapper助手类
this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
}
private void configurationElement(Element element) {
// 配置namespace
String namespace = element.attributeValue("namespace");
builderAssistant.setCurrentNamespace(namespace);
// 省略...
}
}
MappedStatement修改的地方,主要添加了ResultMap的存储以List的形式,最后在Builder内部类里的resultMaps赋值即可
public class MappedStatement {
// 省略...
private List<ResultMap> resultMaps;
public static class Builder {
// 省略...
public Builder resultMaps(List<ResultMap> resultMaps) {
mappedStatement.resultMaps = resultMaps;
return this;
}
}
}
4.2 使用映射处理返回结果
4.2.1 结果集处理器
构建完了我们需要的返回值映射,如果执行了Sql语句开始处理结果,可以知道JDBC返回的是结果集,需要处理这样的对象数据,所以定义一个结果集处理接口,看过之前的章节的朋友就会知道,这个接口的定义之前就是有的,我们这次大改的主要是它的实现类,可以接着往下看。
/**
* @Author df
* @Date 2022/6/13 14:26
* @Version 1.0
* @description 结果集处理器
*/
public interface ResultSetHandler {
<E> List<E> handleResultSets(Statement statement) throws SQLException;
}
它的实现类里则耦合度极高并没有职责分离,现在则分出来结果集包装器,结果处理器,结果上下文供此方法使用,它结果集内容如属性类型则由结果包装器进来拆解组装,结果集处理器则是根据MappedStatement的ResultMap里的type类型进行反射获取对象,然后通过结果包装器里解析的数据反射到对象设置属性,然后将已处理完的对象存储到结果上下文中,最后每一个结果上下文都存储到结果处理器中返回,供调用者获取结果对象。
handleResultSets(),这个方法执行调用流程,可以打断点仔细梳理,调用链比较多,但都是本方法内的,把每一个调用链条上的都梳理通了也就不难了,
public class DefaultResultSetHandler implements ResultSetHandler {
private final BoundSql boundSql;
private final MappedStatement mappedStatement;
private final Configuration configuration;
private final RowBounds rowBounds;
private final ResultHandler resultHandler;
private final TypeHandlerRegistry typeHandlerRegistry;
private final ObjectFactory objectFactory;
public DefaultResultSetHandler(Executor executor, MappedStatement mappedStatement, ResultHandler resultHandler, RowBounds rowBounds, BoundSql boundSql) {
this.boundSql = boundSql;
this.mappedStatement = mappedStatement;
this.configuration = mappedStatement.getConfiguration();
this.rowBounds = rowBounds;
this.resultHandler = resultHandler;
this.objectFactory = configuration.getObjectFactory();
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
}
/**
* 返回结果集的处理流程
* 1.调用结果集包装器处理JDBC返回的数据
* 2.获取resultMap也就知道了返回对象,将返回对象类传入下一流程中
* 3.handleResultSet()这个方法后就是一整个流程,实例化对象、找到未被映射的属性,将这些属性反射到对象中
* 4.将处理过的对象存储到结果处理器,结果上下文中,并返回结果处理器中数据
* */
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
final List<Object> multipleResults = new ArrayList<>();
int resultSetCount = 0;
// 处理返回的列名,类型等数据
ResultSetWrapper rsw = new ResultSetWrapper(stmt.getResultSet(), configuration);
// 从mappedStatement获取ResultMap(ResultMap里有id和类型以及ResultMapping)
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
while (rsw != null && resultMaps.size() > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
// 处理封装保存结果
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
resultSetCount++;
}
return multipleResults.size() == 1 ? (List<Object>) multipleResults.get(0) : multipleResults;
}
private ResultSetWrapper getNextResultSet(Statement stmt) {
try {
if (stmt.getConnection().getMetaData().supportsMultipleResultSets()) {
if (!((!stmt.getMoreResults()) && (stmt.getUpdateCount() == -1))) {
ResultSet rs = stmt.getResultSet();
return rs != null ? new ResultSetWrapper(rs, configuration) : null;
}
}
} catch (Exception ihnore) {
}
return null;
}
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
if (resultHandler == null) {
// 1.新创建结果处理器
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
// 2.封装结果数据
handleRowValuesForSimpleResultMap(rsw, resultMap, defaultResultHandler, rowBounds, null);
// 3.保存结果,从resultHandler拿到上下文结果数据
multipleResults.add(defaultResultHandler.getResultList());
}
}
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
// 实例化默认结果上下文
DefaultResultContext resultContext = new DefaultResultContext();
while (resultContext.getResultCount() < rowBounds.getLimit() && rsw.getResultSet().next()) {
// 为对象赋值
// 1.实例化对象
// 2.找到未被映射的属性名称
// 3.根据属性名称进行一系列反射工具反射设置对象属性值.
Object rowValue = getRowValue(rsw, resultMap);
// 存储结果
callResultHandler(resultHandler, resultContext, rowValue);
}
}
/**
* 返回结果存储
* */
private void callResultHandler(ResultHandler resultHandler, DefaultResultContext resultContext, Object rowValue) throws SQLException {
// 将返回结果赋值给resultContext
resultContext.nextResultObject(rowValue);
// 将结果存储到resultHandler
resultHandler.handleResult(resultContext);
}
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
// 根据返回值类型,实例化对象
Object resultObject = createResultObject(rsw, resultMap, null);
if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) {
final MetaObject metaObject = configuration.newMetaObject(resultObject);
applyAutomaticMappings(rsw, resultMap, metaObject, null);
}
return resultObject;
}
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
// 得到未被处理映射的列名
final List<String> unmappedColumnNames = rsw.getUnMappedColumnNamesMap(resultMap, columnPrefix);
boolean foundValues = false;
for (String columnName : unmappedColumnNames) {
String propertyName = columnName;
if (columnPrefix != null && !columnPrefix.isEmpty()) {
// When columnPrefix is specified,ignore columns without the prefix.
if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
propertyName = columnName.substring(columnPrefix.length());
} else {
continue;
}
}
// 从反射工具根据属性名称获取属性
final String property = metaObject.findProperty(propertyName, false);
// 这个属性有set方法
if (property != null && metaObject.hasSetter(property)) {
// 获取当前set方法属性类型
final Class<?> propertyType = metaObject.getSetterType(property);
// 根据属性类型获取对应类型处理器
if (typeHandlerRegistry.hasTypeHandler(propertyType)) {
// 使用 TypeHandler 取得结果
final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
// 根据jdbc的数据获取当前属性名称、类型下的数据
final Object value = typeHandler.getResult(rsw.getResultSet(), columnName);
if (value != null) {
foundValues = true;
}
if (value != null || propertyType.isPrimitive()) {
// 通过反射工具类设置属性值,假如返回某个对象,那么这个对象的set方法将被调用
metaObject.setValue(property, value);
}
}
}
}
return foundValues;
}
// 创建返回值对象
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
// 定义构造方法参数类型
final List<Class<?>> constructorArgTypes = new ArrayList<>();
// 定义构造方法参数数据
final List<Object> constructorArgs = new ArrayList<>();
return createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
}
/**
* 通过反射工具具体创建返回对象
*/
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix) throws SQLException {
final Class<?> resultType = resultMap.getType();
final MetaClass metaType = MetaClass.forClass(resultType);
if (resultType.isInterface() || metaType.hasDefaultConstructor()) {
// 获取普通的bean对象类型
return objectFactory.create(resultType);
}
throw new RuntimeException("Do not know how to create an instance of " + resultType);
}
}
4.2.2 结果集包装器
为结果集处理器提供支持,如获取对应类型处理器,数据集属性,未被映射的属性,这样结果集处理器才会将未被映射的属性进行映射。
/**
* @Author df
* @Date 2023/3/31 12:31
* @Version 1.0
* 结果集包装器
*/
public class ResultSetWrapper {
// JDBC返回的结果集
private final ResultSet resultSet;
// 类型处理注册器,可以获取具体类型
private final TypeHandlerRegistry typeHandlerRegistry;
// 属性名称
private final List<String> columnNames = new ArrayList<>();
private final List<String> classNames = new ArrayList<>();
// JDBC类型
private final List<JdbcType> jdbcTypes = new ArrayList<>();
private final Map<String, Map<Class<?>, TypeHandler<?>>> typeHandlerMap = new HashMap<>();
// 已被映射的缓存
private final Map<String, List<String>> mappedColumnNamesMap = new HashMap<>();
// 未被映射的列的缓存
private final Map<String, List<String>> unMappedColumnNamesMap = new HashMap<>();
public ResultSetWrapper(ResultSet rs, Configuration configuration) throws SQLException {
super();
this.resultSet = rs;
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
final ResultSetMetaData metaData = rs.getMetaData();
final int columnCount = metaData.getColumnCount();
// 处理属性名称封装,jdbc类型封装,属性类型封装
for (int i = 1; i <= columnCount; i++) {
columnNames.add(metaData.getColumnLabel(i));
jdbcTypes.add(JdbcType.forCode(metaData.getColumnType(i)));
classNames.add(metaData.getColumnClassName(i));
}
}
public ResultSet getResultSet() {
return resultSet;
}
public TypeHandlerRegistry getTypeHandlerRegistry() {
return typeHandlerRegistry;
}
public List<String> getColumnNames() {
return columnNames;
}
public List<String> getClassNames() {
return classNames;
}
// 获取类型处理器,在从jdbc结果集中获取值则可以通过对应类型去获取了
public TypeHandler<?> getTypeHandler(Class<?> propertyType, String columnName) {
TypeHandler<?> handler = null;
Map<Class<?>, TypeHandler<?>> columnHandlers = typeHandlerMap.get(columnName);
if (columnHandlers == null) {
columnHandlers = new HashMap<>();
typeHandlerMap.put(columnName, columnHandlers);
} else {
handler = columnHandlers.get(propertyType);
}
if (handler == null) {
// 从注册器获取当前类型处理器
handler = typeHandlerRegistry.getTypeHandler(propertyType, null);
columnHandlers.put(propertyType, handler);
}
return handler;
}
public List<JdbcType> getJdbcTypes() {
return jdbcTypes;
}
public Map<String, Map<Class<?>, TypeHandler<?>>> getTypeHandlerMap() {
return typeHandlerMap;
}
public Map<String, List<String>> getMappedColumnNamesMap() {
return mappedColumnNamesMap;
}
// 获取未被映射的属性
public List<String> getUnMappedColumnNamesMap(ResultMap resultMap, String columnPrefix) {
List<String> unMappedColumnNames = unMappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
if (unMappedColumnNames == null) {
// 加载没有被映射的属性名
loadMappedAndUnmappedColumnNames(resultMap, columnPrefix);
// 从缓存获取未被映射的属性名数据
unMappedColumnNames = unMappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
}
return unMappedColumnNames;
}
/**
* 加载没有被映射的属性名
* */
private void loadMappedAndUnmappedColumnNames(ResultMap resultMap, String columnPrefix) {
// 有被映射的属性名
List<String> mappedColumnNames = new ArrayList<>();
// 没有被映射的属性名
List<String> unmappedColumnNames = new ArrayList<>();
final String upperColumnPrefix = columnPrefix == null ? null : columnPrefix.toUpperCase(Locale.ENGLISH);
// 已被映射的属性名
final Set<String> mappedColumns = prependPrefixes(resultMap.getMappedColumns(), upperColumnPrefix);
for (String columnName : columnNames) {
final String upperColumnName = columnName.toUpperCase(Locale.ENGLISH);
if (mappedColumns.contains(upperColumnName)) {
mappedColumnNames.add(upperColumnName);
} else {
unmappedColumnNames.add(columnName);
}
}
mappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), mappedColumnNames);
unMappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), unmappedColumnNames);
}
private Set<String> prependPrefixes(Set<String> columnNames, String prefix) {
if (columnNames == null || columnNames.isEmpty() || prefix == null || prefix.length() == 0) {
return columnNames;
}
final Set<String> prefixed = new HashSet<>();
for (String columnName : columnNames) {
prefixed.add(prefix + columnName);
}
return prefixed;
}
private String getMapKey(ResultMap resultMap, String columnPrefix) {
return resultMap.getId() + ":" + columnPrefix;
}
}
4.2.3 类型处理器
上节类型处理器只定义了参数的设置,本节需要添加上定义获取返回值
public interface TypeHandler<T> {
T getResult(ResultSet rs, String columnName) throws SQLException;
}
Long类型处理器获取返回值,则从JDBC的结果集中getLong就可以获取相应类型的值
public class LongTypeHandler extends BaseTypeHandler<Long> {
// 省略..
@Override
public Long getResult(ResultSet rs, String columnName) throws SQLException {
return rs.getLong(columnName);
}
}
String类型处理器获取返回值,则从JDBC的结果集中getString就可以获取相应类型的值
public class StringTypeHandler extends BaseTypeHandler<String> {
// 省略..
@Override
public String getResult(ResultSet rs, String columnName) throws SQLException {
return rs.getString(columnName);
}
}
4.2.4 结果上下文
结果上下文接口
/**
* @Author df
* @Date 2023/3/30 12:48
* @Version 1.0
* 结果上下文
*/
public interface ResultContext {
/**
* 获取结果
*/
Object getResultObject();
/**
* 获取记录数
*/
int getResultCount();
}
实现接口,结果上下文存储结果对象
/**
* @Author df
* @Date 2023/3/30 12:52
* @Version 1.0
* 默认结果上下文
*/
public class DefaultResultContext implements ResultContext {
// 结果数据对象
private Object resultObject;
private int resultCount;
public DefaultResultContext() {
this.resultObject = null;
this.resultCount = 0;
}
@Override
public Object getResultObject() {
return resultObject;
}
@Override
public int getResultCount() {
return resultCount;
}
public void nextResultObject(Object resultObject) {
resultCount++;
this.resultObject = resultObject;
}
}
4.2.5 结果处理器
将上下文结果行数据存储到结果器中。
/**
* @Author df
* @Date 2023/3/30 12:44
* @Version 1.0
* 默认结果处理器
*/
public class DefaultResultHandler implements ResultHandler {
// 存储结果上下文数据
private final List<Object> list;
public DefaultResultHandler() {
this.list = new ArrayList<>();
}
/**
* 通过 ObjectFactory 反射工具类,产生特定的空List
用 objectFactory.create(List.class)创建并没有什么特殊,只是统一工具类
*/
public DefaultResultHandler(ObjectFactory objectFactory) {
this.list = objectFactory.create(List.class);
}
@Override
public void handleResult(ResultContext resultContext) {
list.add(resultContext.getResultObject());
}
public List<Object> getResultList() {
return list;
}
}
5.测试
配置文件都还是上节的就可以,以下单元测试
public class TestApi {
private SqlSession sqlSession;
@Before
public void init() throws IOException {
// 1. 从SqlSessionFactory中获取SqlSession
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config-datasource.xml"));
sqlSession = sqlSessionFactory.openSession();
}
@org.junit.Test
public void test_queryUserInfoById() {
// 1. 获取映射器对象
IUserDao userDao = sqlSession.getMapper(IUserDao.class);
// 2. 测试验证:基本参数
User user = userDao.queryUserInfoById(1L);
System.out.println("测试结果:" + user.getUserName());
}
}
修改完后的代码依然能够获取返回对象,得到对应的返回值,大家也可以打电话看看每一步的变化。