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}