001/*
002 * Copyright 2002-2012 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.jdbc.core.support;
018
019import java.io.IOException;
020import java.sql.ResultSet;
021import java.sql.SQLException;
022
023import org.springframework.dao.DataAccessException;
024import org.springframework.dao.EmptyResultDataAccessException;
025import org.springframework.dao.IncorrectResultSizeDataAccessException;
026import org.springframework.jdbc.LobRetrievalFailureException;
027import org.springframework.jdbc.core.ResultSetExtractor;
028
029/**
030 * Abstract ResultSetExtractor implementation that assumes streaming of LOB data.
031 * Typically used as inner class, with access to surrounding method arguments.
032 *
033 * <p>Delegates to the {@code streamData} template method for streaming LOB
034 * content to some OutputStream, typically using a LobHandler. Converts an
035 * IOException thrown during streaming to a LobRetrievalFailureException.
036 *
037 * <p>A usage example with JdbcTemplate:
038 *
039 * <pre class="code">JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);  // reusable object
040 * final LobHandler lobHandler = new DefaultLobHandler();  // reusable object
041 *
042 * jdbcTemplate.query(
043 *               "SELECT content FROM imagedb WHERE image_name=?", new Object[] {name},
044 *               new AbstractLobStreamingResultSetExtractor() {
045 *                       public void streamData(ResultSet rs) throws SQLException, IOException {
046 *                               FileCopyUtils.copy(lobHandler.getBlobAsBinaryStream(rs, 1), contentStream);
047 *             }
048 *         }
049 * );</pre>
050 *
051 * @author Juergen Hoeller
052 * @since 1.0.2
053 * @see org.springframework.jdbc.support.lob.LobHandler
054 * @see org.springframework.jdbc.LobRetrievalFailureException
055 */
056public abstract class AbstractLobStreamingResultSetExtractor<T> implements ResultSetExtractor<T> {
057
058        /**
059         * Delegates to handleNoRowFound, handleMultipleRowsFound and streamData,
060         * according to the ResultSet state. Converts an IOException thrown by
061         * streamData to a LobRetrievalFailureException.
062         * @see #handleNoRowFound
063         * @see #handleMultipleRowsFound
064         * @see #streamData
065         * @see org.springframework.jdbc.LobRetrievalFailureException
066         */
067        @Override
068        public final T extractData(ResultSet rs) throws SQLException, DataAccessException {
069                if (!rs.next()) {
070                        handleNoRowFound();
071                }
072                else {
073                        try {
074                                streamData(rs);
075                                if (rs.next()) {
076                                        handleMultipleRowsFound();
077                                }
078                        }
079                        catch (IOException ex) {
080                                throw new LobRetrievalFailureException("Couldn't stream LOB content", ex);
081                        }
082                }
083                return null;
084        }
085
086        /**
087         * Handle the case where the ResultSet does not contain a row.
088         * @throws DataAccessException a corresponding exception,
089         * by default an EmptyResultDataAccessException
090         * @see org.springframework.dao.EmptyResultDataAccessException
091         */
092        protected void handleNoRowFound() throws DataAccessException {
093                throw new EmptyResultDataAccessException(
094                                "LobStreamingResultSetExtractor did not find row in database", 1);
095        }
096
097        /**
098         * Handle the case where the ResultSet contains multiple rows.
099         * @throws DataAccessException a corresponding exception,
100         * by default an IncorrectResultSizeDataAccessException
101         * @see org.springframework.dao.IncorrectResultSizeDataAccessException
102         */
103        protected void handleMultipleRowsFound() throws DataAccessException {
104                throw new IncorrectResultSizeDataAccessException(
105                                "LobStreamingResultSetExtractor found multiple rows in database", 1);
106        }
107
108        /**
109         * Stream LOB content from the given ResultSet to some OutputStream.
110         * <p>Typically used as inner class, with access to surrounding method arguments
111         * and to a LobHandler instance variable of the surrounding class.
112         * @param rs the ResultSet to take the LOB content from
113         * @throws SQLException if thrown by JDBC methods
114         * @throws IOException if thrown by stream access methods
115         * @throws DataAccessException in case of custom exceptions
116         * @see org.springframework.jdbc.support.lob.LobHandler#getBlobAsBinaryStream
117         * @see org.springframework.util.FileCopyUtils
118         */
119        protected abstract void streamData(ResultSet rs) throws SQLException, IOException, DataAccessException;
120
121}