简体   繁体   中英

How to use Spring jdbc templates (jdbcTemplate or namedParameterJDBCTem) to retrieve values from database

Few days into Spring now. Integrating Spring-JDBC into my web application. I was successfully able to preform CRUD operations on my DB, impressed with boiler-plate code reduction. But I am failing to use the query*() methods provided in NamedParameterJDBCTemplate . Most of the examples on the internet provide the usage of either RowMapper or ResultSetExtractor . Though both uses are fine, it forces me to create classes which have to implement these interfaces. I have to create bean for every type of data I am loading for the DB (or maybe I am mistaken).

Problem arises in code section where I have used something like this:

String query="select username, password from usertable where username=?"
ps=conn.prepareStatement(query);
ps.setString(username);
rs=ps.executeQuery();

if(rs.next()){
    String username=rs.getString("username");
    String password=rs.getString("password")

    //Performs operation on them
}  

As these values are not stored in any bean and used directly, I am not able to integrate jdbcTemplate in these kind of situations. Another situation arises when I am extracting only part of properties present in bean from my database. Example:

public class MangaBean{
    private String author;
    private String title;
    private String isbn;
    private String releaseDate;
    private String rating;

    //getters and setters

} 

Mapper:

public class MangaBeanMapper implements RowMapper<MangaBean>{

    @Override
    public MangaBean mapRow(ResultSet rs, int arg1) throws SQLException {
        MangaBean mb=new MangaBean();
        mb.setAuthor(rs.getString("author"));
        mb.setTitle(rs.getString("title"));
        mb.setIsbn(rs.getString("isbn"));
        mb.setReleaseDate(rs.getString("releaseDate"));
        mb.setRating(rs.getString("rating"));
        return mb;
    }
}

The above arrangement runs fine like this:

String query="select * from manga_data where isbn=:isbn"
Map<String, String> paramMap=new HashMap<String, String>();
paramMap.put("isbn", someBean.getIsbn());
return template.query(query, paramMap, new MangaBeanMapper());

However, if I only want to retrieve two/three values from my db, I cannot use the above pattern as it generates a BadSqlGrammarException: releaseDate does not exist in ResultSet . Example :

String query="select title, author where isbn=:isbn"
Map<String, String> paramMap=new HashMap<String, String>();
paramMap.put("isbn", someBean.getIsbn());
return template.query(query, paramMap, new MangaBeanMapper());

Template is an instance of NamedParameterJDBCTemplate . Please advice me solutions for these situations.

The other answers are sensible: you should create a DTO bean, or use the BeanPropertyRowMapper .

But if you want to be able to have more control than the BeanPropertyRowMapper, (or reflection makes it too slow), you can use the

queryForMap

method, which will return you a list of Maps (one per row) with the returned columns as keys. Because you can call get(/* key that is not there */) on a Map without throwing an exception (it will just return null), you can use the same code to populate your object irrespective of which columns you selected.

You don't even need to write your own RowMapper, just use the BeanPropertyRowMapper that spring provides. The way it works is it matches the column names returned to the properties of your bean. Your query has columns that match your bean exactly, if it didn't you would use an as in your select as follows...

-- This query matches a property named matchingName in the bean
select my_column_that doesnt_match as matching_name from mytable;

The BeanPropertyRowMapper should work with both queries you listed.

In yr first example I would just create a DTO Bean/Value object to store them. There is a reason its a commonly implemented pattern, it takes minutes to code and provides many long term benefits.

In your second example, create a second implementation of rowmapper where you don;t set the fields, or supply a null/subsitute value to mangabean where necessary :

    @Override
    public MangaBean mapRow(ResultSet rs, int arg1) throws SQLException {
    MangaBean mb=new MangaBean();
    mb.setAuthor(rs.getString("author"));
    mb.setTitle(rs.getString("title"));
   /* mb.setIsbn("unknown");*/
    mb.setReleaseDate("unknown");
    mb.setRating(null);
    return mb;
    }

Typically, yes : for most queries you would create a bean or object to transform the result into. I would suggest that more most cases, that's want you want to do.

However, you can create a RowMapper that maps a result set to a map, instead of a bean, like this. Downside would be be losing the type management of beans, and you'd be relying on your jdbc driver to return the correct type for each column.

As @NimChimpskey has just posted, it's best to create a tiny bean object : but if you really don't want to do that, this is another option.

  class SimpleRowMapper implements RowMapper<Map<String, Object>> {
    String[] columns;

    SimpleRowMapper(String[] columns) {
      this.columns = columns;
    }

    @Override
    public Map<String, Object> mapRow(ResultSet resultSet, int i) throws SQLException {
      Map<String, Object> rowAsMap = new HashMap<String, Object>();
      for (String column : columns) {
        rowAsMap.put(column, resultSet.getObject(column));
      }
      return rowAsMap;
    }
  }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM