복붙노트

[SPRING] 문자열에 대한 Jdbctemplate 쿼리 : EmptyResultDataAccessException : 잘못된 결과 크기 : expected 1, actual 0

SPRING

문자열에 대한 Jdbctemplate 쿼리 : EmptyResultDataAccessException : 잘못된 결과 크기 : expected 1, actual 0

Jdbctemplate을 사용하여 db에서 단일 문자열 값을 검색하고 있습니다. 여기 내 방법이 있습니다.

    public String test() {
        String cert=null;
        String sql = "select ID_NMB_SRZ from codb_owner.TR_LTM_SLS_RTN 
             where id_str_rt = '999' and ID_NMB_SRZ = '60230009999999'";
        cert = (String) jdbc.queryForObject(sql, String.class); 
        return cert;
    }

내 시나리오에서 내 질문에 그래서 내게 질문을 얻을 수있는 완전한 완료 가능한 다음 오류 메시지 주위에 어떻게합니까?

EmptyResultDataAccessException: Incorrect result size: expected 1, actual 0

예외를 던지는 대신 null을 돌려 주어야 할 것 같습니다. 이 문제를 어떻게 해결할 수 있습니까? 미리 감사드립니다.

해결법

  1. ==============================

    1.JdbcTemplate, queryForInt, queryForLong, queryForObject에서 이러한 모든 메소드는 실행 된 조회가 단 하나의 행만 리턴 할 것으로 예상합니다. 행이 없거나 하나 이상의 행이 있으면 IncorrectResultSizeDataAccessException이 발생합니다. 이제 올바른 방법은이 예외 또는 EmptyResultDataAccessException을 catch하는 것이 아니라 사용중인 쿼리가 한 행만 반환하도록해야합니다. 전혀 가능하지 않다면 대신 쿼리 메서드를 사용하십시오.

    JdbcTemplate, queryForInt, queryForLong, queryForObject에서 이러한 모든 메소드는 실행 된 조회가 단 하나의 행만 리턴 할 것으로 예상합니다. 행이 없거나 하나 이상의 행이 있으면 IncorrectResultSizeDataAccessException이 발생합니다. 이제 올바른 방법은이 예외 또는 EmptyResultDataAccessException을 catch하는 것이 아니라 사용중인 쿼리가 한 행만 반환하도록해야합니다. 전혀 가능하지 않다면 대신 쿼리 메서드를 사용하십시오.

    List<String> strLst  = getJdbcTemplate().query(sql,new RowMapper {
    
      public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
            return rs.getString(1);
      }
    
    });
    
    if ( strLst.isEmpty() ){
      return null;
    }else if ( strLst.size() == 1 ) { // list contains exactly 1 element
      return strLst.get(0);
    }else{  // list contains more than 1 elements
      //your wish, you can either throw the exception or return 1st element.    
    }
    
  2. ==============================

    2.또한 RowMapper 대신 ResultSetExtractor를 사용할 수도 있습니다. 둘 다 서로 쉽게 동일하지만, 유일한 차이점은 ResultSet.next ()를 호출한다는 것입니다.

    또한 RowMapper 대신 ResultSetExtractor를 사용할 수도 있습니다. 둘 다 서로 쉽게 동일하지만, 유일한 차이점은 ResultSet.next ()를 호출한다는 것입니다.

    public String test() {
        String sql = "select ID_NMB_SRZ from codb_owner.TR_LTM_SLS_RTN "
                     + " where id_str_rt = '999' and ID_NMB_SRZ = '60230009999999'";
        return jdbc.query(sql, new ResultSetExtractor<String>() {
            @Override
            public String extractData(ResultSet rs) throws SQLException,
                                                           DataAccessException {
                return rs.next() ? rs.getString("ID_NMB_SRZ") : null;
            }
        });
    }
    

    ResultSetExtractor에는 둘 이상의 행이 있거나 행이 리턴되지 않은 모든 경우를 처리 할 수있는 추가 이점이 있습니다.

    업데이트 : 몇 년 동안 공유 할 몇 가지 트릭이 있습니다. JdbcTemplate은 다음 예제가 설계된 Java 8 lambda와 훌륭하게 작동하지만 정적 클래스를 사용하여 동일하게 구현할 수 있습니다.

    간단한 유형에 대한 질문이지만이 예제는 도메인 객체 추출의 일반적인 경우에 대한 지침으로 사용됩니다.

    우선. Account (Long ID, String name)를 단순화하기 위해 두 개의 특성을 가진 계정 오브젝트가 있다고 가정 해 보겠습니다. 이 도메인 객체에 RowMapper를 사용하고자 할 것입니다.

    private static final RowMapper<Account> MAPPER_ACCOUNT =
            (rs, i) -> new Account(rs.getLong("ID"),
                                   rs.getString("NAME"));
    

    이제이 mapper를 메소드 내에서 직접 사용하여 쿼리 (jt는 JdbcTemplate 인스턴스)에서 계정 도메인 객체를 매핑 할 수 있습니다.

    public List<Account> getAccounts() {
        return jt.query(SELECT_ACCOUNT, MAPPER_ACCOUNT);
    }
    

    좋습니다.하지만 이제는 원래의 문제를 원하며 RowMapper를 다시 사용하여 원래의 솔루션을 사용하여 매핑을 수행합니다.

    public Account getAccount(long id) {
        return jt.query(
                SELECT_ACCOUNT,
                rs -> rs.next() ? MAPPER_ACCOUNT.mapRow(rs, 1) : null,
                id);
    }
    

    좋습니다. 그러나 이것은 반복 할 수있는 패턴입니다. 따라서 작업을위한 새로운 ResultSetExtractor를 생성하기 위해 일반적인 팩토리 메소드를 생성 할 수 있습니다.

    public static <T> ResultSetExtractor singletonExtractor(
            RowMapper<? extends T> mapper) {
        return rs -> rs.next() ? mapper.mapRow(rs, 1) : null;
    }
    

    ResultSetExtractor를 생성하는 것은 이제는 사소한 일이되었습니다.

    private static final ResultSetExtractor<Account> EXTRACTOR_ACCOUNT =
            nullableExtractor(MAPPER_ACCOUNT);
    
    public Account getAccount(long id) {
        return jt.query(SELECT_ACCOUNT, EXTRACTOR_ACCOUNT, id);
    }
    

    이 방법으로 부품을 강력한 방법으로 결합하여 도메인을 단순하게 만들 수 있음을 알 수 있기를 바랍니다.

    업데이트 2 : null 대신 선택적 값에 대해 Optional 옵션과 결합하십시오.

    public static <T> ResultSetExtractor<Optional<T>> singletonOptionalExtractor(
            RowMapper<? extends T> mapper) {
        return rs -> rs.next() ? Optional.of(mapper.mapRow(rs, 1)) : Optional.empty();
    }
    

    지금 사용하면 다음과 같은 것을 가질 수 있습니다 :

    private static final ResultSetExtractor<Optional<Double>> EXTRACTOR_DISCOUNT =
            nullableOptionalExtractor(MAPPER_DISCOUNT);
    
    public double getDiscount(long accountId) {
        return jt.query(SELECT_DISCOUNT, EXTRACTOR_DISCOUNT, accountId)
                .orElse(0.0);
    }
    
  3. ==============================

    3.제어 흐름에 대한 예외에 의존하기 때문에 좋은 해결책은 아닙니다. 솔루션에서 예외를 얻는 것은 정상적인 일이며, 로그에 넣는 것이 정상입니다.

    제어 흐름에 대한 예외에 의존하기 때문에 좋은 해결책은 아닙니다. 솔루션에서 예외를 얻는 것은 정상적인 일이며, 로그에 넣는 것이 정상입니다.

    public String test() {
        String sql = "select ID_NMB_SRZ from codb_owner.TR_LTM_SLS_RTN where id_str_rt = '999' and ID_NMB_SRZ = '60230009999999'";
        List<String> certs = jdbc.queryForList(sql, String.class); 
        if (certs.isEmpty()) {
            return null;
        } else {
            return certs.get(0);
        }
    }
    
  4. ==============================

    4.사실, 당신은 JdbcTemplate을 가지고 놀 수 있으며 원하는대로 당신 만의 방법을 커스터마이즈 할 수 있습니다. 내 제안은 다음과 같이 만드는 것입니다.

    사실, 당신은 JdbcTemplate을 가지고 놀 수 있으며 원하는대로 당신 만의 방법을 커스터마이즈 할 수 있습니다. 내 제안은 다음과 같이 만드는 것입니다.

    public String test() {
        String cert = null;
        String sql = "select ID_NMB_SRZ from codb_owner.TR_LTM_SLS_RTN
            where id_str_rt = '999' and ID_NMB_SRZ = '60230009999999'";
        ArrayList<String> certList = (ArrayList<String>) jdbc.query(
            sql, new RowMapperResultSetExtractor(new UserMapper()));
        cert =  DataAccessUtils.singleResult(certList);
    
        return cert;
    }
    

    원래 jdbc.queryForObject로 작동하지만 크기 == 0 인 경우 새 EmptyResultDataAccessException을 throw하지 않습니다.

  5. ==============================

    5.데이터가 없을 때 널 (null)을 리턴하는 것은 queryForObject를 사용할 때 자주하고 싶어하는 것이므로 JdbcTemplate을 확장하고 아래와 유사한 queryForNullableObject 메소드를 추가하는 것이 유용하다는 것을 알았다.

    데이터가 없을 때 널 (null)을 리턴하는 것은 queryForObject를 사용할 때 자주하고 싶어하는 것이므로 JdbcTemplate을 확장하고 아래와 유사한 queryForNullableObject 메소드를 추가하는 것이 유용하다는 것을 알았다.

    public class JdbcTemplateExtended extends JdbcTemplate {
    
        public JdbcTemplateExtended(DataSource datasource){
            super(datasource);
        }
    
        public <T> T queryForNullableObject(String sql, RowMapper<T> rowMapper) throws DataAccessException {
            List<T> results = query(sql, rowMapper);
    
            if (results == null || results.isEmpty()) {
                return null;
            }
            else if (results.size() > 1) {
                throw new IncorrectResultSizeDataAccessException(1, results.size());
            }
            else{
                return results.iterator().next();
            }
        }
    
        public <T> T queryForNullableObject(String sql, Class<T> requiredType) throws DataAccessException {
            return queryForObject(sql, getSingleColumnRowMapper(requiredType));
        }
    
    }
    

    이제 코드에서 queryForObject와 같은 방식으로이 코드를 사용할 수 있습니다.

    String result = queryForNullableObject(queryString, String.class);
    

    나는 다른 사람들이 이것이 좋은 생각이라고 생각하는지 알기를 원할 것입니다.

  6. ==============================

    6.좋아, 알아 냈어. 방금 try catch에 포장하고 null을 다시 보냈습니다.

    좋아, 알아 냈어. 방금 try catch에 포장하고 null을 다시 보냈습니다.

        public String test() {
                String cert=null;
                String sql = "select ID_NMB_SRZ from codb_owner.TR_LTM_SLS_RTN 
                         where id_str_rt = '999' and ID_NMB_SRZ = '60230009999999'";
                try {
                    Object o = (String) jdbc.queryForObject(sql, String.class);
                    cert = (String) o;
                } catch (EmptyResultDataAccessException e) {
                    e.printStackTrace();
                }
                return cert;
        }
    
  7. ==============================

    7.쿼리가 항상 결과를 반환하도록 그룹 함수를 사용할 수 있습니다. 즉

    쿼리가 항상 결과를 반환하도록 그룹 함수를 사용할 수 있습니다. 즉

    MIN(ID_NMB_SRZ)
    
  8. ==============================

    8.Postgres에서는 거의 모든 단일 값 쿼리가 값을 래핑하여 null을 반환 할 수 있습니다.

    Postgres에서는 거의 모든 단일 값 쿼리가 값을 래핑하여 null을 반환 할 수 있습니다.

    SELECT (SELECT <query>) AS value
    

    호출자의 복잡성을 피할 수 있습니다.

  9. ==============================

    9.getJdbcTemplate (). queryForMap은 최소 크기를 예상하므로 null을 반환하면 EmptyResultDataAccesso가 다음 논리를 사용할 수있을 때 수정합니다.

    getJdbcTemplate (). queryForMap은 최소 크기를 예상하므로 null을 반환하면 EmptyResultDataAccesso가 다음 논리를 사용할 수있을 때 수정합니다.

    Map<String, String> loginMap =null;
    try{
        loginMap = getJdbcTemplate().queryForMap(sql, new Object[] {CustomerLogInInfo.getCustLogInEmail()});
    }
    catch(EmptyResultDataAccessException ex){
        System.out.println("Exception.......");
        loginMap =null;
    }
    if(loginMap==null || loginMap.isEmpty()){
        return null;
    }
    else{
        return loginMap;
    }
    
  10. ==============================

    10.나는 봄 포럼에 게시하기 전에 이것을 처리했다.

    나는 봄 포럼에 게시하기 전에 이것을 처리했다.

    http://forum.spring.io/forum/spring-projects/data/123129-frustrated-with-emptyresultdataaccessexception

    우리가받은 조언은 일종의 SQlQuery를 사용하는 것이 었습니다. 여기에 없을 수도있는 DB에서 가치를 얻으려고 할 때 우리가 한 일의 예가 있습니다.

    @Component
    public class FindID extends MappingSqlQuery<Long> {
    
            @Autowired
            public void setDataSource(DataSource dataSource) {
    
                    String sql = "Select id from address where id = ?";
    
                    super.setDataSource(dataSource);
    
                    super.declareParameter(new SqlParameter(Types.VARCHAR));
    
                    super.setSql(sql);
    
                    compile();
            }
    
            @Override
            protected Long mapRow(ResultSet rs, int rowNum) throws SQLException {
                    return rs.getLong(1);
            }
    

    DAO에서는 다음을 호출합니다.

    Long id = findID.findObject(id);
    

    성능에 대해서는 명확하지 않지만 작동하며 깔끔합니다.

  11. ==============================

    11.Byron에게, 당신은 이것을 시도 할 수 있습니다 ..

    Byron에게, 당신은 이것을 시도 할 수 있습니다 ..

    public String test(){
                    String sql = "select ID_NMB_SRZ from codb_owner.TR_LTM_SLS_RTN 
                         where id_str_rt = '999' and ID_NMB_SRZ = '60230009999999'";
                    List<String> li = jdbcTemplate.queryForList(sql,String.class);
                    return li.get(0).toString();
            }
    
  12. ==============================

    12.만들다

    만들다

        jdbcTemplate.queryForList(sql, String.class)
    

    작업, jdbcTemplate이 유형인지 확인하십시오.

        org.springframework.jdbc.core.JdbcTemplate
    
  13. ==============================

    13.우리는 queryForObject 대신에 query를 사용할 수 있습니다. query와 queryForObject의 주된 차이점은 Object의 반환 목록 (Row mapper 반환 유형에 기반)입니다. queryForObject는 항상 단일 객체만을 기대하면서 데이터베이스로부터 데이터를받지 않으면이 목록은 비어있을 수 있습니다. db에서 Null이 아니고 여러 행을 가져오고 결과가 비어있는 경우 queryForObject는 EmptyResultDataAccessException을 throw합니다. null 결과의 경우 EmptyResultDataAccessException 문제를 해결할 쿼리를 사용하여 하나의 코드를 작성했습니다.

    우리는 queryForObject 대신에 query를 사용할 수 있습니다. query와 queryForObject의 주된 차이점은 Object의 반환 목록 (Row mapper 반환 유형에 기반)입니다. queryForObject는 항상 단일 객체만을 기대하면서 데이터베이스로부터 데이터를받지 않으면이 목록은 비어있을 수 있습니다. db에서 Null이 아니고 여러 행을 가져오고 결과가 비어있는 경우 queryForObject는 EmptyResultDataAccessException을 throw합니다. null 결과의 경우 EmptyResultDataAccessException 문제를 해결할 쿼리를 사용하여 하나의 코드를 작성했습니다.

  14. ==============================

    14.나는이 "EmptyResultDataAccessException"을 잡는다.

    나는이 "EmptyResultDataAccessException"을 잡는다.

    public Myclass findOne(String id){
        try {
            Myclass m = this.jdbcTemplate.queryForObject(
                    "SELECT * FROM tb_t WHERE id = ?",
                    new Object[]{id},
                    new RowMapper<Myclass>() {
                        public Myclass mapRow(ResultSet rs, int rowNum) throws SQLException {
                            Myclass m = new Myclass();
                            m.setName(rs.getString("name"));
                            return m;
                        }
                    });
            return m;
        } catch (EmptyResultDataAccessException e) { // result.size() == 0;
            return null;
        }
    }
    

    다음을 확인할 수 있습니다.

    if(m == null){
        // insert operation.
    }else{
        // update operation.
    }
    
  15. from https://stackoverflow.com/questions/10606229/jdbctemplate-query-for-string-emptyresultdataaccessexception-incorrect-result by cc-by-sa and MIT license