Results 1 to 5 of 5

Thread: Reusable ResultSetExtractor available?

  1. #1
    Join Date
    Dec 2004
    Posts
    3

    Default Reusable ResultSetExtractor available?

    Is there a single, reusable implementation of ResultSetExtractor?
    I'd like to see something like:
    Code:
    Object[] sqlBindParameters = { userId };
    JdbcTemplate jdbcTemplate = new JdbcTemplate(this.getDataSource());
    List users = (List) jdbcTemplate.query(
       "select userId, firstName, lastName from users where userId = ?",
       sqlBindParameters,
       new REUSABLERESULTSETEXTRACTOR( User.class ) );
    where the REUSABLERESULTSETEXTRACTOR class converts the ResultSet into populated instances of the target class.
    This would eliminate the need to write in-line implementations of ResultSetExtractor much of the time.

    Thanks!

  2. #2
    Join Date
    Aug 2004
    Location
    Montréal, Canada
    Posts
    845

    Default

    kenfitz,

    please take a look at org.springframework.jdbc.core.ReflectionRowExtract or in SpringFramework sandbox.
    HTH
    Omar Irbouh

    Spring Modules Team
    http://irbouh.blogspot.com/

  3. #3
    Join Date
    Dec 2004
    Posts
    3

    Default

    irbouho,

    Thank you for the link. That's very close to exactly what I'm looking for.
    It looks as though it is still in the SandBox, though ... any idea when that code might be Gold? (I saw the notes about no documentation and not thoroughly tested)

    The code:
    // HACK!!!!
    colname = colname.toLowerCase();
    is a somewhat disconcerting. I had a similar discussion with Craig McClanahan about the Jakarta Commons BeanUtils ResultSetDynaClass (http://jakarta.apache.org/commons/be...DynaClass.html) which serves a similar purpose. The issue at that time was that forcing the column name to lower-case would seem to violate the Java Beans Property Naming standard, but more importantly it also would reduce the chance of populating a given Java Bean target property, based on reflection (e.g., Column Name "firstname" is not the ideal candidate to populate the Java Bean property named "firstName".) Craig ended-up adding a boolean parameter to the constructor to control the dropping of the column names to lower-case.

    Working with ResultSetDynaClass has been great since working through that final issue.

    Would there be a way for ReflectionRowExtractor to provide that same option?

    Thanks!

  4. #4
    Join Date
    Dec 2004
    Posts
    10

    Default

    I would very much appreciate this functionality. I agree that the lowercasing of the column name would hurt the usability. I would rather suggest that this class accepts a callback that implements a name transformation method.
    Tipically, colum names in rdbms are case insensitive an composite names are expressed using underscores (for instance "MAX_SALARY"; a useful implementation of the callback would transform such a colum name in a Java identifier using camel notation (for instance "maxSalary"). Other implementations could enforce special naming conventions, etc.

    Thanks in advance for the attention and best regards, Fabio.

  5. #5
    Join Date
    Dec 2004
    Posts
    3

    Default JavaBeanResultSetExtractor - code

    (KenFitz)
    This code uses Apache's BeanUtils because Spring's equivalent
    isn't sufficiently forgiving ...
    Also uses Apache's Logging & Lang for the same reason.
    Code:
    /*
     * JavaBeanResultSetExtractor
     * Created on Dec 1, 2004
     * 
     * Copyright 2002-2004 the original author or authors.
     * 
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     * 
     *      http://www.apache.org/licenses/LICENSE-2.0
     * 
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package com.kenfitz.springframework.jdbc;
    
    import org.apache.commons.beanutils.BeanUtils;
    import org.apache.commons.beanutils.DynaBean;
    import org.apache.commons.beanutils.ResultSetDynaClass;
    import org.apache.commons.lang.builder.ReflectionToStringBuilder;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    // TODO Spring's BeanUtils.copyProperties is not sufficiently forgiving.  must use Apache's
    // import org.springframework.beans.BeanUtils;
    import org.springframework.dao.DataAccessException;
    import org.springframework.jdbc.core.ResultSetExtractor;
    import org.springframework.util.ClassUtils;
    
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    
    /**
     * JavaBeanResultSetExtractor
     * implements Spring's ResultSetExtractor interface
     * in a reusable manner, 
     * providing a single solution for 
     * processing a ResultSet and returning populated Java Bean instances.
     * JavaBeanResultSetExtractor is typically used in conjunction with Spring's JdbcTemplate,
     * although it can be used in any 
     * Spring context where an implementation of ResultSetExtractor is used.
      * </p>
     * <p>
     * Use of JavaBeanResultSetExtractor is a single alternative to 
     * repeatedly coding in-line anonymous classes that
     * also implement the ResultSetExtractor interface.
     * </p>
     * <p>
     * An example of using this class is&#58;
     * <pre>
     * <code> 
     * 		Object&#91;&#93; sqlBindParameters = &#123; userId &#125;;
    		JdbcTemplate jdbcTemplate = new JdbcTemplate&#40;this.getDataSource&#40;&#41;&#41;;
    		List users = &#40;List&#41; jdbcTemplate.query&#40;
    			"select userId, firstName, lastName from users where userId = ?",
    			sqlBindParameters,
    			<b>new JavaBeanResultSetExtractor&#40; User.class &#41;</b> &#41;;
     * </code> 
     * </pre>
     * Normally, an in-line class implementation of 
     * the ResultSetExtractor interface
     * would appear where 
     * <code>
     * new JavaBeanResultSetExtractor&#40; User.class &#41;
     * </code>
     * appears above.
     * <p>
     * </p>
     * Note that the Column Names of the SQL Select clause
     * must follow JavaBean property naming conventions 
     * for the successful population of the target Java Bean properties.
     * This may prompt the need for establishing Column Name Aliases,
     * if the Column Names don't naturally meet this requirement.
     * <br/>  
     * For example&#58;
     * <pre>
     * <code> 
     * 		"select USER_ID <b>as userId</b>, FIRST_NAME <b>as firstName</b>, LAST_NAME <b>as lastName</b> from users " +
     * 			"where USER_ID = ?"
     * </code> 
     * </pre>
     * The JavaBeanResultSetExtractor constructor
     * <code> 
     * 		JavaBeanResultSetExtractor&#40;Class&#41;
     * </code> 
     * can be used to create the JavaBeanResultSetExtractor instance
     * while simultaneously establishing the Class of the target Java Bean.
     * The default constructor also can be used, 
     * but it is necessary to have established the  
     * Class of the target Java Bean &#40;through the 
     * <code> 
     * 		setBeanClass&#40;Class&#41;
     * </code> 
     * method prior to executing the
     * <code> 
     * 		extractData&#40;ResultSet&#41;
     * </code> 
     * method.  
     * An 
     * <code> 
     * 		IllegalStateException
     * </code> 
     * is thrown if the 
     * <code> 
     * 		beanClass
     * </code> 
     * property remains unspecified at the time that the
     * <code> 
     * 		extractData&#40;ResultSet&#41;
     * </code> 
     * method is executed. 
     * <p>
     * The returned value is always a 
     * <code> 
     * 		List,
     * </code> 
     * which may be empty. 
     * </p> 
     * <p>
     * JavaBeanResultSetExtractor has a dependency on the
     * Apache/Jakarta/Commons/BeanUtils component.
     * As such, the beanutils.jar is required.
     * </p> 
     * 
     * @author Ken Fitzpatrick, Boerne, Texas, USA
     * @see <a href="http&#58;//www.springframework.org/docs/api/org/springframework/jdbc/core/JdbcTemplate.html" >JdbcTemplate</a>
     * @see <a href="http&#58;//www.springframework.org/docs/api/org/springframework/jdbc/core/ResultSetExtractor.html" >ResultSetExtractor</a>
     * @see <a href="http&#58;//jakarta.apache.org/commons/beanutils/apidocs/org/apache/commons/beanutils/DynaBean.html" >Apache/Jakarta/Commons/BeanUtils DynaBean</a>
     * @see <a href="hhttp&#58;//jakarta.apache.org/commons/beanutils/apidocs/org/apache/commons/beanutils/ResultSetDynaClass.html" >Apache/Jakarta/Commons/BeanUtils ResultSetDynaClass</a>
     * @see <a href="http&#58;//jakarta.apache.org/commons/beanutils/">Apache/Jakarta/Commons/BeanUtils</a>
     */
    
    public class JavaBeanResultSetExtractor implements ResultSetExtractor
    &#123;
    
    	private Class beanClass;
    
    	private List beans = new ArrayList&#40;&#41;;
    
    	private static Log log = LogFactory.getLog&#40; JavaBeanResultSetExtractor.class &#41;;
    	private static String PACKAGE_NAME = ClassUtils.getShortName&#40; JavaBeanResultSetExtractor.class &#41;;
    	private static String CLASS_NAME = ClassUtils.getShortName&#40; JavaBeanResultSetExtractor.class &#41;;
    
    	/**
    	 * Constructs a new instance of
    	 * <code>JavaBeanResultSetExtractor</code>.
    	 * Since no
    	 * <code>beanClass</code>
    	 * is provided to this constructor,
    	 * the user must execute the 
    	 * <code>setBeanClass&#40;Class&#41;</code> 
    	 * method prior to using this instance
    	 * against a ResultSet.
    	 */
    	public JavaBeanResultSetExtractor&#40;&#41;
    	&#123;
    		super&#40;&#41;;
    	&#125;
    	
    	/**
    	 * Constructs a new instance of
    	 * <code>JavaBeanResultSetExtractor</code>,
    	 * setting the beanClass property to the value found in
    	 * <code>beanClass</code>.
    	 * @param beanClass Class of the target bean to be created, populated and returned
    	 * while processing the ResultSet.
    	 */
    	public JavaBeanResultSetExtractor&#40; Class beanClass &#41;
    	&#123;
    		this&#40;&#41;;
    		this.setBeanClass&#40;beanClass&#41;;
    	&#125;
    	
    	/**
    	 * Returns a <code>List</code> containing instances of the 
    	 * target Java Bean Class defined in the 
    	 * <code>beanClass</code> property, 
    	 * where those beans have been populated from the 
    	 * contents of 
    	 * <code>resultSet</code>,
    	 * using normal Java Bean Property Naming conventions.
    	 * <p>
    	 * An 
    	 * <code> 
    	 * 		IllegalStateException
    	 * </code> 
    	 * is thrown if the 
    	 * <code> 
    	 * 		beanClass
    	 * </code> 
    	 * property remains unspecified at the time that this method is executed. 
    	 * </p>
    	 * @param resultSet ResultSet that has not yet been processed or moved from its origin.
    	 * @return List of populated instances of the target class.  May be an empty list.
    	 * @see org.springframework.jdbc.core.ResultSetExtractor#extractData&#40;java.sql.ResultSet&#41;
    	 */
    	public Object extractData&#40; ResultSet resultSet &#41;
    		throws SQLException, DataAccessException
    	&#123;
    		final String METHOD_NAME = CLASS_NAME + ".extractData&#58; ";
    
    		// beanClass must be defined
    		if &#40; this.getBeanClass&#40;&#41; == null &#41;
    			throw new IllegalStateException&#40; METHOD_NAME +
    				"Property 'beanClass' must not be null when this method is executed. " +
    				"Use the JavaBeanResultSetExtractor&#40;Class&#41; constructor or " +
    				"subsequently execute the setBeanClass&#40;Class&#41; method before executing this method." &#41;;
    		
    		if &#40; log.isDebugEnabled&#40;&#41; &#41;
    			log.debug&#40;METHOD_NAME +
    				"Using beanClass=" + this.getBeanClass&#40;&#41; +
    				""&#41;;
    		
    		// create a ResultSetDynaClass from resultSet
    		ResultSetDynaClass rsdc = new ResultSetDynaClass&#40;resultSet,false&#41;;
    		Iterator rows = rsdc.iterator&#40;&#41;;
    		List beans = this.getBeans&#40;&#41;;
    		int successfullyProcessedRows = 0;
    
    		// process resultSet
    		while &#40;rows.hasNext&#40;&#41;&#41;  
    		&#123;
    			Object bean = null;
    			DynaBean rowDynaBean = null;
    
    			try
    			&#123;
    				// create instance of target class
    				bean = this.getBeanClass&#40;&#41;.newInstance&#40;&#41;;
    				// get next row, wrapped as a DynaBean
    				rowDynaBean = &#40;DynaBean&#41; rows.next&#40;&#41;;
    				// copy like-named properties from the DynaBean to the target bean
    				BeanUtils.copyProperties&#40; bean, rowDynaBean &#41;;
    					// accumulate beans
    				beans.add&#40;bean&#41;;
    				successfullyProcessedRows++;
    			&#125;
    			catch &#40; Exception e &#41;
    			&#123;
    				String message = METHOD_NAME + "Error occurred while attempting to " +
    					" copy row values into new instance of " + 
    					"beanClass=" + this.getBeanClass&#40;&#41; +
    					". At the time of the error" +
    					", bean=" + &#40; bean == null ? "null" &#58; ReflectionToStringBuilder.toString&#40;bean&#41; &#41; +
    					", rowDynaBean=" + &#40; rowDynaBean == null ? "null" &#58; ReflectionToStringBuilder.toString&#40;rowDynaBean&#41; &#41; +
    					", error=" + e + "";
    				log.error&#40; message, e &#41;;
    			&#125;
    		&#125;
    		
    		if &#40; log.isDebugEnabled&#40;&#41; &#41;
    			log.debug&#40;METHOD_NAME +
    				"Successfully processed " + successfullyProcessedRows + " rows" +
    				", returning beans.size=" + beans.size&#40;&#41; +
    				" of class=" + this.getBeanClass&#40;&#41; +
    				""&#41;;
    
    		return beans;
    	&#125;
    
    	/**
    	 * Returns the 
    	 * <code>beanClass</code> 
    	 * property.
    	 * @return Class
    	 */
    	public Class getBeanClass&#40;&#41;
    	&#123;
    		return this.beanClass;
    	&#125;
    	/**
    	 * Sets the 
    	 * <code>beanClass</code>
    	 * property to the value found in
    	 * <code>beanClass</code>.
    	 * @param beanClass Class to be used to set the beanClass property.
    	 */
    	public void setBeanClass&#40; Class beanClass &#41;
    	&#123;
    		this.beanClass = beanClass;
    	&#125;
    	/**
    	 * Returns the 
    	 * <code>beans</code> 
    	 * property.
    	 * @return List
    	 */
    	public List getBeans&#40;&#41;
    	&#123;
    		return this.beans;
    	&#125;
    	/**
    	 * Sets the 
    	 * <code>beans</code>
    	 * property to the value found in
    	 * <code>beans</code>.
    	 * @param beans List to be used to set the beans property.
    	 */
    	public void setBeans&#40; List beans &#41;
    	&#123;
    		this.beans = beans;
    	&#125;
    &#125;

Similar Threads

  1. Reusable 'Module' configuration files?
    By zanfolim in forum Architecture
    Replies: 6
    Last Post: Sep 28th, 2006, 06:52 AM
  2. Reusable component architecture best practices?
    By rebornspirit in forum Architecture
    Replies: 3
    Last Post: Oct 18th, 2005, 07:57 AM
  3. Reusable String[] object in Application Context
    By hanson.char in forum Container
    Replies: 3
    Last Post: Jun 22nd, 2005, 07:39 PM
  4. Replies: 1
    Last Post: Jan 13th, 2005, 02:48 PM
  5. Reusable Input Fields with Bind Tag
    By j2eeguru in forum Web
    Replies: 1
    Last Post: Nov 13th, 2004, 06:01 PM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •