Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Some doubts about RowMapper use in JDBC in a Spring Framework application

I am studying how to execute query on a database using JDBC in Spring Framework.

I am following this tutorial: http://www.tutorialspoint.com/spring/spring_jdbc_example.htm

In this tutorial I define a StudentDAO interface which only define the CRUD method that I want.

Then is defined the Student class that is the entity that I want to persist on the Student database table.

Then is defined the StudentMapper class that is a specific implementation of RowMapper interface that, in this case, is used to map a specific record in the ResultSet (returned by a query) to a Student object.

Then I have the StudentJDBCTemplate that rappresent the implementation of my StudentDAO interface, in this class I implement the CRUD method that was defined in the interface.

Ok, and now I have a doubt about how the StudentMapper class work: in this StudentJDBCTemplate class there is defined the method that return the list of all record that are in the Student database table, this one:

   public List<Student> listStudents() {
      String SQL = "select * from Student";
      List <Student> students = jdbcTemplateObject.query(SQL, 
                                new StudentMapper());
      return students;
   }

How you can see, this method return a List of Student object and work in the following way:

the first thing that it do is to define the query that return all record in the Student database table in the SQL String.

Then this query is executed by the query method call on the jdbcTemplateObject object (that is an istance of JdbcTemplate Spring class**

This method take two parameter: the SQL String (that contains the SQL query that must be executed) and a new StudentMapper object that take the ResultSet object returned by the query and map it's record on a new Student object

Reading here: http://static.springsource.org/spring/docs/current/javadoc-api/org/springframework/jdbc/core/JdbcTemplate.html sayas that: Execute a query given static SQL, mapping each row to a Java object via a RowMapper.

My doubt is related to the fact that my StudentMapper map a ResultSet record on a Student object using the mapRow() method, this is the code:

package com.tutorialspoint;

import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;

public class StudentMapper implements RowMapper<Student> {
   public Student mapRow(ResultSet rs, int rowNum) throws SQLException {
      Student student = new Student();
      student.setId(rs.getInt("id"));
      student.setName(rs.getString("name"));
      student.setAge(rs.getInt("age"));
      return student;
   }
}

So, who call this mapRow method? is it called automatically by the Spring Framework? (because in this example is never called manually...)

Tnx

Andrea

Then this query is executed by the query method call on the jdbcTemplateObject object (that is an istance of JdbcTemplate Spring class**

like image 630
AndreaNobili Avatar asked Feb 27 '13 17:02

AndreaNobili


People also ask

What is the purpose of RowMapper interface?

RowMapper<T> interface is used by JdbcTemplate for mapping rows of a ResultSet on a per-row basis. Implementations of this interface perform the actual work of mapping each row to a result object. SQLExceptions if any thrown will be caught and handled by the calling JdbcTemplate.

What is the use of RowMapper in Spring JDBC?

In Spring, the RowMapper interface is used to fetch the records from the database using the query() method of the JdbcTemplate class. RowMapper is a callback interface that is called for each row and maps the row of relations with the instances to the model(user-defined) class.

Is RowMapper functional interface?

RowMapper is a interface with a single abstract method (not inheriting from a method of Object ), so it can be considered a functional interface.

Which method of RowMapper interface in which you have to construct the object that represents a row and return it as the method's return value?

Explanation: From the viewpoint of reuse, it's better to implement the RowMapper interface as a normal class than as an inner class. In the mapRow() method of this interface, you have to construct the object that represents a row and return it as the method's return value.


3 Answers

When you pass an instance of your RowMapper to the JdbcTemplate method

List <Student> students = jdbcTemplateObject.query(SQL, new StudentMapper());

The JdbcTemplate depending on which method you called, will internally use the mapper with the result set it gets from the JDBC Connection to create an object of your requested type. For example, since you called JdbcTemplate#query(String, RowMapper), the method will use your String SQL to query the database and will loop through each "row" in the ResultSet kind of like this:

ResultSet rs = ... // execute query
List<Student> students = ...// some list
int rowNum = 0;
while(rs.next()) {
    Student student = rowMapper.mapRow(rs, rowNum);
    students.add(student);
    rowNum++;
}

return students;

So, Spring's JdbcTemplate method will use the RowMapper you provide and call its mapRow method to create the expected return object.

You might like to look at Martin Fowler's Data Mapper in conjunction with Table Data Gateway for an idea of how these things are distributed and provide low coupling.

like image 131
Sotirios Delimanolis Avatar answered Oct 02 '22 16:10

Sotirios Delimanolis


Here is the typical pattern I use with BeanPropertyRowMapper. It saves a lot of coding. Your query needs to alias each column to match the property name in the class. In this case species_name as species and the other column names happen to match already.

public class Animal {
    String species;
    String phylum;
    String family;
    ...getters and setters omitted
}

@Repository
public class AnimalRepository {
    private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

    @Autowired
    public void setDataSource(DataSource dataSource) {
        this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
    }

    public List<Animal> getAnimalsByPhylum(String phylum) {
        String sql = " SELECT species_name as species, phylum, family FROM animals"
                 +" WHERE phylum = :phylum";

        Map<String, Object> namedParameters = new HashMap<String, Object>();
        namedParameters.put("phylum", phylum);
        SqlParameterSource params = new MapSqlParameterSource(namedParameters);
        List<Animal> records = namedParameterJdbcTemplate.query(sql,
                params, BeanPropertyRowMapper.newInstance(Animal.class));

        return records;
    }
}

An alternative is to use a RowMapper (this example just uses an anonymous class) when you need more customization per row:

    List<Animal> records = namedParameterJdbcTemplate.query(sql,
            params, new RowMapper<Animal>(){
        public Animal mapRow(ResultSet rs, int i) throws SQLException {
            Animal animal = new Animal();   
            animal.setSpecies(rs.getString("species_name"));
            if (some condition) {
                animal.setPhylum(rs.getString("phylum"));
            } else {
                animal.setPhylum(rs.getString("phylum")+someThing());
            }
            animal.setFamily(rs.getString("family"));

            return animal;
        }
    });
like image 28
Jim Avatar answered Oct 02 '22 14:10

Jim


Using RowMapper in Spring

import java.sql.ResultSet;
import java.sql.SQLException;

import org.springframework.jdbc.core.RowMapper;

public class RowsMap implements RowMapper<EmpPojo>{

    @Override
    public EmpPojo mapRow(ResultSet rs, int counts) throws SQLException {
        EmpPojo em=new EmpPojo();
        em.setEid(rs.getInt(1));
        em.setEname(rs.getString(2));
        em.setEsal(rs.getDouble(3));

        return em;
    }

}

Finally in Main class

List<EmpPojo> lm=jt.query("select * from emps", new RowsMap());
for(EmpPojo e:lm)
{
    System.out.println(e.getEid()+" "+e.getEname()+" "+e.getEsal());
}
like image 30
Raja Rao Avatar answered Oct 02 '22 15:10

Raja Rao