{"record":{"id":"07d1e6c393efa916","repo":"mybatis/mybatis-3","slug":"statement-returned-results-where-exactly-one-1","errorCode":null,"errorMessage":"Statement returned {} results where exactly one (1) was expected.","messagePattern":"Statement returned (.+?) results where exactly one \\(1\\) was expected\\.","errorType":"exception","errorClass":"SQLException","httpStatus":null,"severity":"error","filePath":"src/main/java/org/apache/ibatis/jdbc/SqlRunner.java","lineNumber":72,"sourceCode":"  }\n\n  /**\n   * Executes a SELECT statement that returns one row.\n   *\n   * @param sql\n   *          The SQL\n   * @param args\n   *          The arguments to be set on the statement.\n   *\n   * @return The row expected.\n   *\n   * @throws SQLException\n   *           If less or more than one row is returned\n   */\n  public Map<String, Object> selectOne(String sql, Object... args) throws SQLException {\n    List<Map<String, Object>> results = selectAll(sql, args);\n    if (results.size() != 1) {\n      throw new SQLException(\"Statement returned \" + results.size() + \" results where exactly one (1) was expected.\");\n    }\n    return results.get(0);\n  }\n\n  /**\n   * Executes a SELECT statement that returns multiple rows.\n   *\n   * @param sql\n   *          The SQL\n   * @param args\n   *          The arguments to be set on the statement.\n   *\n   * @return The list of rows expected.\n   *\n   * @throws SQLException\n   *           If statement preparation or execution fails\n   */\n  public List<Map<String, Object>> selectAll(String sql, Object... args) throws SQLException {","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/mybatis/mybatis-3/blob/008069adb1b089579b5dcba87ee591908b263274/src/main/java/org/apache/ibatis/jdbc/SqlRunner.java#L54-L90","documentation":"SqlRunner.selectOne(String sql, Object... args) executes the query via selectAll() and throws SQLException unless exactly one row comes back. Both zero rows and multiple rows are errors: it is a strict 'expect exactly one' API, unlike selectOne in the mapper layer which returns null for zero rows.","triggerScenarios":"Calling selectOne with a query that matches 0 rows (lookup by absent id) or >1 rows (non-unique filter, bad WHERE clause). Typical in test utilities that fetch generated keys or verify a single row: new SqlRunner(conn).selectOne(\"select * from users where id=?\", id).","commonSituations":"Verification queries in tests after inserts; querying by a natural key that turns out not unique; a WHERE clause accidentally omitted or too broad; data not yet committed by another transaction so zero rows are visible.","solutions":["Use selectAll(sql, args) and inspect the list size when 0..N rows are acceptable.","Tighten the WHERE clause / add LIMIT 1 semantics or query by the true primary key so exactly one row matches.","If zero rows is a valid outcome, branch on selectAll().isEmpty() before treating the row as present.","Verify the row was actually committed before querying from another connection."],"exampleFix":"// before\nMap<String,Object> row = sqlRunner.selectOne(\"select * from users where name = ?\", name);\n\n// after\nList<Map<String,Object>> rows = sqlRunner.selectAll(\"select * from users where name = ?\", name);\nif (rows.size() != 1) {\n  throw new IllegalStateException(\"Expected 1 user named \" + name + \" but got \" + rows.size());\n}\nMap<String,Object> row = rows.get(0);","handlingStrategy":"validation","validationCode":"// When 0..N rows are possible, check before selecting one\nList<Map<String, Object>> rows = runner.selectAll(\"select * from users where id = ?\", id);\nif (rows.size() != 1) {\n  throw new NoSuchElementException(\"user \" + id + \" not unique: \" + rows.size() + \" rows\");\n}\nMap<String, Object> row = rows.get(0);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Prefer selectAll + explicit size check over selectOne whenever absence is a normal outcome.","Query by primary key or add uniqueness constraints so 'exactly one' is guaranteed by schema.","In tests, assert row counts with selectAll so failures carry the actual count in the message."],"tags":["mybatis","sql","query","sql-runner"],"backgroundTag":null,"analyzedSha":"008069adb1b089579b5dcba87ee591908b263274","analyzedAt":"2026-08-14T13:07:10.264Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}