Byr*_*ron 99 java spring jdbctemplate
我正在使用Jdbctemplate从db中检索单个String值.这是我的方法.
    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而不是抛出异常.我怎样才能解决这个问题?提前致谢.
Rak*_*yal 167
在JdbcTemplate的,queryForInt,queryForLong,queryForObject所有这些方法预计执行的查询将返回一个只有一行.如果没有行或多行将导致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.    
}
Bre*_*yan 42
您也可以使用a ResultSetExtractor而不是a 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 lambdas完美配合,以下示例是为其设计的,但您可以非常轻松地使用静态类来实现相同的功能.
虽然问题是关于简单类型,但这些示例可作为提取域对象的常见情况的指南.
首先.为简单起见,假设您有一个具有两个属性的帐户对象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);
}
Phi*_*all 21
这不是一个好的解决方案,因为你依赖于控制流的异常.在您的解决方案中,获取异常是正常的,将它们放在日志中是正常的.
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);
    }
}
小智 7
实际上,您可以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 EmptyResultDataAccessException时间size == 0.  
由于在没有数据时返回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);
我很想知道是否有其他人认为这是个好主意?
好的,我明白了.我只是将它包装在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;
    }
使用 Java 8 或更高版本,您可以使用Optional和 Java Streams。
因此,您可以简单地使用该JdbcTemplate.queryForList()方法,创建一个 Stream 并使用Stream.findFirst()它将返回 Stream 的第一个值或一个空值Optional:
public Optional<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.queryForList(sql, String.class)
            .stream().findFirst();
}
为了提高查询的性能,您可以附加LIMIT 1到您的查询,因此从数据库传输的项目不超过 1 个。
你可以这样做:
String cert = DataAccessUtils.singleResult(
    jdbcTemplate.queryForList(
        "select ID_NMB_SRZ from codb_owner.TR_LTM_SLS_RTN where ID_STR_RT = :id_str_rt and ID_NMB_SRZ = :id_nmb_srz",
        new MapSqlParameterSource()
            .addValue("id_str_rt", "999")
            .addValue("id_nmb_srz", "60230009999999"),
        String.class
    )
)
DataAccessUtils.singleResult + queryForList:
| 归档时间: | 
 | 
| 查看次数: | 208289 次 | 
| 最近记录: |