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.support.xml;
018
019import java.io.IOException;
020import java.io.InputStream;
021import java.io.Reader;
022import java.sql.PreparedStatement;
023import java.sql.ResultSet;
024import java.sql.SQLException;
025import java.sql.SQLXML;
026
027import javax.xml.transform.Result;
028import javax.xml.transform.Source;
029import javax.xml.transform.dom.DOMResult;
030import javax.xml.transform.dom.DOMSource;
031
032import org.w3c.dom.Document;
033
034import org.springframework.dao.DataAccessResourceFailureException;
035import org.springframework.lang.Nullable;
036
037/**
038 * Default implementation of the {@link SqlXmlHandler} interface.
039 * Provides database-specific implementations for storing and
040 * retrieving XML documents to and from fields in a database,
041 * relying on the JDBC 4.0 {@code java.sql.SQLXML} facility.
042 *
043 * @author Thomas Risberg
044 * @author Juergen Hoeller
045 * @since 2.5.6
046 * @see java.sql.SQLXML
047 * @see java.sql.ResultSet#getSQLXML
048 * @see java.sql.PreparedStatement#setSQLXML
049 */
050public class Jdbc4SqlXmlHandler implements SqlXmlHandler {
051
052        //-------------------------------------------------------------------------
053        // Convenience methods for accessing XML content
054        //-------------------------------------------------------------------------
055
056        @Override
057        @Nullable
058        public String getXmlAsString(ResultSet rs, String columnName) throws SQLException {
059                SQLXML xmlObject = rs.getSQLXML(columnName);
060                return (xmlObject != null ? xmlObject.getString() : null);
061        }
062
063        @Override
064        @Nullable
065        public String getXmlAsString(ResultSet rs, int columnIndex) throws SQLException {
066                SQLXML xmlObject = rs.getSQLXML(columnIndex);
067                return (xmlObject != null ? xmlObject.getString() : null);
068        }
069
070        @Override
071        @Nullable
072        public InputStream getXmlAsBinaryStream(ResultSet rs, String columnName) throws SQLException {
073                SQLXML xmlObject = rs.getSQLXML(columnName);
074                return (xmlObject != null ? xmlObject.getBinaryStream() : null);
075        }
076
077        @Override
078        @Nullable
079        public InputStream getXmlAsBinaryStream(ResultSet rs, int columnIndex) throws SQLException {
080                SQLXML xmlObject = rs.getSQLXML(columnIndex);
081                return (xmlObject != null ? xmlObject.getBinaryStream() : null);
082        }
083
084        @Override
085        @Nullable
086        public Reader getXmlAsCharacterStream(ResultSet rs, String columnName) throws SQLException {
087                SQLXML xmlObject = rs.getSQLXML(columnName);
088                return (xmlObject != null ? xmlObject.getCharacterStream() : null);
089        }
090
091        @Override
092        @Nullable
093        public Reader getXmlAsCharacterStream(ResultSet rs, int columnIndex) throws SQLException {
094                SQLXML xmlObject = rs.getSQLXML(columnIndex);
095                return (xmlObject != null ? xmlObject.getCharacterStream() : null);
096        }
097
098        @Override
099        @Nullable
100        public Source getXmlAsSource(ResultSet rs, String columnName, @Nullable Class<? extends Source> sourceClass)
101                        throws SQLException {
102
103                SQLXML xmlObject = rs.getSQLXML(columnName);
104                if (xmlObject == null) {
105                        return null;
106                }
107                return (sourceClass != null ? xmlObject.getSource(sourceClass) : xmlObject.getSource(DOMSource.class));
108        }
109
110        @Override
111        @Nullable
112        public Source getXmlAsSource(ResultSet rs, int columnIndex, @Nullable Class<? extends Source> sourceClass)
113                        throws SQLException {
114
115                SQLXML xmlObject = rs.getSQLXML(columnIndex);
116                if (xmlObject == null) {
117                        return null;
118                }
119                return (sourceClass != null ? xmlObject.getSource(sourceClass) : xmlObject.getSource(DOMSource.class));
120        }
121
122
123        //-------------------------------------------------------------------------
124        // Convenience methods for building XML content
125        //-------------------------------------------------------------------------
126
127        @Override
128        public SqlXmlValue newSqlXmlValue(final String value) {
129                return new AbstractJdbc4SqlXmlValue() {
130                        @Override
131                        protected void provideXml(SQLXML xmlObject) throws SQLException, IOException {
132                                xmlObject.setString(value);
133                        }
134                };
135        }
136
137        @Override
138        public SqlXmlValue newSqlXmlValue(final XmlBinaryStreamProvider provider) {
139                return new AbstractJdbc4SqlXmlValue() {
140                        @Override
141                        protected void provideXml(SQLXML xmlObject) throws SQLException, IOException {
142                                provider.provideXml(xmlObject.setBinaryStream());
143                        }
144                };
145        }
146
147        @Override
148        public SqlXmlValue newSqlXmlValue(final XmlCharacterStreamProvider provider) {
149                return new AbstractJdbc4SqlXmlValue() {
150                        @Override
151                        protected void provideXml(SQLXML xmlObject) throws SQLException, IOException {
152                                provider.provideXml(xmlObject.setCharacterStream());
153                        }
154                };
155        }
156
157        @Override
158        public SqlXmlValue newSqlXmlValue(final Class<? extends Result> resultClass, final XmlResultProvider provider) {
159                return new AbstractJdbc4SqlXmlValue() {
160                        @Override
161                        protected void provideXml(SQLXML xmlObject) throws SQLException, IOException {
162                                provider.provideXml(xmlObject.setResult(resultClass));
163                        }
164                };
165        }
166
167        @Override
168        public SqlXmlValue newSqlXmlValue(final Document document) {
169                return new AbstractJdbc4SqlXmlValue() {
170                        @Override
171                        protected void provideXml(SQLXML xmlObject) throws SQLException, IOException {
172                                xmlObject.setResult(DOMResult.class).setNode(document);
173                        }
174                };
175        }
176
177
178        /**
179         * Internal base class for {@link SqlXmlValue} implementations.
180         */
181        private abstract static class AbstractJdbc4SqlXmlValue implements SqlXmlValue {
182
183                @Nullable
184                private SQLXML xmlObject;
185
186                @Override
187                public void setValue(PreparedStatement ps, int paramIndex) throws SQLException {
188                        this.xmlObject = ps.getConnection().createSQLXML();
189                        try {
190                                provideXml(this.xmlObject);
191                        }
192                        catch (IOException ex) {
193                                throw new DataAccessResourceFailureException("Failure encountered while providing XML", ex);
194                        }
195                        ps.setSQLXML(paramIndex, this.xmlObject);
196                }
197
198                @Override
199                public void cleanup() {
200                        if (this.xmlObject != null) {
201                                try {
202                                        this.xmlObject.free();
203                                }
204                                catch (SQLException ex) {
205                                        throw new DataAccessResourceFailureException("Could not free SQLXML object", ex);
206                                }
207                        }
208                }
209
210                protected abstract void provideXml(SQLXML xmlObject) throws SQLException, IOException;
211        }
212
213}