001/*
002 * Copyright 2002-2019 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;
028import org.springframework.lang.Nullable;
029
030/**
031 * Abstract ResultSetExtractor implementation that assumes streaming of LOB data.
032 * Typically used as inner class, with access to surrounding method arguments.
033 *
034 * <p>Delegates to the {@code streamData} template method for streaming LOB
035 * content to some OutputStream, typically using a LobHandler. Converts an
036 * IOException thrown during streaming to a LobRetrievalFailureException.
037 *
038 * <p>A usage example with JdbcTemplate:
039 *
040 * <pre class="code">JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);  // reusable object
041 * final LobHandler lobHandler = new DefaultLobHandler();  // reusable object
042 *
043 * jdbcTemplate.query(
044 *               "SELECT content FROM imagedb WHERE image_name=?", new Object[] {name},
045 *               new AbstractLobStreamingResultSetExtractor() {
046 *                       public void streamData(ResultSet rs) throws SQLException, IOException {
047 *                               FileCopyUtils.copy(lobHandler.getBlobAsBinaryStream(rs, 1), contentStream);
048 *             }
049 *         }
050 * );</pre>
051 *
052 * @author Juergen Hoeller
053 * @since 1.0.2
054 * @param <T> the result type
055 * @see org.springframework.jdbc.support.lob.LobHandler
056 * @see org.springframework.jdbc.LobRetrievalFailureException
057 */
058public abstract class AbstractLobStreamingResultSetExtractor<T> implements ResultSetExtractor<T> {
059
060        /**
061         * Delegates to handleNoRowFound, handleMultipleRowsFound and streamData,
062         * according to the ResultSet state. Converts an IOException thrown by
063         * streamData to a LobRetrievalFailureException.
064         * @see #handleNoRowFound
065         * @see #handleMultipleRowsFound
066         * @see #streamData
067         * @see org.springframework.jdbc.LobRetrievalFailureException
068         */
069        @Override
070        @Nullable
071        public final T extractData(ResultSet rs) throws SQLException, DataAccessException {
072                if (!rs.next()) {
073                        handleNoRowFound();
074                }
075                else {
076                        try {
077                                streamData(rs);
078                                if (rs.next()) {
079                                        handleMultipleRowsFound();
080                                }
081                        }
082                        catch (IOException ex) {
083                                throw new LobRetrievalFailureException("Could not stream LOB content", ex);
084                        }
085                }
086                return null;
087        }
088
089        /**
090         * Handle the case where the ResultSet does not contain a row.
091         * @throws DataAccessException a corresponding exception,
092         * by default an EmptyResultDataAccessException
093         * @see org.springframework.dao.EmptyResultDataAccessException
094         */
095        protected void handleNoRowFound() throws DataAccessException {
096                throw new EmptyResultDataAccessException(
097                                "LobStreamingResultSetExtractor did not find row in database", 1);
098        }
099
100        /**
101         * Handle the case where the ResultSet contains multiple rows.
102         * @throws DataAccessException a corresponding exception,
103         * by default an IncorrectResultSizeDataAccessException
104         * @see org.springframework.dao.IncorrectResultSizeDataAccessException
105         */
106        protected void handleMultipleRowsFound() throws DataAccessException {
107                throw new IncorrectResultSizeDataAccessException(
108                                "LobStreamingResultSetExtractor found multiple rows in database", 1);
109        }
110
111        /**
112         * Stream LOB content from the given ResultSet to some OutputStream.
113         * <p>Typically used as inner class, with access to surrounding method arguments
114         * and to a LobHandler instance variable of the surrounding class.
115         * @param rs the ResultSet to take the LOB content from
116         * @throws SQLException if thrown by JDBC methods
117         * @throws IOException if thrown by stream access methods
118         * @throws DataAccessException in case of custom exceptions
119         * @see org.springframework.jdbc.support.lob.LobHandler#getBlobAsBinaryStream
120         * @see org.springframework.util.FileCopyUtils
121         */
122        protected abstract void streamData(ResultSet rs) throws SQLException, IOException, DataAccessException;
123
124}