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

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

实体

package com.test.User

public class User{
private String userId;
private String userName;
private String userPassword;
private Date createTime;
...
setter getter....
}

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()来进行查询的。

  • 使用Mapper 接口的方式的查询结果时,Mybatis会生成该接口的代理类(MapperProxy),然后根据Method的getReturnType()方法,拿到返回类型,来确定返回的是列表还是单个实体。最后也是调用sqlSession的一些方法。
  • 使用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怎么知道是查询的单条数据还是列表呢?

  • 如果直接使用 SqlSession,这个需要自己控制 是调用selectOne 还是 selectList
  • 如果使用 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="getUserInfoById" resultType="map">
		select * from user where id=#{userId}
	</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对数据类型做进一步处理
返回结果 Mapper xml
实体 T getT() returnType=“T”
集合 List<T> getTList() returnType=“T”

到此这篇关于Mybatis返回单个实体或者返回List的实现的文章就介绍到这了,更多相关Mybatis返回实体或者返回List内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • mybatis插入与批量插入返回ID的原理详解

    背景 最近正在整理之前基于mybatis的半ORM框架.原本的框架底层类ORM操作是通过StringBuilder的append拼接的,这次打算用JsqlParser重写一遍,一来底层不会存在太多的文本拼接,二来基于其他开源包维护难度会小一些,最后还可以整理一下原有的冗余方法. 这两天整理insert相关的方法,在将对象插入数据库后,期望是要返回完整对象,并且包含实际的数据库id. 基础相关框架为:spring.mybatis.hikari. 底层调用方法 最底层的做法实际上很直白,就是利用my

  • Mybatis查不到数据查询返回Null问题

    mybatis突然查不到数据,查询返回的都是Null,但是 select count(*) from xxx查询数量,返回却是正常的. Preparing: SELECT id,a9004,a9005,a9015 FROM a90 where a9010 = ? ORDER BY id LIMIT 1 [DEBUG] org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:139):http-bio-8080

  • MyBatis查询返回Map示例代码

    前言 有时候我们做查询, 只需要返回两个string类型的字段,方便我们后续的处理.比如根据商品的code查询对应的分类的名字,需要返回一个map, key为商品code, value为商品分类对应的名称.以方便我们后续对结果集的处理.如果你直接用mybatis返回一个map, 你可能会发现,结果根据不是自己需要的.这时候我们需要自己实现一个ResultHandler来实现我们想要的结果,废话不多说,直接上代码 方法如下 第一步,在mapper.xml文件当中,定义一个ResultMap <re

  • MyBatis+MySQL 返回插入的主键ID的方法

    需求:使用MyBatis往MySQL数据库中插入一条记录后,需要返回该条记录的自增主键值. 方法:在mapper中指定keyProperty属性,示例如下: <insert id="insertAndGetId" useGeneratedKeys="true" keyProperty="userId" parameterType="com.chenzhou.mybatis.User"> insert into us

  • mybatis中查询结果为空时不同返回类型对应返回值问题

    今天在别人的代码基础上实现新需求,看到对于mybatis查询结果的判断不是很正确,如果查询结果为空就会异常,不知道大家有没有这样的疑惑:mybatis中resultType有多种返回类型,对于每种不同类型,查询结果为空时dao接口的返回值是一样的吗?接下来我就总结一下常见的几种情况. 第一种:resultType为基本类型,如string(在此暂且把string归纳为基本类型) 如果select的结果为空,则dao接口返回结果为null 第二种,resultType为基本类型,如int 后台报异

  • Mybatis Update操作返回值问题

    后端的数据持久化使用的是 Mybatis ,在做高并发下账户增减余额的时候,打算使用乐观锁来解决这个问题.在获取update操作的返回值时遇到了一个问题,似乎 Mybatis 进行 update 操作得到的 int 返回值并不是影响的行数.这下就尴尬了. 一般而言,我们知道当我们使用 Mybatis 在 mapper 接口中定义 insert delete 等操作,定义一个 int 类型的返回值,通过该值是否为 0 来判断数据库中受影响的行数进而判断操作是否成功. 到底 update 返回值代表

  • 解决mybatis执行SQL语句部分参数返回NULL问题

    今天在写代码的时候发现一个问题:mybatis执行sql语句的时候返回bean的部分属性为null,在数据库中执行该sql语句能够正常返回,把相关代码反反复复翻了个遍,甚至都重启eclipse了,依旧没解决问题,后来网上搜了一下,还真有类似的问题. 闲话少说,直接说问题,该sql语句是自己写的,resultType直接用了该bean全名称,最终导致部分属性显示为null, 原来的写法: <select id="selectByArticle" parametertype=&quo

  • Mybatis返回插入主键id的方法

    在mapper的xml文件中配置  useGeneratedKeys 以及 keyProperty 返回Id即可 <insert id="insertObject" useGeneratedKeys="true" keyProperty="id" parameterType="www.change.tm.model.Orders" > insert into orders <trim prefix="

  • Mybatis返回单个实体或者返回List的实现

    Mybatis 的强大之处之一体现在映射语句上,让我们可以使用简单的配置,就可以实现对参数和返回结果的映射. 实体 package com.test.User public class User{ private String userId; private String userName; private String userPassword; private Date createTime; ... setter getter.... } DAO public interface User

  • mybatis查询结果返回至实体类的示例代码

    近期,利用mybatis做一个简单查询,先看主要代码: Service层: package com.example1.service; import java.util.List; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.exampl

  • MyBatis insert实体如何返回主键

    目录 insert实体如何返回主键 一.insert 属性详解 二.Mapper接口 三.执行mapper.xml 返回主键 四.测试结果 六.批量插入 七.小结一下 Mybatis添加记录,返回主键id insert实体如何返回主键 一.insert 属性详解 parameterType:入参的全限定类名或类型别名 keyColumn:设置数据表自动生成的主键名.对特定数据库(如PostgreSQL),若自动生成的主键不是第一个字段则必须设置 keyProperty:默认值unset,用于设置

  • mybatis多层嵌套resultMap及返回自定义参数详解

    1.两层嵌套,一个list中加另外一个list data:[ {a:123,b:456,c:[{d:7,e:8}]} ] xml文件定义的sql select * from zhy z LEFT JOIN wl w on z.id = w.zid resultMap可以定义: <resultMap id="zhyResultMap" type="zhy的doman实体" extends="zhy自动生成的BaseResultMap">

  • SpringBoot 返回Json实体类属性大小写的解决

    目录 返回Json实体类属性大小写问题 Json转换大小写的坑 返回Json实体类属性大小写问题 总归上述问题Rt,其实今天开发刚遇到,当时找了半天为啥前台传参后台却接收不到,原来是返回的时候返回小写,但是前台依旧大写传参. 查了很多后发现其实是json返回的时候把首字母变小写了,也就是Spring Boot中Jackson的功劳 百度后得@JsonProperty注解完美解决.但与此同时会出现两个问题 如果注解放到属性上,则返回的时候既有大写也有小写, @JsonProperty("Ao&qu

  • SpringBoot+JPA 分页查询指定列并返回指定实体方式

    目录 SpringBoot+JPA分页查询指定列并返回指定实体 SpringBoot JPA实现自定义语句分页查询 SpringBoot+JPA分页查询指定列并返回指定实体 用习惯Mybatis,没用过jpa 真是各种踩坑了 脑壳疼,一个分页弄老半天,原来就一句话的事情,唉 先来说说正常的JPA如何操作 实体类对应表来创建,举个例子 @Entity @Table(name = "td_user") public class TdUser extends BaseModel { priv

  • SpringBoot+JPA 分页查询指定列并返回指定实体方式

    目录 SpringBoot JPA分页查询指定列并返回指定实体 实体类对应表来创建,举个例子 SpringBoot JPA实现自定义语句分页查询 1.JPA持久层 InvoiceRepository.java 2.服务层 SpringBoot JPA分页查询指定列并返回指定实体 用习惯Mybatis,没用过jpa 真是各种踩坑了 脑壳疼,一个分页弄老半天,原来就一句话的事情,唉 先来说说正常的JPA如何操作 实体类对应表来创建,举个例子 @Entity @Table(name = "td_use

  • mybatis插入数据后如何返回新增数据的id值

    目录 1.自增主键情况下插入数据获取自增主键值 2.主键非自增的情况下获取主键值 3.keyColumn作用 总结 1.自增主键情况下插入数据获取自增主键值 mybatis为我们提供了一个方法,能够插入数据时获取自动生成的值,并且把取的值赋值给实体类的某一属性 设置方法: 要求:主键必须是自增的 <insert id = "insert" useGeneratedKeys = "true" keyProperty = "id">  

  • C#查询SqlServer数据库并返回单个值的方法

    本文实例讲述了C#查询SqlServer数据库并返回单个值的方法.分享给大家供大家参考.具体实现方法如下: static public string GetSqlAsString(string sqlText, SqlParameter[] sqlParameters, string databaseConnectionString) { string result = ""; SqlDataReader reader; SqlConnection connection = new S

  • 详解mybatis plus使用insert没有返回主键的处理

    项目使用springboot搭建.最初的时候是使用mybatis,后来升级到mybatis plus.按照mp的官网介绍,使用mp的insert方法,对于自增的数据库表,mp会把主键写入回实例的对应属性.但实际操作起来,却没有主键. entity 类设置如下: @TableName(value = "USERINFO") public class UserInfo { /** * 指定自增策略 */ @TableId(value = "user_id",type =

随机推荐