Best practice to select data using Spring JdbcTemplate
I am facing similar scenario and found a cleaner solution when using ResultSetExtractor instead of RowMapper
jdbcTemplate.query(DBConstants.GET_VENDOR_DOCUMENT, new Object[]{vendorid}, rs -> {
if(rs.next()){
DocumentPojo vendorDoc = new DocumentPojo();
vendorDoc.setRegDocument(rs.getString("registrationdoc"));
vendorDoc.setMsmeLetter(rs.getString("msmeletter"));
vendorDoc.setProprietorshipDocument(rs.getString("propertiershipformat"));
vendorDoc.setNeftDocument(rs.getString("neftdoc"));
vendorDoc.setPanCardDocument(rs.getString("pancard"));
vendorDoc.setCancelledChequeDoc(rs.getString("cheque"));
return vendorDoc;
}
else {
return null;
}
});
If no result is found from database, I had put a if condition for resultset and return null reference. So, I didn't need to try catch the code and pass two queries to database.
Main advantage of ResultSetExtractor (in this scenario) is with ResultsetExtractor you will need to iterate through the result set yourself, say in while loop.
More Points can be found here here
Definitely the first way is the best practice, because in the second way you are hitting the database twice where you should actually hit it only once. This can cause performance issues.
What you need to do is catch the exception EmptyResultDataAccessException
and then return null back. Spring JDBC templates throws back an EmptyResultDataAccessException exception if it doesn't find the data in the database.
Your code should look like this.
try {
sql = "SELECT id FROM tableNmae WHERE column_name ='"+ coulmn value+ "'";
id= jdbcTemplate.queryForObject(sql, Long.class);
}
catch (EmptyResultDataAccessException e) {
if(log.isDebugEnabled()){
log.debug(e);
}
return null
}
This is queryForObject method source code
@Nullable
public <T> T queryForObject(String sql, RowMapper<T> rowMapper) throws
DataAccessException {
List<T> results = this.query(sql, rowMapper);
return DataAccessUtils.nullableSingleResult(results);
}
DataAccessUtils.nullableSingleResult
@Nullable
public static <T> T nullableSingleResult(@Nullable Collection<T> results) throws IncorrectResultSizeDataAccessException {
if (CollectionUtils.isEmpty(results)) {
throw new EmptyResultDataAccessException(1);
} else if (results.size() > 1) {
throw new IncorrectResultSizeDataAccessException(1, results.size());
} else {
return results.iterator().next();
}
}
dunno why they throw exception on empty collection, probably this is just a copy-paste from method above
public static <T> T requiredSingleResult(@Nullable Collection<T> results) throws IncorrectResultSizeDataAccessException {
if (CollectionUtils.isEmpty(results)) {
throw new EmptyResultDataAccessException(1);
} else if (results.size() > 1) {
throw new IncorrectResultSizeDataAccessException(1, results.size());
} else {
return results.iterator().next();
}
}
One more step above the method they shoult have used
@Nullable
public static <T> T singleResult(@Nullable Collection<T> results) throws IncorrectResultSizeDataAccessException {
if (CollectionUtils.isEmpty(results)) {
return null;
} else if (results.size() > 1) {
throw new IncorrectResultSizeDataAccessException(1, results.size());
} else {
return results.iterator().next();
}
}
NOW SOLUTION helped me: Extend JdbcTemlate class (you can construct it with DataSource injected) and overrride the queryForObject method:
@Nullable
public <T> T queryForObject(String sql, RowMapper<T> rowMapper) throws DataAccessException {
List<T> results = this.query(sql, rowMapper);
return DataAccessUtils.singleResult(results);
}
now work with your implementation Don't forget to check if it works on spring version update (very unlikely IMHO)
Better way to Use ifNull in query so if there is null then you get 0 Eg.-
sql = "SELECT ifNull(id,0) FROM tableName WHERE column_name ='"+ coulmn value+ "'";
Using this way you can get as default 0 otherwise your Id