EmptyResultDataAccessException:字符串JdbcTemplate的查询结

2019-05-14 03:43发布

我使用的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'";
        cert = (String) jdbc.queryForObject(sql, String.class); 
        return cert;
    }

我的方案是完全可以不得到一击在我的查询,所以我的问题是我怎么围绕以下错误消息得到。

EmptyResultDataAccessException: Incorrect result size: expected 1, actual 0

这似乎给我,我应该只是拿回一个空,而不是抛出异常。 我怎样才能解决这个问题? 提前致谢。

Answer 1:

在JdbcTemplate的, queryForIntqueryForLongqueryForObject所有这些方法预计执行的查询将返回一个只有一行。 如果你没有行或多个行,这将导致IncorrectResultSizeDataAccessException 。 现在,正确的做法是不捕获这个异常或EmptyResultDataAccessException ,但要确保你使用的应该只返回一行的查询。 如果一切是不可能的,那么使用query方法来代替。

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.    
}


Answer 2:

你也可以使用一个ResultSetExtractor代替的RowMapper 。 两者都是一样彼此容易,唯一的区别是你调用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"));

您现在可以直接使用的方法在此映射器映射Account从查询的域对象( 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 =
        singletonExtractor(MAPPER_ACCOUNT);

public Account getAccount(long id) {
    return jt.query(SELECT_ACCOUNT, EXTRACTOR_ACCOUNT, id);
}

我希望这有助于证明你现在可以很容易地在一个强大的方式来让您的站点更简单的结合部位。

更新2:与结合可选的用于可选的值,而不是空。

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 =
        singletonOptionalExtractor(MAPPER_DISCOUNT);

public double getDiscount(long accountId) {
    return jt.query(SELECT_DISCOUNT, EXTRACTOR_DISCOUNT, accountId)
            .orElse(0.0);
}


Answer 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);
    }
}


Answer 4:

其实,你可以玩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 ,但不throw new EmptyResultDataAccessExceptionsize == 0



Answer 5:

由于返回一个空当没有数据是我想用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);

我很想知道,如果任何人认为这是一个好主意吗?



Answer 6:

好吧,我想通了。 我只是包裹在一个尝试捕捉并发送回零。

    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;
    }


Answer 7:

你可以使用一组功能,使您的查询总是返回一个结果。 即

MIN(ID_NMB_SRZ)


Answer 8:

在Postgres里,你可以做几乎任何单个值查询通过包装它返回一个值或空:

SELECT (SELECT <query>) AS value

因此避免在呼叫者的复杂性。



Answer 9:

由于getJdbcTemplate()。queryForMap期望一个的最小尺寸,但是当它返回null它显示EmptyResultDataAccesso固定DIS时可以使用下面的逻辑

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;
}


Answer 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);

不明确的性能,但它的工作原理和整齐。



Answer 11:

拜伦,你可以试试这个..

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();
        }


Answer 12:

使

    jdbcTemplate.queryForList(sql, String.class)

工作,请确保您的JdbcTemplate是类型

    org.springframework.jdbc.core.JdbcTemplate


Answer 13:

我们可以使用查询,而不是queryForObject,查询和queryForObject之间的主要区别是对象的该查询返回列表(基于行映射器返回类型),如果没有从数据库中没有接收到数据,同时queryForObject总是希望只有一个对象是列表可以是空的从数据库既不为null也不多列,并在情况下,如果结果为空,则queryForObject抛出EmptyResultDataAccessException牵强,我写了使用查询,将克服空结果的情况下EmptyResultDataAccessException的问题,一个代码。

 ---------- public UserInfo getUserInfo(String username, String password) { String sql = "SELECT firstname, lastname,address,city FROM users WHERE id=? and pass=?"; List<UserInfo> userInfoList = jdbcTemplate.query(sql, new Object[] { username, password }, new RowMapper<UserInfo>() { public UserInfo mapRow(ResultSet rs, int rowNum) throws SQLException { UserInfo user = new UserInfo(); user.setFirstName(rs.getString("firstname")); user.setLastName(rs.getString("lastname")); user.setAddress(rs.getString("address")); user.setCity(rs.getString("city")); return user; } }); if (userInfoList.isEmpty()) { return null; } else { return userInfoList.get(0); } } 


Answer 14:

我正好赶上这个“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.
}


文章来源: Jdbctemplate query for string: EmptyResultDataAccessException: Incorrect result size: expected 1, actual 0