001/*
002 * Copyright 2002-2017 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.InputStream;
020import java.io.Reader;
021import java.sql.PreparedStatement;
022import java.sql.SQLException;
023import java.sql.Types;
024
025import org.springframework.jdbc.core.DisposableSqlTypeValue;
026import org.springframework.jdbc.support.lob.DefaultLobHandler;
027import org.springframework.jdbc.support.lob.LobCreator;
028import org.springframework.jdbc.support.lob.LobHandler;
029import org.springframework.lang.Nullable;
030
031/**
032 * Object to represent an SQL BLOB/CLOB value parameter. BLOBs can either be an
033 * InputStream or a byte array. CLOBs can be in the form of a Reader, InputStream
034 * or String. Each CLOB/BLOB value will be stored together with its length.
035 * The type is based on which constructor is used. Objects of this class are
036 * immutable except for the LobCreator reference. Use them and discard them.
037 *
038 * <p>This class holds a reference to a LocCreator that must be closed after the
039 * update has completed. This is done via a call to the closeLobCreator method.
040 * All handling of the LobCreator is done by the framework classes that use it -
041 * no need to set or close the LobCreator for end users of this class.
042 *
043 * <p>A usage example:
044 *
045 * <pre class="code">JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);  // reusable object
046 * LobHandler lobHandler = new DefaultLobHandler();  // reusable object
047 *
048 * jdbcTemplate.update(
049 *     "INSERT INTO imagedb (image_name, content, description) VALUES (?, ?, ?)",
050 *     new Object[] {
051 *       name,
052 *       new SqlLobValue(contentStream, contentLength, lobHandler),
053 *       new SqlLobValue(description, lobHandler)
054 *     },
055 *     new int[] {Types.VARCHAR, Types.BLOB, Types.CLOB});
056 * </pre>
057 *
058 * @author Thomas Risberg
059 * @author Juergen Hoeller
060 * @since 1.1
061 * @see org.springframework.jdbc.support.lob.LobHandler
062 * @see org.springframework.jdbc.support.lob.LobCreator
063 * @see org.springframework.jdbc.core.JdbcTemplate#update(String, Object[], int[])
064 * @see org.springframework.jdbc.object.SqlUpdate#update(Object[])
065 * @see org.springframework.jdbc.object.StoredProcedure#execute(java.util.Map)
066 */
067public class SqlLobValue implements DisposableSqlTypeValue {
068
069        @Nullable
070        private final Object content;
071
072        private final int length;
073
074        /**
075         * This contains a reference to the LobCreator - so we can close it
076         * once the update is done.
077         */
078        private final LobCreator lobCreator;
079
080
081        /**
082         * Create a new BLOB value with the given byte array,
083         * using a DefaultLobHandler.
084         * @param bytes the byte array containing the BLOB value
085         * @see org.springframework.jdbc.support.lob.DefaultLobHandler
086         */
087        public SqlLobValue(@Nullable byte[] bytes) {
088                this(bytes, new DefaultLobHandler());
089        }
090
091        /**
092         * Create a new BLOB value with the given byte array.
093         * @param bytes the byte array containing the BLOB value
094         * @param lobHandler the LobHandler to be used
095         */
096        public SqlLobValue(@Nullable byte[] bytes, LobHandler lobHandler) {
097                this.content = bytes;
098                this.length = (bytes != null ? bytes.length : 0);
099                this.lobCreator = lobHandler.getLobCreator();
100        }
101
102        /**
103         * Create a new CLOB value with the given content string,
104         * using a DefaultLobHandler.
105         * @param content the String containing the CLOB value
106         * @see org.springframework.jdbc.support.lob.DefaultLobHandler
107         */
108        public SqlLobValue(@Nullable String content) {
109                this(content, new DefaultLobHandler());
110        }
111
112        /**
113         * Create a new CLOB value with the given content string.
114         * @param content the String containing the CLOB value
115         * @param lobHandler the LobHandler to be used
116         */
117        public SqlLobValue(@Nullable String content, LobHandler lobHandler) {
118                this.content = content;
119                this.length = (content != null ? content.length() : 0);
120                this.lobCreator = lobHandler.getLobCreator();
121        }
122
123        /**
124         * Create a new BLOB/CLOB value with the given stream,
125         * using a DefaultLobHandler.
126         * @param stream the stream containing the LOB value
127         * @param length the length of the LOB value
128         * @see org.springframework.jdbc.support.lob.DefaultLobHandler
129         */
130        public SqlLobValue(InputStream stream, int length) {
131                this(stream, length, new DefaultLobHandler());
132        }
133
134        /**
135         * Create a new BLOB/CLOB value with the given stream.
136         * @param stream the stream containing the LOB value
137         * @param length the length of the LOB value
138         * @param lobHandler the LobHandler to be used
139         */
140        public SqlLobValue(InputStream stream, int length, LobHandler lobHandler) {
141                this.content = stream;
142                this.length = length;
143                this.lobCreator = lobHandler.getLobCreator();
144        }
145
146        /**
147         * Create a new CLOB value with the given character stream,
148         * using a DefaultLobHandler.
149         * @param reader the character stream containing the CLOB value
150         * @param length the length of the CLOB value
151         * @see org.springframework.jdbc.support.lob.DefaultLobHandler
152         */
153        public SqlLobValue(Reader reader, int length) {
154                this(reader, length, new DefaultLobHandler());
155        }
156
157        /**
158         * Create a new CLOB value with the given character stream.
159         * @param reader the character stream containing the CLOB value
160         * @param length the length of the CLOB value
161         * @param lobHandler the LobHandler to be used
162         */
163        public SqlLobValue(Reader reader, int length, LobHandler lobHandler) {
164                this.content = reader;
165                this.length = length;
166                this.lobCreator = lobHandler.getLobCreator();
167        }
168
169
170        /**
171         * Set the specified content via the LobCreator.
172         */
173        @Override
174        public void setTypeValue(PreparedStatement ps, int paramIndex, int sqlType, @Nullable String typeName)
175                        throws SQLException {
176
177                if (sqlType == Types.BLOB) {
178                        if (this.content instanceof byte[] || this.content == null) {
179                                this.lobCreator.setBlobAsBytes(ps, paramIndex, (byte[]) this.content);
180                        }
181                        else if (this.content instanceof String) {
182                                this.lobCreator.setBlobAsBytes(ps, paramIndex, ((String) this.content).getBytes());
183                        }
184                        else if (this.content instanceof InputStream) {
185                                this.lobCreator.setBlobAsBinaryStream(ps, paramIndex, (InputStream) this.content, this.length);
186                        }
187                        else {
188                                throw new IllegalArgumentException(
189                                                "Content type [" + this.content.getClass().getName() + "] not supported for BLOB columns");
190                        }
191                }
192                else if (sqlType == Types.CLOB) {
193                        if (this.content instanceof String || this.content == null) {
194                                this.lobCreator.setClobAsString(ps, paramIndex, (String) this.content);
195                        }
196                        else if (this.content instanceof InputStream) {
197                                this.lobCreator.setClobAsAsciiStream(ps, paramIndex, (InputStream) this.content, this.length);
198                        }
199                        else if (this.content instanceof Reader) {
200                                this.lobCreator.setClobAsCharacterStream(ps, paramIndex, (Reader) this.content, this.length);
201                        }
202                        else {
203                                throw new IllegalArgumentException(
204                                                "Content type [" + this.content.getClass().getName() + "] not supported for CLOB columns");
205                        }
206                }
207                else {
208                        throw new IllegalArgumentException("SqlLobValue only supports SQL types BLOB and CLOB");
209                }
210        }
211
212        /**
213         * Close the LobCreator, if any.
214         */
215        @Override
216        public void cleanup() {
217                this.lobCreator.close();
218        }
219
220}