Expected one result (or null) to be returned by selectOne(), but found: 2

selectOne查询一条记录,如果使用selectOne查询多条记录则抛出异常

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

	at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:70)
	at test.MybatisTest.testFindLike2(MybatisTest.java:87)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

selectOne改成selectList

日常一记!!!!!!!!!!!!

<think>我们正在处理一个MyBatis异常:Expectedoneresult(ornull)tobereturnedbyselectOne(),butfound:7根据引用,这个异常的原因是:MyBatis的selectOne()方法期望返回一个结果对象(或null),但实际查询返回了多个结果(这里是7个)。通常,这个异常发生在以下情况:1.查询语句本身可能返回了多行数据(例如,没有使用唯一键查询,或者连接查询导致结果重复)。2.结果映射(resultMap)配置可能存在问题,导致即使查询返回一行,但映射过程中产生了多个对象(但根据异常信息,是找到了7个,所以更可能是查询返回了7行)。解决方案:1.检查SQL语句:确保该查询应该只返回一行数据。如果是因为条件不足,需要添加足够的条件(例如使用主键或唯一约束的列)来确保唯一性。2.如果确实可能返回多行,但只需要第一行,可以使用LIMIT1(在MySQL中)或类似的方法限制结果数量,然后使用selectOne。但注意,这可能会隐藏数据问题。3.如果查询预期就是返回多个结果,那么应该使用selectList方法,并将返回结果作为列表处理。另外,根据引用[2]中的情况,当进行多表连接查询(尤其是一对多关系)时,即使主表只有一行,但由于从表有多行,会导致返回多行结果(主表数据重复)。在这种情况下,MyBatis的结果映射(使用<collection>)可以正确映射成一个对象(包含一个集合),但是注意:此时查询返回的行数仍然是多个。因此,如果我们在这种情况下使用selectOne,就会报错。因此,我们需要区分:-如果查询是用于获取单个对象(例如根据主键查询),那么应该确保SQL只返回一行。-如果查询是用于获取一个具有关联集合的对象(一对多),那么应该使用selectOne,并且确保在resultMap中配置了<collection>来处理关联集合。但是,注意:在这种情况下,MyBatis的selectOne方法实际上不能使用,因为底层的SQL执行返回的是多行。所以,正确的做法是使用selectList,然后取第一个元素?或者MyBatis的resultMap可以处理多行结果并映射成一个对象?实际上,MyBatis的resultMap可以处理这种情况,即使返回多行,也可以映射成一个对象(关联集合被填充)。但是,我们需要注意的是,如果使用selectOne,MyBatis内部会调用sqlSession.selectOne,而该方法会检查返回的行数,如果多于一行就会抛出异常。因此,对于这种一对多的查询,我们应该使用selectList,然后从列表中取第一个(如果确定只有一个主对象)?或者,我们也可以使用selectOne,前提是我们确保查询条件能够使主表只返回一行?但即使主表一行,由于从表多行,返回结果集还是多行,所以selectOne会报错。引用[2]中的例子,他们使用的是resultMap配置了<collection>,但是查询方法使用的是selectOne,所以会报错。因此,对于这种一对多查询,应该使用selectList方法,并且返回类型是单个对象(Classes),但是MyBatis会正确映射,即使返回多行,也会将多行数据映射到一个对象中(关联的集合属性被填充)。但是,selectList返回的是一个列表,即使我们只期望一个对象。所以,我们可以在调用selectList后取第一个元素,或者修改方法返回类型为List,然后调用方判断。然而,实际上MyBatis的resultMap可以处理这种情况:当使用selectList时,返回的列表大小应该是1(因为主表条件只查一行),但内部通过<collection>将多行结果集映射到一个对象的集合属性中。所以,我们可以这样:List<Classes>list=sqlSession.selectList("findById",id);Classesclasses=list.isEmpty()?null:list.get(0);但是,如果我们希望使用selectOne,那么我们需要确保查询只返回一行。对于一对多的情况,我们无法改变返回行数(因为关联表有多行),所以不能使用selectOne。因此,针对用户的问题:返回了7个结果,我们需要判断:-如果查询本应返回一个对象(没有关联集合),那么需要修改SQL条件,确保只返回一行。-如果查询是一个包含关联集合的查询,那么应该使用selectList,然后取第一个(如果确定主表条件只返回一行)。步骤:1.检查SQL语句:运行该SQL,看是否返回7行。如果是,检查条件是否足够唯一。2.检查映射文件中的resultMap:如果包含<collection>,那么说明是一对多查询,应该使用selectList。3.修改DAO方法:将使用selectOne的地方改为selectList,并调整返回类型(或者从列表中取出第一个对象)。举例:原方法:publicAddressselectAddress(intid){returnsqlSession.selectOne("selectAddress",id);}修改为:publicAddressselectAddress(intid){List<Address>list=sqlSession.selectList("selectAddress",id);if(list.size()>1){//这里可以根据需要处理,比如抛出异常,或者取第一个//但正常情况下,因为按id查,应该只有一条主记录,所以list的大小应该为1(即使有多个关联对象,也会被映射到同一个对象的集合中)//所以这里如果大于1,说明有多个主记录,这是不应该的thrownewRuntimeException("Multipleaddressesfoundforid:"+id);}returnlist.isEmpty()?null:list.get(0);}但是,注意:在resultMap配置正确的情况下,即使SQL返回多行,MyBatis也会将属于同一个主对象的行合并成一个对象(通过<id>配置的主键字段)。因此,使用selectList查询,返回的列表大小应该等于主对象的个数。如果主对象只有一个(比如按主键查),那么列表大小就是1。因此,如果用户遇到Expectedoneresult(ornull)tobereturnedbyselectOne(),butfound:7,那么很可能是没有使用<collection>,而是使用了<result>来映射关联对象?或者查询条件返回了多个主对象?需要具体分析。总结:1.如果是简单查询(没有关联集合),返回多个结果,则需修改SQL条件。2.如果是一对多查询,则必须使用selectList,不能使用selectOne。根据引用[2]的例子,他们的resultMap配置了<collection>,所以应该使用selectList。而引用[3]中,他们使用selectOne,但返回了9个结果,同样应该改为selectList。因此,用户的问题可能是:-SQL查询返回了7个主记录(而不是因为关联导致的7行),那么需要修改查询条件。-或者,是一对多查询,但错误地使用了selectOne。建议用户检查SQL和resultMap配置,并根据情况选择使用selectList。</think>### MyBatis 查询报错:Expected one result (or null) to be returned by selectOne(), but found: 7 当 MyBatis 的 `selectOne()` 方法预期返回单个结果(或 `null`),但实际查询返回了 **7 个结果**时,会抛出此异常。以下是根本原因和解决方案: #### 一、根本原因分析 1. **SQL 查询逻辑问题** SQL 语句的查询条件**不足以唯一标识一行数据**(如未使用主键或唯一约束字段),导致返回多行结果。例如: ```sql SELECT * FROM users WHERE status = 'ACTIVE'; -- 可能返回多个活跃用户 ``` 2. **结果映射配置错误** 在 `resultMap` 中处理一对多关系时(如 `<collection>` 标签),若未正确配置主对象标识(`<id>` 标签),MyBatis 无法合并关联数据: ```xml <resultMap id="userMap" type="User"> <!-- 缺少 <id> 标签定义主键 --> <result column="name" property="name"/> <collection property="orders" ofType="Order"> <!-- 关联集合 --> <id column="order_id" property="id"/> </collection> </resultMap> ``` 3. **DAO 方法使用错误** 预期返回多个结果时仍调用 `selectOne()` 方法: ```java // 错误:应使用 selectList 但误用 selectOne User user = sqlSession.selectOne("findUsersByStatus", "ACTIVE"); ``` --- #### 二、解决方案 ##### 1. **修正 SQL 查询条件** 确保查询条件能唯一锁定单行数据: ```sql -- 使用主键或唯一字段 SELECT * FROM users WHERE id = #{id}; ``` ##### 2. **正确配置 resultMap** 添加 `<id>` 标签标识主对象,使 MyBatis 合并关联数据: ```xml <resultMap id="userMap" type="User"> <id column="id" property="id"/> <!-- 关键:定义主键 --> <result column="name" property="name"/> <collection property="orders" ofType="Order"> <id column="order_id" property="id"/> </collection> </resultMap> ``` ##### 3. **替换 DAO 方法为 selectList** 当查询可能返回多行时,改用 `selectList()` 并处理返回集合: ```java // 正确:使用 selectList 获取结果集 List<User> users = sqlSession.selectList("findUsersByStatus", "ACTIVE"); if (!users.isEmpty()) { User user = users.get(0); // 取第一个结果(按需) } ``` ##### 4. **添加 SQL 限制子句** 若只需首行数据,通过 SQL 限制结果数量: ```sql SELECT * FROM users WHERE status = 'ACTIVE' LIMIT 1; -- MySQL SELECT * FROM users WHERE status = 'ACTIVE' FETCH FIRST 1 ROWS ONLY; -- Oracle ``` --- #### 三、最佳实践 - **调试 SQL**:直接运行 SQL 到数据库验证返回行数。 - **日志分析**:开启 MyBatis SQL 日志(配置 `log4j.logger.org.mybatis=DEBUG`)检查实际执行语句。 - **防御性编程**:对非唯一条件查询,默认使用 `selectList()` 并处理集合结果。 > **关键总结**:`selectOne()` 仅适用于**确定返回单行**的场景;多行结果必须用 `selectList()`,关联查询需正确配置 `<id>` 标签[^1][^2][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值