質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.64%

DB検索をしてヒットしない場合の判定方法がわかりません。

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,467

usako

score 25

前提・実現したいこと

閲覧いただき、ありがとうございます。
Eclipse,Springを使ってJAVAの勉強をしている(超)初心者です。
元々、受講していた通信教育の課題がDBを使用しておらず、
内部にテーブルを保持している構造だったので、それを
MySQL,JDBCで外部化しようとしています。
一覧表示、更新、参照機能だけのシンプルな画面です。
現在、参照機能で該当データを検索するところまでは動作するよう
になったのですが、わざと存在しないIDを入力すると
以下のエラーが発生してしまいます。
意味は「1件データが返ってくるはずなのに、1件もデータが
返ってこない」といったようなことだと理解しています。
rowNumに該当する件数が入ってくるのではと考えて
以下のようなコードを書いてみたのですが(★部分)、
その前で落ちてしまいます。
どうすべきなのか、何を調べればよいのかなど、
どなたかご教示いただけないでしょうか。 

発生している問題・エラーメッセージ

HTTPステータス 500 - Request processing failed; nested exception is org.springframework.dao.EmptyResultDataAccessException: Incorrect result size: expected 1, actual 0


type 例外レポート

メッセージ Request processing failed; nested exception is org.springframework.dao.EmptyResultDataAccessException: Incorrect result size: expected 1, actual 0

説明 The server encountered an internal error that prevented it from fulfilling this request.

例外 
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.dao.EmptyResultDataAccessException: Incorrect result size: expected 1, actual 0
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982)
    org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:643)
    org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:723)
    org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)



原因 
org.springframework.dao.EmptyResultDataAccessException: Incorrect result size: expected 1, actual 0
    org.springframework.dao.support.DataAccessUtils.requiredSingleResult(DataAccessUtils.java:71)
    org.springframework.jdbc.core.JdbcTemplate.queryForObject(JdbcTemplate.java:795)
    jp.practice.address.RecordManager.selectEmployee(RecordManager.java:57)
    jp.practice.address.AddressBookController.refer(AddressBookController.java:38)
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    java.lang.reflect.Method.invoke(Method.java:498)
    org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
    org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133)
    org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:116)
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
    org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
    org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
    org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
    org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:643)
    org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:723)
    org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)

該当のソースコード

package jp.practice.address;

import java.util.List;
import java.util.Map;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping(value = "/book")
public class AddressBookController {
    private static final String INIT   = "init";
    private static final String REFER  = "refer";
    private static final String UPDATE = "update";
    private static final String LIST   = "list";
    private static final String INIMSG = "従業員番号を入力してください";
    private static final String REFMSG = "情報が取得できました";
    private static final String UPDMSG = "情報を更新してください";
    private static final String ERRMSG = "該当するデータはありません";

    private RecordManager recordManager;
    public AddressBookController(RecordManager recordManager) {
        this.recordManager = recordManager;
    }

    @RequestMapping(value = "/start")
    public String init(AddressBookForm form, Model model) {
        model.addAttribute("message", INIMSG);
        return INIT;
    }

    @RequestMapping(params = "reference")
    public String refer(AddressBookForm form, Model model) {
        Employee employee = recordManager.selectEmployee(form.getId());
        if (employee != null) {
            form.setName(employee.getName());
            form.setPhone(employee.getPhone());
            form.setAddress(employee.getAddress());
            model.addAttribute("message", REFMSG);
            return REFER;
        } else {
            model.addAttribute("message", ERRMSG);
            return INIT;
        }
    }

    @RequestMapping(params = "toInit")
    public String toInit(AddressBookForm form, Model model) {
        model.addAttribute("message", INIMSG);
        return INIT;
    }

    @RequestMapping(params = "update")
    public String update(AddressBookForm form, Model model) {
        Employee employee = recordManager.selectEmployee(form.getId());
        if (employee != null) {
            form.setName(employee.getName());
            form.setPhone(employee.getPhone());
            form.setAddress(employee.getAddress());
            model.addAttribute("message", UPDMSG);
            return UPDATE;
        } else {
            model.addAttribute("message", ERRMSG);
            return INIT;
        }
    }

    @RequestMapping(params = "reflection")
    public String reflection(AddressBookForm form, Model model) {
        Employee employee = new Employee(form.getId(), form.getName(), form.getPhone(), form.getAddress());
        RecordManager.updateEmployee(employee);
        model.addAttribute("message", INIMSG);
        return INIT;
    }

    @RequestMapping(params = "list")
    public String list(Model model) {
        List<Map<String, Object>> employeeList = recordManager.getEmployeeList();
        model.addAttribute("employeeList", employeeList);
        model.addAttribute("employeeCount", employeeList.size());
        return LIST;
    }

}
package jp.practice.address;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Component;
@Component
public class RecordManager {
    private JdbcTemplate jdbcTemplate;

    public  RecordManager(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    /**
     * 指定したID のレコードを取得
     *
     * @param id
     *            従業員番号
     * @return Employee
     */

 * @param name ****/
    public Employee selectEmployee(String id) {
            return jdbcTemplate.queryForObject("select * from unit02.addressbook where id = ? ",
              new Object[]{ id },
            new RowMapper<Employee>() {
            @Override
            public Employee mapRow(ResultSet rs, int rowNum) throws SQLException {
★            if (rowNum == 0){
★                  return null;
★            }
            Employee employee = new Employee("","","","");
              employee.setId(rs.getString("id"));
              employee.setName(rs.getString("name"));
              employee.setPhone(rs.getString("tel"));
              employee.setAddress(rs.getString("address"));
              return employee;
            }
          });
    }

    /**
     * 指定した従業員情報を反映
     *
     * @param emp
     *            従業員情報
     */
    public static void updateEmployee(Employee emp) {
//        int index = list.indexOf(emp);
//        list.set(index, emp);
    }

    public List<Map<String, Object>> getEmployeeList() {
        List<Map<String, Object>> list = jdbcTemplate.queryForList("select * from unit02.addressbook order by id");
    return list;
    }
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • asahina1979

    2019/05/31 19:16

    同じ全文で始める質問ならまず書籍を購入するか別の通信講座を受けてください。

    キャンセル

  • m.ts10806

    2019/05/31 19:24

    エラーもコードブロック対応してもらえたらと。平で提示されてもかなり見づらいので…

    キャンセル

  • usako

    2019/06/03 13:03 編集

    ご指摘ありがとうございます。
    コードブロックで未対応の件、申し訳ないです。お作法を心得ておりませんでした。
    次回から留意するようにします。

    キャンセル

回答 2

checkベストアンサー

+2

原因 
org.springframework.dao.EmptyResultDataAccessException: Incorrect result size: expected 1, actual 0

とありますので,

意味は「1件データが返ってくるはずなのに、1件もデータが返ってこない」といったようなことだと理解しています

で合っていると思います.
では EmptyResultDataAccessException はどこで発生しているかと言いますと,

jp.practice.address.RecordManager.selectEmployee(RecordManager.java:57)

ですから, 対処箇所も合っていると思います.
ですが,

rowNumに該当する件数が入ってくるのではと考えて以下のようなコードを書いてみた

ここは推測するのでは無く, メソッドの仕様をご確認するべきかと思います.

バージョンが新しいか古いかが分かりませんが, 見つけた所では
JdbcTemplate (Spring Framework 5.1.7.RELEASE API)

queryForObject
@Nullable
public <T> T queryForObject(String sql,
                                     @Nullable
                                     Object[] args,
                                     RowMapper<T> rowMapper)
                              throws DataAccessException

rowMapper - a callback that will map one object per row

Throws:
IncorrectResultSizeDataAccessException - if the query does not return exactly one row

とあり, rowMapper( の mapRow メソッド)は per row=行毎 に呼ばれますので, 行が無かった場合は呼ばれなさそうです.
ついでに, mapRow のパラメータ rowNum は
RowMapper (Spring Framework 5.1.7.RELEASE API)

mapRow
@Nullable
T mapRow(ResultSet rs,
                   int rowNum)
            throws SQLException

Parameters:

rowNum - the number of the current row

今が何行目なのかを伝えてくるだけですので 0 はありえません.

対処方ですが, return を try-catch で囲み IncorrectResultSizeDataAccessException( queryForObject の throws に書かれている, EmptyResultDataAccessException のスーパークラスです) をキャッチしたら null を return する方法が一つです.

もう一つは, 同じパラメータで List を返すことの出来る

query
public <T> List<T> query(String sql,
                        @Nullable
                        Object[] args,
                        RowMapper<T> rowMapper)
                 throws DataAccessException

を使うという方法です.
結果を即 return せず, 一度 List<Employee> の変数で受け取り, size() が 0 なら null を return, 1なら変数から取り出した Employee を return すれば良いかと思います.

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/06/03 13:13

    jimbe様、コメントありがとうございます。
    ご教授いただいたようにtry-catch文でIncorrectResultSizeDataAccessExceptionを拾うようにしたらうまく動くようになりました!
    まだまだ勉強不足なのでこれからもがんばりたいと思います。

    キャンセル

+1

例外にはすべて意味があります。

今回の場合

EmptyResultDataAccessException

が投げられているのであーる。

空で(empty)
返却されたデータ(ResultData)
を操作(Access)
したので例外にします(Exception)

という感じ

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/06/03 13:11

    asahina1979様、ご教授ありがとうございます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.64%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる