I am using Jdbctemplate to retrieve a single String value from the db. Here is my method.
public String test() {
String cert=null;
Strin
Ok, I figured it out. I just wrapped it in a try catch and send back 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;
}
to make
jdbcTemplate.queryForList(sql, String.class)
work, make sure your jdbcTemplate is of type
org.springframework.jdbc.core.JdbcTemplate
Since returning a null when there is no data is something I want to do often when using queryForObject I have found it useful to extend JdbcTemplate and add a queryForNullableObject method similar to below.
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));
}
}
You can now use this in your code the same way you used queryForObject
String result = queryForNullableObject(queryString, String.class);
I would be interested to know if anyone else thinks this is a good idea?
You may also use a ResultSetExtractor instead of a RowMapper. Both are just as easy as one another, the only difference is you call 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;
}
});
}
The ResultSetExtractor has the added benefit that you can handle all cases where there are more than one row or no rows returned.
UPDATE: Several years on and I have a few tricks to share. JdbcTemplate works superbly with java 8 lambdas which the following examples are designed for but you can quite easily use a static class to achieve the same.
While the question is about simple types, these examples serve as a guide for the common case of extracting domain objects.
First off. Let's suppose that you have an account object with two properties for simplicity Account(Long id, String name)
. You would likely wish to have a RowMapper
for this domain object.
private static final RowMapper<Account> MAPPER_ACCOUNT =
(rs, i) -> new Account(rs.getLong("ID"),
rs.getString("NAME"));
You may now use this mapper directly within a method to map Account
domain objects from a query (jt
is a JdbcTemplate
instance).
public List<Account> getAccounts() {
return jt.query(SELECT_ACCOUNT, MAPPER_ACCOUNT);
}
Great, but now we want our original problem and we use my original solution reusing the RowMapper to perform the mapping for us.
public Account getAccount(long id) {
return jt.query(
SELECT_ACCOUNT,
rs -> rs.next() ? MAPPER_ACCOUNT.mapRow(rs, 1) : null,
id);
}
Great, but this is a pattern you may and will wish to repeat. So you can create a generic factory method to create a new ResultSetExtractor for the task.
public static <T> ResultSetExtractor singletonExtractor(
RowMapper<? extends T> mapper) {
return rs -> rs.next() ? mapper.mapRow(rs, 1) : null;
}
Creating a ResultSetExtractor now becomes trivial.
private static final ResultSetExtractor<Account> EXTRACTOR_ACCOUNT =
singletonExtractor(MAPPER_ACCOUNT);
public Account getAccount(long id) {
return jt.query(SELECT_ACCOUNT, EXTRACTOR_ACCOUNT, id);
}
I hope this helps to show that you can now quite easily combine parts in a powerful way to make your domain simpler.
UPDATE 2: Combine with an Optional for optional values instead of null.
public static <T> ResultSetExtractor<Optional<T>> singletonOptionalExtractor(
RowMapper<? extends T> mapper) {
return rs -> rs.next() ? Optional.of(mapper.mapRow(rs, 1)) : Optional.empty();
}
Which now when used could have the following:
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);
}
Actually, You can play with JdbcTemplate
and customize your own method as you prefer. My suggestion is to make something like this:
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;
}
It works as the original jdbc.queryForObject
, but without throw new EmptyResultDataAccessException
when size == 0
.
I just catch this "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;
}
}
then you can check:
if(m == null){
// insert operation.
}else{
// update operation.
}