001/*
002 * Copyright 2002-2018 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.support.lob;
018
019import java.io.IOException;
020import java.io.InputStream;
021import java.io.Reader;
022import java.sql.Blob;
023import java.sql.Clob;
024import java.sql.PreparedStatement;
025import java.sql.SQLException;
026import java.util.LinkedHashSet;
027import java.util.Set;
028
029import org.apache.commons.logging.Log;
030import org.apache.commons.logging.LogFactory;
031
032import org.springframework.dao.DataAccessResourceFailureException;
033import org.springframework.lang.Nullable;
034import org.springframework.util.FileCopyUtils;
035
036/**
037 * {@link LobCreator} implementation based on temporary LOBs,
038 * using JDBC 4.0's {@link java.sql.Connection#createBlob()} /
039 * {@link java.sql.Connection#createClob()} mechanism.
040 *
041 * <p>Used by DefaultLobHandler's {@link DefaultLobHandler#setCreateTemporaryLob} mode.
042 * Can also be used directly to reuse the tracking and freeing of temporary LOBs.
043 *
044 * @author Juergen Hoeller
045 * @since 3.2.2
046 * @see DefaultLobHandler#setCreateTemporaryLob
047 * @see java.sql.Connection#createBlob()
048 * @see java.sql.Connection#createClob()
049 */
050public class TemporaryLobCreator implements LobCreator {
051
052        protected static final Log logger = LogFactory.getLog(TemporaryLobCreator.class);
053
054        private final Set<Blob> temporaryBlobs = new LinkedHashSet<>(1);
055
056        private final Set<Clob> temporaryClobs = new LinkedHashSet<>(1);
057
058
059        @Override
060        public void setBlobAsBytes(PreparedStatement ps, int paramIndex, @Nullable byte[] content)
061                        throws SQLException {
062
063                if (content != null) {
064                        Blob blob = ps.getConnection().createBlob();
065                        blob.setBytes(1, content);
066                        this.temporaryBlobs.add(blob);
067                        ps.setBlob(paramIndex, blob);
068                }
069                else {
070                        ps.setBlob(paramIndex, (Blob) null);
071                }
072
073                if (logger.isDebugEnabled()) {
074                        logger.debug(content != null ? "Copied bytes into temporary BLOB with length " + content.length :
075                                        "Set BLOB to null");
076                }
077        }
078
079        @Override
080        public void setBlobAsBinaryStream(
081                        PreparedStatement ps, int paramIndex, @Nullable InputStream binaryStream, int contentLength)
082                        throws SQLException {
083
084                if (binaryStream != null) {
085                        Blob blob = ps.getConnection().createBlob();
086                        try {
087                                FileCopyUtils.copy(binaryStream, blob.setBinaryStream(1));
088                        }
089                        catch (IOException ex) {
090                                throw new DataAccessResourceFailureException("Could not copy into LOB stream", ex);
091                        }
092                        this.temporaryBlobs.add(blob);
093                        ps.setBlob(paramIndex, blob);
094                }
095                else {
096                        ps.setBlob(paramIndex, (Blob) null);
097                }
098
099                if (logger.isDebugEnabled()) {
100                        logger.debug(binaryStream != null ?
101                                        "Copied binary stream into temporary BLOB with length " + contentLength :
102                                        "Set BLOB to null");
103                }
104        }
105
106        @Override
107        public void setClobAsString(PreparedStatement ps, int paramIndex, @Nullable String content)
108                        throws SQLException {
109
110                if (content != null) {
111                        Clob clob = ps.getConnection().createClob();
112                        clob.setString(1, content);
113                        this.temporaryClobs.add(clob);
114                        ps.setClob(paramIndex, clob);
115                }
116                else {
117                        ps.setClob(paramIndex, (Clob) null);
118                }
119
120                if (logger.isDebugEnabled()) {
121                        logger.debug(content != null ? "Copied string into temporary CLOB with length " + content.length() :
122                                        "Set CLOB to null");
123                }
124        }
125
126        @Override
127        public void setClobAsAsciiStream(
128                        PreparedStatement ps, int paramIndex, @Nullable InputStream asciiStream, int contentLength)
129                        throws SQLException {
130
131                if (asciiStream != null) {
132                        Clob clob = ps.getConnection().createClob();
133                        try {
134                                FileCopyUtils.copy(asciiStream, clob.setAsciiStream(1));
135                        }
136                        catch (IOException ex) {
137                                throw new DataAccessResourceFailureException("Could not copy into LOB stream", ex);
138                        }
139                        this.temporaryClobs.add(clob);
140                        ps.setClob(paramIndex, clob);
141                }
142                else {
143                        ps.setClob(paramIndex, (Clob) null);
144                }
145
146                if (logger.isDebugEnabled()) {
147                        logger.debug(asciiStream != null ?
148                                        "Copied ASCII stream into temporary CLOB with length " + contentLength :
149                                        "Set CLOB to null");
150                }
151        }
152
153        @Override
154        public void setClobAsCharacterStream(
155                        PreparedStatement ps, int paramIndex, @Nullable Reader characterStream, int contentLength)
156                        throws SQLException {
157
158                if (characterStream != null) {
159                        Clob clob = ps.getConnection().createClob();
160                        try {
161                                FileCopyUtils.copy(characterStream, clob.setCharacterStream(1));
162                        }
163                        catch (IOException ex) {
164                                throw new DataAccessResourceFailureException("Could not copy into LOB stream", ex);
165                        }
166                        this.temporaryClobs.add(clob);
167                        ps.setClob(paramIndex, clob);
168                }
169                else {
170                        ps.setClob(paramIndex, (Clob) null);
171                }
172
173                if (logger.isDebugEnabled()) {
174                        logger.debug(characterStream != null ?
175                                        "Copied character stream into temporary CLOB with length " + contentLength :
176                                        "Set CLOB to null");
177                }
178        }
179
180        @Override
181        public void close() {
182                for (Blob blob : this.temporaryBlobs) {
183                        try {
184                                blob.free();
185                        }
186                        catch (SQLException ex) {
187                                logger.warn("Could not free BLOB", ex);
188                        }
189                }
190                for (Clob clob : this.temporaryClobs) {
191                        try {
192                                clob.free();
193                        }
194                        catch (SQLException ex) {
195                                logger.warn("Could not free CLOB", ex);
196                        }
197                }
198        }
199
200}