Mybatis返回单个实体或者返回List

Mybatis 的强大之处之一体现在映射语句上,让我们可以使用简单的配置,就可以实现对参数和返回结果的映射。

实体

// com.test
@Data
public class User{
	private String userId;
	private String userName;
	private String userPassword;
	private Date createTime;
}

DAO

public interface UserMapper{
	User getUserById(String userId);  //返回单个实体
	List<User> getUserByName(String userName); //返回List
	Map<String,Object> getUserInfoById(String userId);
	List<Map<String,Object>> getUserInfoByName(String userName);
}

数据库

create table user{
	USER_ID varchar(40),
	USER_NAME varchar(200),
	USER_PASSWORD varchar(100),
	CREATE_TIME datetime,
	....
}
1.返回某个实体

mybatis映射文件

	<select id="getUserById" parameterType="string" resultType="com.test.User">
		select * from user where id = #{userId}
	</select>
  • id :identification:语句的标识,在同一个mapper映射文件下id需要唯一
  • parameterType: 参数类型,可以不写。因为 MyBatis 可以推断出传入语句的具体参数
  • resultType: 全限定类名或者是类型别名.

当使用resultType来映射结果时,需要 数据库表的列名或列别名 和 类的属性名相同,这样才能进行字段的匹配(USER_ID 和userId 就不能匹配)。但是如果在Mybatis配置文件中设置了

<settings>
  <setting name="mapUnderscoreToCamelCase" value="true"/> <!--开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。-->
</settings>

此时,表列名的下划线标记方式可以映射到驼峰标记的形式。(USER_ID -> userId)。mybatis进行映射时会将实体类属性和数据库列名(别名)都转化为大写来比较,所以USER_ID 和 UserId,userID等都可以匹配。

TooManyResultsException

返回单个实体时,调用方法 getUserById,但是如果是因为数据错误导致实际查询结果存在多个时,则会抛出异常。

User getUserById(String userId);  //返回单个实体

当实际返回值有多个时则抛出异常。

org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 2

除非可以确定最多只能查询到一条结果,否则的话不建议这么写.可以尝试返回集合的方式。

2.返回List<entityName>
	<select id="getUserByName"  resultType="com.test.User">
		select * from user where user_name = #{userName}
	</select>

返回List<T> 集合时,resultType设置为集合元素的类型即T。然后使用返回几何数据的Mapper方法即可。

List<User> getUserByName(String userName); //返回List

从上面可以看到,返回单个实体与返回集合的resultType指定类型是一样的,不一样的地方在Mapper接口或者sqlSession中定义的返回结果类型。实际上mybatis执行查询的时候也都是使用sqlSession.selectList()来进行查询的。

  1. 使用Mapper 接口的方式的查询结果时,Mybatis会生成该接口的代理类(MapperProxy),然后根据Method的getReturnType()方法,拿到返回类型,来确定返回的是列表还是单个实体。最后也是调用sqlSession的一些方法。
  2. 使用SqlSession时,提供了selectOne() 或者selectList()来返回单个实体或者集合。selectOne 实际会调用selectList获取结果。

推荐使用返回List的方式来查询结果

//查询单条结果
List<User> userList= mapper.getUserByName(userName);
if(userList.isEmpty() || userList.size() >1)//期望获得一条结果
	//业务处理,一般是抛出异常或者直接返回错误结果
	//return xx;	
	//throw xxx
User user = userList.get(0);
扩展

为什么查询单条和查询多条使用的是相同的resultType,而返回的结果不同呢。
这是因为Mybatis 在内部进行数据查询的时,无论查询单条还是多条都是通过selectList实现的,不同的是查询单条Mybatis会获取第一条,并且如果结果中存在多条时抛出异常 TooManyResultsException

  • 查询单数据

      @Override
      public <T> T selectOne(String statement, Object parameter) {
        // Popular vote was to return null on 0 results and throw exception on too many.
        List<T> list = this.<T>selectList(statement, parameter);
        if (list.size() == 1) {
          return list.get(0);
        } else if (list.size() > 1) {
          throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
        } else {
          return null;
        }
      }
    
  • 查询列表

      private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
        List<E> result;
        Object param = method.convertArgsToSqlCommandParam(args);
        if (method.hasRowBounds()) {
          RowBounds rowBounds = method.extractRowBounds(args);
          result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
        } else {
          result = sqlSession.<E>selectList(command.getName(), param);
        }
        // issue #510 Collections & arrays support
        if (!method.getReturnType().isAssignableFrom(result.getClass())) {
          if (method.getReturnType().isArray()) {
            return convertToArray(result);
          } else {
            return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
          }
        }
        return result;
      }
    

那么Mybatis怎么知道是查询的单条数据还是列表呢?

  1. 如果直接使用 SqlSession,这个需要自己控制 是调用 selectOne 还是 selectList
  2. 如果使用 Mapper 接口,Mybatis会解析Mapper接口中的方法,会根据方法的返回值,判断该方法属于那种类型

解析方法中的参数、返回值

public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
      Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
      if (resolvedReturnType instanceof Class<?>) {
        this.returnType = (Class<?>) resolvedReturnType;
      } else if (resolvedReturnType instanceof ParameterizedType) {
        this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
      } else {
        this.returnType = method.getReturnType();
      }
      this.returnsVoid = void.class.equals(this.returnType);
      this.returnsMany = (configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray());
      this.returnsCursor = Cursor.class.equals(this.returnType);
      this.mapKey = getMapKey(method);
      this.returnsMap = (this.mapKey != null);
      this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
      this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
      this.paramNameResolver = new ParamNameResolver(configuration, method);
    }

method 对象就是 Mapper中的方法

// select 查询操作
 case SELECT:	
 		// 方法中没有定义返回结果,并且方法存在结果处理器
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {
        // 返回列表
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
        // 返回Map
          result = executeForMap(sqlSession, args);
        } else if (method.returnsCursor()) {
        	// 返回游标
          result = executeForCursor(sqlSession, args);
        } else {
        // 返回单条数据
          Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);
        }
        break;
3.返回Map

返回map 本质上也是返回一个实体。

	<select id="getUserInfoById"  resultType="map">
		select * from user where id=#{userId}
	</select>

如果想要返回单个Map<key,value>集合,只需要设置resultType="map"就可以了,此时返回的实例类型是HashMap
Map的key就是数据表的列名 或者列别名, value 就是查询的数据库中的结果。如果需要返回LinkedHashMap,需要使用全限定类名resultType="java.util.LinkedHashMap"

注意: 返回列对应的结果为 null,则不显示该 key - value 键值对(只针对该行数据对应的Map)

<select id="getStatus"  resultType="map">
	select '' as  code ,'全部'  as name from dual
</select>

Oracle环境下:
在这里插入图片描述
最终返回Map:

{"name ":"全部"}
// code 不是显示

如果使用map接受则不会该map不存在数据. 因为Mybatis默认情况下,不会进行赋值,此时该key-value缺失
如果需要改变该行为可以在mybatis配置文件中设置

  <setting name="callSettersOnNulls" value="true"/>

callSettersOnNulls 指定当返回结果为null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,注意基本类型(int、boolean等)是不能设置成 null 的。

配置之后的返回结果:

{"code":null,"name":"全部"}
4.返回List<Map>
	<select id="" parameterType="" resultType="map">
		sql_caluse
	</select>

resultType设置为map,跟上面一样resultType设置为List集合中元素的类型。

关于mybatis传递多个参数,可以参考mybatis3-传递多参数

注意:

偶然发现Mybatis 会自动对重名的列做去重

比如我有一组数据,使用Map接受

  SELECT l1.*,l2.*,l3.* FROM ITEM_CAT  l1 LEFT JOIN ITEM_CAT l2 ON l1.Id=L2.PARENT_ID LEFT JOIN ITEM_CAT l3
        ON l2.Id=L3.PARENT_ID WHERE L1.PARENT_ID='0';

在这里插入图片描述

实际返回结果,会发现 name1,name2 都没有映射到Map中

[
	{"parent_id":0,"name":"图书","id":1},
	{"parent_id":0,"name":"图书","id":1},
	{"parent_id":0,"name":"图书","id":1},
	{"parent_id":0,"name":"图书","id":1}
	......
]

稍微修改一下字段名称。

  SELECT l1.*,l2.*,l3.*,'test' as name1 FROM ITEM_CAT  l1 LEFT JOIN ITEM_CAT l2 ON l1.Id=L2.PARENT_ID LEFT JOIN ITEM_CAT l3
        ON l2.Id=L3.PARENT_ID WHERE L1.PARENT_ID='0';
[
	{"parent_id":0,"name":"图书","id":1,"name1":"test"},
	{"parent_id":0,"name":"图书","id":1,"name1":"test"},
	{"parent_id":0,"name":"图书","id":1,"name1":"test"},
	{"parent_id":0,"name":"图书","id":1,"name1":"test"}
	......
]

可以看到 新增的自定义列名 “name1”,可以正常显示。这是因为使用sql 查询出的同名的列名自动追加数字做区分,而实际保存在 元数据信息中的列名还是原来的。就如同Excel 的单元格一样,不管单元格内容以什么样式显示都不会修改实际值。
在这里插入图片描述

在这里插入图片描述

小结:
  • 返回集合与返回单个实体对象在映射文件的写法是一致的,不同的地方在于Mapper的返回类型不同。
  • 如果不确定返回值是否是唯一的,尽量使用 集合的返回方式。然乎使用get(0)的方式获取实体。
  • 如果返回实体,一般情况会使用 resultMap来映射返回结果。这样更清晰,直观,而且还可以使用typeHandler对数据类型做进一步处理
返回结果Mapperxml
实体T getT()returnType=“T”
集合List<T> getTList()returnType=“T”
  • 19
    点赞
  • 64
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
### 回答1: 当使用MyBatis进行查询时,如果查询结果为空,MyBatis返回一个空的集合或者null。 在MyBatis中,查询结果可以通过返回值类型来确定。如果查询结果期望返回一个列表或集合,当查询结果为空时,MyBatis返回一个空的集合,即长度为0的列表或集合。例如,如果使用List作为返回值类型,当查询结果为空时,MyBatis返回一个空的List对象。 另外,如果查询结果期望返回单个对象,当查询结果为空时,MyBatis返回null。例如,如果使用对象作为返回值类型,当查询结果为空时,MyBatis返回null。 开发者可以根据实际需要来处理这种空的查询结果。可以通过判断返回的集合或对象是否为空,来进行相应的处理逻辑。例如,可以根据返回的集合的长度来判断查询结果是否为空,如果长度为0,则表示查询结果为空,可以做一些特殊处理,如提示用户或者进行其他操作。如果返回的对象为null,则表示查询结果为空,也可以根据需要进行相应的处理。 总的来说,MyBatis在查询结果为空时返回一个空的集合或者null,开发者可以根据返回值类型来处理这种情况,以满足业务需求。 ### 回答2: 当使用MyBatis进行查询时,如果查询结果为空,MyBatis将会返回一个空的集合或者null。 具体返回哪种结果由配置决定。在MyBatis的配置文件中,可以配置`<setting name="returnInstanceForEmptyRow" value="true"/>`来设置返回的结果。当`returnInstanceForEmptyRow`设置为`true`时,MyBatis将会返回空的实例对象,该对象的所有字段将会被设置为null或者默认值。当`returnInstanceForEmptyRow`设置为`false`时,MyBatis将会返回null。 例如,如果查询一个不存在的记录,如果`returnInstanceForEmptyRow`设置为`true`,MyBatis将会返回一个空的实例对象,该对象的字段都为null或者默认值。如果`returnInstanceForEmptyRow`设置为`false`,MyBatis将会返回null。 在程序中处理返回结果时,可以通过判断返回的集合是否为空或者判断返回的对象是否为null来判断查询结果是否为空。根据具体的业务需求,可以进行相应的处理,如抛出异常、返回默认值等。 ### 回答3: 当MyBatis查询结果为空时,通常会返回一个空的集合对象(如List或Set类型)或者是null。 在MyBatis中,查询结果的返回值是由Mapper接口中定义的方法返回类型决定的。如果查询结果为空,则根据方法返回类型的不同,MyBatis会进行不同的处理。 当返回类型为集合类型(如List、Set等)时,如果查询结果为空,MyBatis返回一个空的集合对象。这样可以避免返回null值,在使用查询结果时可以方便地进行判空操作,同时也保证代码的健壮性。例如: ```java List<User> userList = userDao.queryUsers(); if (userList != null && !userList.isEmpty()) { // 处理查询结果 } else { // 查询结果为空时的处理逻辑 } ``` 当返回类型为单个实体对象类型(如User、Order等)时,如果查询结果为空,MyBatis返回null。在使用查询结果时,需要进行判空操作,以避免NullPointerException的发生。例如: ```java User user = userDao.queryUserById(1); if (user != null) { // 处理查询结果 } else { // 查询结果为空时的处理逻辑 } ``` 需要注意的是,有时候我们可以通过配置MyBatis的选项来自定义空结果的返回值或采取其他处理方式。可以通过配置`<setting name="callSettersOnNulls" value="true" />`来使MyBatis返回实体对象而不是null。这个根据具体需求来决定。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值