001package com.randomnoun.common.spring;
002
003/* (c) 2013 randomnoun. All Rights Reserved. This work is licensed under a
004 * BSD Simplified License. (http://www.randomnoun.com/bsd-simplified.html)
005 */
006
007import java.io.*;
008import java.sql.*;
009
010import org.springframework.dao.TypeMismatchDataAccessException;
011import org.springframework.jdbc.core.*;
012
013import com.randomnoun.common.StreamUtil;
014
015
016
017/**
018 * This class is intended to replace the standard RowMapper provided by Spring
019 * to return a List of String objects (rather than a List of Maps). The
020 * queries executed by this rowMapper must therefore always evaluate to
021 * a single String or CLOB column (the CLOB will be mapped to a String
022 * if required).
023 *
024 * <p>This class can be used as in the following code fragment:
025 *
026 * <pre style="code">
027                JdbcTemplate jt = EjbConfig.getEjbConfig().getJdbcTemplate();
028                List stringList = jt.query(
029                    "SELECT DISTINCT roleName FROM userRole " +
030                    "WHERE customerId = ? ",
031                    new Object[] { new Long(customerId) },
032                    new RowMapperResultReader(new StringRowMapper() ));
033 * </pre>
034 *
035 *
036 * 
037 * @author knoxg
038 */
039public class StringRowMapper implements RowMapper<String>
040{
041
042    /**
043     * Returns a single object representing this row.
044     *
045     * @param resultSet The resultset to map
046     * @param rowNumber The current row number
047     *
048     * @return a single object representing this row.
049     *
050     * @throws TypeMismatchDataAccessException if the first column of the restset is not
051     *   a String or CLOB.
052     */
053    public String mapRow(ResultSet resultSet, int rowNumber)
054        throws SQLException
055    {
056        Object value;
057
058        value = resultSet.getObject(1);
059
060        if (value == null) {
061            // just keep it as a null
062        } else if (value.getClass().getName().equals("oracle.sql.CLOB")) {
063                // need to do text comparison rather than instanceof to get around classloading issues
064            // value = lobHandler.getClobAsString(resultSet, 1);
065                throw new UnsupportedOperationException("Unexpected CLOB");
066        } else if (value instanceof java.sql.Clob) {
067                        Clob clob = (Clob) value;
068                        ByteArrayOutputStream baos = new ByteArrayOutputStream();
069                        try {
070                                StreamUtil.copyStream(clob.getAsciiStream(), baos, 1024);
071                        } catch (IOException ioe) {
072                                throw (SQLException) new SQLException("IO error transferring CLOB").initCause(ioe);
073                        }
074                        value = baos.toString();
075        } else if (!(value instanceof String)) {
076            throw new TypeMismatchDataAccessException(
077                "Expecting String/CLOB in column 1 of query (found " +
078                value.getClass().getName() + ")");
079        }
080
081        return (String)value;
082    }
083}