使用动态SQl出错 Expected one result (or null) to be returned by selectOne(), but found: 24

博客讲述了MyBatis使用中遇到的错误,即期望selectOne()返回一个结果或null,却找到多条数据。经检查SQL语句无误,错误原因是SQL返回多条数据时,mapper中不能用类返回,应改为List集合,更改后错误解决。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

出现以下错误,多次检查sql语句并没有错,错误的中文翻译是:期望由selectOne()返回一个结果(或null),但是找到:24
在这里插入图片描述
我的mapper代码如下:

//更改前
public CarInfo findCarInfo(@Param("car_id")String carid,@Param("car_num")String carnum);

对应的sql语句;

	<!-- 动态sql -->
	<select id="findCarInfo" resultType="CarInfo">
		select * from car_info where 1=1
		<choose>
			<when test="car_id != null and car_num != ''">
				and car_id = #{car_id}
			</when>
			<when test="car_num != null and car_num != ''">
				and car_num like concat('%',#{car_num},'%')
			</when>
			<otherwise>
				and car_id is not null
			</otherwise>
		</choose>
	</select>

错误的原因是当sql返回了多条数据,mapper中,返回就不能使用类CatInfo了,这里更改为List集合,更改后mapper代码如下:

// 更改后
public List<CarInfo> findCarInfo(@Param("car_id")String carid,@Param("car_num")String carnum);

以上错误就成功解决了,希望能帮到大家,谢谢

<think>我们正在处理一个MyBatis的异常:Expected one result (or null) to be returned by selectOne(), but found 3 这个异常表示:MyBatis期望查询返回一个结果(或null),但实际查询返回了3条记录。 原因分析: 1. 在MyBatis中,当我们使用`selectOne`方法(或者Mapper接口方法返回单个对象)时,要求查询结果最多只能有一条记录。 2. 如果查询返回多条记录,MyBatis就会抛出这个异常。 解决方案: 根据引用[1][2][3]的内容,我们可以从以下几个方面解决: 方案一:修改SQL查询条件,确保只返回一条记录 - 检查SQL语句,确保查询条件能够唯一确定一条记录。例如,使用主键(唯一性约束)作为条件。 - 在当前的SQL中,我们使用了`c.id=#{id}`,但是因为同时连接了`emp`表,如果`clazz`表和`emp`表都有`id`字段,那么在没有明确指定表别名的情况下,可能会出现歧义(之前的错误已经解决)。但这里的问题不是歧义,而是返回了多条记录。 - 实际上,我们通过`c.id=#{id}`应该只返回一个班级(clazz)记录,但是为什么返回了多条?可能的原因有: a) 连接条件写错了,导致多对多连接,返回了多条记录。 b) 数据库中存在重复数据(比如同一个班级有多条记录,但主键id应该唯一,所以这种情况较少)。 方案二:检查Mapper接口的返回类型 - 确保Mapper接口的方法声明为返回单个对象,而不是集合。例如: ```java Clazz getById(Integer id); ``` 但是,即使这样声明,如果SQL返回多条,也会报错。 方案三:修改SQL语句,使用LIMIT 1(如果业务允许) - 如果业务上可以接受(即我们只需要多条记录中的第一条),可以在SQL语句末尾添加`LIMIT 1`(MySQL)或`ROWNUM=1`(Oracle)等。但这种方法需要谨慎,因为可能不是你想要的那条记录。 方案四:使用`selectList`代替`selectOne` - 如果业务上确实可能返回多条记录,那么应该修改Mapper接口的方法,返回一个列表(List),然后调用`selectList`方法。但是,这需要改变方法的签名和调用方式。 针对当前问题,我们首先分析SQL语句: 原SQL: ```sql select id,name, room, begin_date beginDate, end_date endDate, subject, create_time createTime,update_time updateTime,e.name as master_name from clazz c left join emp e on (c.master_id=e.id and c.id=#{id}) ``` 注意:这里连接条件中同时有`c.master_id=e.id`和`c.id=#{id}`。这个条件的意思是:连接条件是clazz表的master_id等于emp表的id,并且同时clazz表的id等于传入的参数id。 但是,这里有一个问题:`c.id=#{id}`作为连接条件的一部分,而不是在WHERE子句中。这样写虽然可以,但是连接条件中同时出现了两个条件。那么,这个查询会先做连接,然后过滤出c.id等于传入id的记录。 然而,这里可能存在一个逻辑错误:我们想要查询的是指定id的班级,并且关联查询其班主任信息(通过master_id关联)。但是,如果这个班级(c.id=#{id})只有一个,那么为什么返回了多条记录? 可能的原因: - 在emp表中,可能存在多个emp记录具有相同的id?(不可能,因为id是主键) - 或者,连接条件写错了?应该将`c.id=#{id}`放在WHERE子句中,而不是连接条件中。 让我们重新组织SQL: 正确的写法应该是: ```sql select ... from clazz c left join emp e on c.master_id = e.id where c.id = #{id} ``` 因为连接条件中,我们只需要通过master_id关联emp表。而班级id的条件应该放在WHERE子句,这样先通过主键id在clazz表中定位到一条记录,然后再去关联emp表,这样最多只会返回一条记录(因为clazz表的主键查询)。 所以,修改SQL为: ```sql @Select("select c.id, c.name, c.room, c.begin_date beginDate, c.end_date endDate, c.subject, c.create_time createTime, c.update_time updateTime, e.name as master_name " + "from clazz c " + "left join emp e on c.master_id = e.id " + "where c.id = #{id}") Clazz getById(Integer id); ``` 这样修改后,由于`c.id`是主键,所以只会返回一条记录,问题解决。 总结: - 将`c.id=#{id}`从连接条件移到WHERE子句。 - 同时,为了避免歧义,在SELECT列表中的每个字段都明确指定表别名(这里特别给id加上了c.id,虽然在这个SQL中因为WHERE子句已经限定clazz表,所以id不会歧义,但为了清晰,建议都加上)。 如果按照上述修改后,问题应该解决。 另外,如果修改后仍然返回多条,请检查数据库数据:在clazz表中,是否存在多个id相同的记录?(主键重复,这是不应该的) 最后,如果业务上确实需要返回多条记录(比如通过非主键查询),那么应该将方法返回类型改为List,并使用`selectList`。 根据引用[3]中的分析,MyBatis的`selectOne`方法内部调用的是`selectList`,如果返回的列表大小大于1,就会抛出`TooManyResultsException`。因此,确保查询结果唯一是关键。 相关问题: §§相关问题§§ 1. MyBatis中`selectOne`和`selectList`方法的使用场景有什么区别? 2. 在MyBatisSQL映射文件中,如何配置结果映射来处理一对一关联查询? 3. 如何避免MyBatis中的多表查询导致的列名歧义问题? 4. 在MyBatis中,如何处理查询结果可能为空的情况?</think>### 解决 MyBatis "Expected one result or null" 异常 当 MyBatis 的 `selectOne()` 方法返回多个结果时(如你遇到的返回 3 条记录),会触发此异常。以下是完整解决方案: #### 🔍 原因分析 1. **方法不匹配**:`selectOne()` 要求返回 **0 或 1 条**记录,但查询返回了多条 2. **SQL 问题**:查询条件缺少唯一性约束(如未使用主键或唯一索引) 3. **映射错误**:Mapper 接口声明返回单个对象,但 SQL 实际返回集合 4. **数据异常**:数据库存在重复数据(如主键不唯一) #### ✅ 解决方案 ##### 1. 修改 SQL 确保唯一性(推荐) ```sql{2-3} SELECT c.id, c.name, room, begin_date beginDate, end_date endDate, subject, create_time createTime, update_time updateTime, e.name AS master_name FROM clazz c LEFT JOIN emp e ON c.master_id = e.id WHERE c.id = #{id} -- 确保使用主键条件 LIMIT 1 -- 强制只返回一条 ``` ##### 2. 修改 Mapper 接口返回值类型 ```java{1} // 原方法(要求单条) // Clazz getById(Integer id); // 改为返回列表 List<Clazz> getById(Integer id); ``` ##### 3. 检查数据唯一性 ```sql -- 验证数据是否重复 SELECT id, COUNT(*) FROM clazz GROUP BY id HAVING COUNT(*) > 1; -- 如果返回结果,说明数据重复 ``` ##### 4. 添加结果集验证 ```java // 在Service层处理 public Clazz getClassSafe(Integer id) { List<Clazz> list = clazzMapper.getById(id); return list.isEmpty() ? null : list.get(0); // 取第一条 } ``` #### ⚠️ 关键注意事项 1. **主键验证**:确保 `WHERE` 条件使用主键字段(如 `c.id` 而非 `e.id`) 2. **表别名**:多表查询时明确指定字段归属(如 `c.id` vs `e.id`) 3. **索引检查**:确认 `id` 字段有唯一索引 4. **MyBatis 配置**:检查是否误用 `@ResultType` 注解 #### 💡 预防措施 ```mermaid graph TD A[新查询方法] --> B{返回类型} B -->|单条结果| C[selectOne] B -->|多条结果| D[selectList] C --> E[确保SQL有唯一约束] D --> F[处理空集合情况] ``` > **经验提示**:在开发阶段使用 `LIMIT 2` 进行测试,如果返回超过 1 条数据立即报警,可提前发现设计缺陷[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值