001/*
002 * Copyright 2002-2012 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.nativejdbc;
018
019import java.sql.CallableStatement;
020import java.sql.Connection;
021import java.sql.DatabaseMetaData;
022import java.sql.PreparedStatement;
023import java.sql.ResultSet;
024import java.sql.SQLException;
025import java.sql.Statement;
026
027import org.springframework.jdbc.datasource.DataSourceUtils;
028
029/**
030 * Abstract adapter class for the {@link NativeJdbcExtractor} interface,
031 * for simplified implementation of basic extractors.
032 * Basically returns the passed-in JDBC objects on all methods.
033 *
034 * <p>{@code getNativeConnection} checks for a ConnectionProxy chain,
035 * for example from a TransactionAwareDataSourceProxy, before delegating to
036 * {@code doGetNativeConnection} for actual unwrapping. You can override
037 * either of the two for a specific connection pool, but the latter is
038 * recommended to participate in ConnectionProxy unwrapping.
039 *
040 * <p>{@code getNativeConnection} also applies a fallback if the first
041 * native extraction process failed, that is, returned the same Connection as
042 * passed in. It assumes that some additional proxying is going in this case:
043 * Hence, it retrieves the underlying native Connection from the DatabaseMetaData
044 * via {@code conHandle.getMetaData().getConnection()} and retries the native
045 * extraction process based on that Connection handle. This works, for example,
046 * for the Connection proxies exposed by Hibernate 3.1's {@code Session.connection()}.
047 *
048 * <p>The {@code getNativeConnectionFromStatement} method is implemented
049 * to simply delegate to {@code getNativeConnection} with the Statement's
050 * Connection. This is what most extractor implementations will stick to,
051 * unless there's a more efficient version for a specific pool.
052 *
053 * @author Juergen Hoeller
054 * @since 1.1
055 * @see #getNativeConnection
056 * @see #getNativeConnectionFromStatement
057 * @see org.springframework.jdbc.datasource.ConnectionProxy
058 */
059public abstract class NativeJdbcExtractorAdapter implements NativeJdbcExtractor {
060
061        /**
062         * Return {@code false} by default.
063         */
064        @Override
065        public boolean isNativeConnectionNecessaryForNativeStatements() {
066                return false;
067        }
068
069        /**
070         * Return {@code false} by default.
071         */
072        @Override
073        public boolean isNativeConnectionNecessaryForNativePreparedStatements() {
074                return false;
075        }
076
077        /**
078         * Return {@code false} by default.
079         */
080        @Override
081        public boolean isNativeConnectionNecessaryForNativeCallableStatements() {
082                return false;
083        }
084
085        /**
086         * Check for a ConnectionProxy chain, then delegate to doGetNativeConnection.
087         * <p>ConnectionProxy is used by Spring's TransactionAwareDataSourceProxy
088         * and LazyConnectionDataSourceProxy. The target connection behind it is
089         * typically one from a local connection pool, to be unwrapped by the
090         * doGetNativeConnection implementation of a concrete subclass.
091         * @see #doGetNativeConnection
092         * @see org.springframework.jdbc.datasource.ConnectionProxy
093         * @see org.springframework.jdbc.datasource.DataSourceUtils#getTargetConnection
094         * @see org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy
095         * @see org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy
096         */
097        @Override
098        public Connection getNativeConnection(Connection con) throws SQLException {
099                if (con == null) {
100                        return null;
101                }
102                Connection targetCon = DataSourceUtils.getTargetConnection(con);
103                Connection nativeCon = doGetNativeConnection(targetCon);
104                if (nativeCon == targetCon) {
105                        // We haven't received a different Connection, so we'll assume that there's
106                        // some additional proxying going on. Let's check whether we get something
107                        // different back from the DatabaseMetaData.getConnection() call.
108                        DatabaseMetaData metaData = targetCon.getMetaData();
109                        // The following check is only really there for mock Connections
110                        // which might not carry a DatabaseMetaData instance.
111                        if (metaData != null) {
112                                Connection metaCon = metaData.getConnection();
113                                if (metaCon != null && metaCon != targetCon) {
114                                        // We've received a different Connection there:
115                                        // Let's retry the native extraction process with it.
116                                        nativeCon = doGetNativeConnection(metaCon);
117                                }
118                        }
119                }
120                return nativeCon;
121        }
122
123        /**
124         * Not able to unwrap: return passed-in Connection.
125         */
126        protected Connection doGetNativeConnection(Connection con) throws SQLException {
127                return con;
128        }
129
130        /**
131         * Retrieve the Connection via the Statement's Connection.
132         * @see #getNativeConnection
133         * @see Statement#getConnection
134         */
135        @Override
136        public Connection getNativeConnectionFromStatement(Statement stmt) throws SQLException {
137                if (stmt == null) {
138                        return null;
139                }
140                return getNativeConnection(stmt.getConnection());
141        }
142
143        /**
144         * Not able to unwrap: return passed-in Statement.
145         */
146        @Override
147        public Statement getNativeStatement(Statement stmt) throws SQLException {
148                return stmt;
149        }
150
151        /**
152         * Not able to unwrap: return passed-in PreparedStatement.
153         */
154        @Override
155        public PreparedStatement getNativePreparedStatement(PreparedStatement ps) throws SQLException {
156                return ps;
157        }
158
159        /**
160         * Not able to unwrap: return passed-in CallableStatement.
161         */
162        @Override
163        public CallableStatement getNativeCallableStatement(CallableStatement cs) throws SQLException {
164                return cs;
165        }
166
167        /**
168         * Not able to unwrap: return passed-in ResultSet.
169         */
170        @Override
171        public ResultSet getNativeResultSet(ResultSet rs) throws SQLException {
172                return rs;
173        }
174
175}