Spring, JDBCTemplate, and Stored Procedures, Part II

March 9th, 2008 by stevi | Filed under Java

In my previous post, I gave an example of code on how to use Spring’s JDBCTemplate to call a stored procedure and process a returned result set using the simple query.

Here’s the code to do the same with overriding Spring’s StoredProcedure class.

The advantage in this method is that using the declareParameter method can help with type checking and make it more clear to the user what expected values are.

The biggest difference to note is the syntax of the query string used in constructing the code. When calling the stored procedure (sproc) via JDBCTemplate.query(), you include the initial “CALL” and include parameters in the actual query string:

String sproc = "call PACKAGE.GET_CUSTOMER(?)";

When using StoredProcedure, you’re just passing in the sproc name; the Spring code will construct the full query as part of the processing:

String sproc = "PACKAGE.GET_CUSTOMER";

I’d recommend using JDBCTemplate.query() when the sproc is very simple – one or two parameters. Once the number of parameters grows, overriding StoredProcedure makes mapping the parameter values more clear and will help with debugging.

package com.stevideter.business.dal.spring;
 
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
import javax.sql.DataSource;
 
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.SqlReturnResultSet;
import org.springframework.jdbc.object.StoredProcedure;
 
import com.stevideter.business.Customer;
import com.stevideter.business.ApplicationException;
 
/**
 * CustomerSpringDao provides the Spring implementation for the CustomerDao
 * @author stevi.deter@gmail.com
 * @version     %I%, %G%
 **/
 
public class CustomerSpringDao {
 
    private static final String CUSTOMER_SPROC = "PACKAGE.GET_CUSTOMER";
    private JdbcTemplate jdbcTemplate;
 
    /**
     *
    * CustomerStoredProcedure provides processing to call SPROC for getting customers
    * @author stevi.deter@gmail.com
    * @version     %I%, %G%
    *
     */
    class CustomerStoredProcedure extends StoredProcedure {
        private static final String ACCOUNTNUMBER_PARAM = "@ACCTNUM";
 
        public CustomerStoredProcedure(DataSource dataSource, String sprocName) {
            super(dataSource, sprocName);
            declareParameter(new SqlReturnResultSet("rs", new CustomerMapper()));
            declareParameter(new SqlParameter(ACCOUNTNUMBER_PARAM,
                    Types.NUMERIC));
            compile();
        }
 
        public Map execute(Long accountNumber) {
            Map inputs = new HashMap();
            inputs.put(ACCOUNTNUMBER_PARAM, accountNumber);
            return super.execute(inputs);
        }
 
    }
 
    public Customer get(Long accountNumber) throws ApplicationException {
        if (accountNumber == null) {
            throw new IllegalArgumentException();
        }
        if (getJdbcTemplate() == null) {
            throw new ApplicationException(ApplicationException.NULL_DAO);
        }
        try {
            CustomerStoredProcedure proc = new CustomerStoredProcedure(
                    getJdbcTemplate().getDataSource(), CUSTOMER_SPROC);
            Map results = proc.execute(accountNumber);
            List customers = (List) results.get("rs");
            if (CollectionUtils.isNotEmpty(customers)) {
                // return the first one
                return (Customer)customers.get(0);
            }
            return null;
        } catch (DataAccessException ex) {
            throw new ApplicationException(ApplicationException.DATA_ACCESS_ERROR);
        }
    }
 
    /**
    * CustomerMapper processes a ResultSet to populate a Customer object
    * @author stevi.deter@gmail.com
    * @version     %I%, %G%
    * @since       1.0
    *
     */
    protected static final class CustomerMapper implements RowMapper {
 
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
            Customer customer = new Customer();
            customer.setAccountNumber(new Long(rs.getLong("ACCOUNTNUMBER")));
            customer.setLastName(StringUtils.trimToNull(rs.getString("LASTNAME")));
            customer.setFirstName(StringUtils.trimToNull(rs.getString("FIRSTNAME")));
            return customer;
        }
    }
    /**
     * Spring DI for the datasource. instantiates the JdbcTemplate
     * @param dataSource
     */
    public void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }
    /**
     * @return the jdbcTemplate
     */
    public JdbcTemplate getJdbcTemplate() {
        return jdbcTemplate;
    }
 
}

No related posts.

Related posts brought to you by Yet Another Related Posts Plugin.

tag_icon

You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

10 Responses to “Spring, JDBCTemplate, and Stored Procedures, Part II”.

  1. Mahesh :

    Article worth reading

  2. Nataraj :

    Hi,

    Shouldnt the method
    public void setDataSource(DataSource dataSource) {
    this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    be renamed as -

    public void setJdbcTemplate(DataSource dataSource) {
    this.jdbcTemplate = new JdbcTemplate(dataSource);
    } ?
    Regards.

  3. Nataraj,
    I think that it’s appropriate that the method is named setDataSource. Using Spring’s JdbcTemplate is an implementation detail, whereas the DataSource is fundamental to how a Data Access Object works.

  4. Shefeek :

    Hi Steve,

    I tried with SQLSErver it works . But no luck with Oracle yet. How we will get the resultset ?
    It always shows ‘ The bad sql grammer error’

    Guide me please !!!

    Regards,
    shef

  5. Cooper :

    Very helpful post.

    Why didn’t you just configure the datasource and inject it into the JdbcTemplate declaratively?

  6. Edwin :

    Very helpful post.
    However could you please post your Oracle PL/SQL procedure code snippet. Especially the first part, where the procedure is declared (in/out parameters) and so on.
    Thanks

  7. Chris V :

    Do you need to close the datasource?

  8. Chris,
    No, you do not need to explicitly open and close the datasource. Spring is managing the database connections for you.

  9. sushant :

    Hi, I’m bit confused with the calling pattern of Stored Procedure using jdbcTemplate.
    Do i need to add a new class everytime a new SP is to be called?

    I WANT to call a SP with some IN and OUT parameters and want to store the result in some DTO object and want to do this using spring in such a manner
    that many functions can be listed in a sinlge class some XXXDAO.java

    So that it can be called from a bllImp class with some input arguments and gets a class object DTO/String/List/Result set in return.

    Please give a running example or provide a .war with the example code or any link where i can clear my doubts.

    Thanks in Advance.

  10. Kishan :

    I have a Oracle function, which returns CURSOR. I tried the same way you have given. But its not working with Functions. Please help me out.

Leave a comment.

To leave a comment, please fill in the fields below.