View Javadoc
1   /*
2    * Copyright 2002-2014 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.jdbc.core;
18  
19  import java.sql.ResultSet;
20  import java.sql.SQLException;
21  import javax.sql.rowset.CachedRowSet;
22  import javax.sql.rowset.RowSetFactory;
23  import javax.sql.rowset.RowSetProvider;
24  
25  import org.springframework.core.JdkVersion;
26  import org.springframework.jdbc.support.rowset.ResultSetWrappingSqlRowSet;
27  import org.springframework.jdbc.support.rowset.SqlRowSet;
28  import org.springframework.lang.UsesJava7;
29  import org.springframework.util.ClassUtils;
30  
31  /**
32   * {@link ResultSetExtractor} implementation that returns a Spring {@link SqlRowSet}
33   * representation for each given {@link ResultSet}.
34   *
35   * <p>The default implementation uses a standard JDBC CachedRowSet underneath.
36   * This means that JDBC RowSet support needs to be available at runtime:
37   * by default, Sun's {@code com.sun.rowset.CachedRowSetImpl} class on Java 5 and 6,
38   * or the {@code javax.sql.rowset.RowSetProvider} mechanism on Java 7 / JDBC 4.1.
39   *
40   * @author Juergen Hoeller
41   * @since 1.2
42   * @see #newCachedRowSet
43   * @see org.springframework.jdbc.support.rowset.SqlRowSet
44   * @see JdbcTemplate#queryForRowSet(String)
45   * @see javax.sql.rowset.CachedRowSet
46   */
47  public class SqlRowSetResultSetExtractor implements ResultSetExtractor<SqlRowSet> {
48  
49  	private static final CachedRowSetFactory cachedRowSetFactory;
50  
51  	static {
52  		if (JdkVersion.getMajorJavaVersion() >= JdkVersion.JAVA_17) {
53  			// using JDBC 4.1 RowSetProvider
54  			cachedRowSetFactory = new StandardCachedRowSetFactory();
55  		}
56  		else {
57  			// JDBC 4.1 API not available - fall back to Sun CachedRowSetImpl
58  			cachedRowSetFactory = new SunCachedRowSetFactory();
59  		}
60  	}
61  
62  
63  	@Override
64  	public SqlRowSet extractData(ResultSet rs) throws SQLException {
65  		return createSqlRowSet(rs);
66  	}
67  
68  	/**
69  	 * Create a SqlRowSet that wraps the given ResultSet,
70  	 * representing its data in a disconnected fashion.
71  	 * <p>This implementation creates a Spring ResultSetWrappingSqlRowSet
72  	 * instance that wraps a standard JDBC CachedRowSet instance.
73  	 * Can be overridden to use a different implementation.
74  	 * @param rs the original ResultSet (connected)
75  	 * @return the disconnected SqlRowSet
76  	 * @throws SQLException if thrown by JDBC methods
77  	 * @see #newCachedRowSet
78  	 * @see org.springframework.jdbc.support.rowset.ResultSetWrappingSqlRowSet
79  	 */
80  	protected SqlRowSet createSqlRowSet(ResultSet rs) throws SQLException {
81  		CachedRowSet rowSet = newCachedRowSet();
82  		rowSet.populate(rs);
83  		return new ResultSetWrappingSqlRowSet(rowSet);
84  	}
85  
86  	/**
87  	 * Create a new CachedRowSet instance, to be populated by
88  	 * the {@code createSqlRowSet} implementation.
89  	 * <p>The default implementation uses JDBC 4.1's RowSetProvider
90  	 * when running on JDK 7 or higher, falling back to Sun's
91  	 * {@code com.sun.rowset.CachedRowSetImpl} class on older JDKs.
92  	 * @return a new CachedRowSet instance
93  	 * @throws SQLException if thrown by JDBC methods
94  	 * @see #createSqlRowSet
95  	 */
96  	protected CachedRowSet newCachedRowSet() throws SQLException {
97  		return cachedRowSetFactory.createCachedRowSet();
98  	}
99  
100 
101 	/**
102 	 * Internal strategy interface for the creation of CachedRowSet instances.
103 	 */
104 	private interface CachedRowSetFactory {
105 
106 		CachedRowSet createCachedRowSet() throws SQLException;
107 	}
108 
109 
110 	/**
111 	 * Inner class to avoid a hard dependency on JDBC 4.1 RowSetProvider class.
112 	 */
113 	@UsesJava7
114 	private static class StandardCachedRowSetFactory implements CachedRowSetFactory {
115 
116 		private final RowSetFactory rowSetFactory;
117 
118 		public StandardCachedRowSetFactory() {
119 			try {
120 				this.rowSetFactory = RowSetProvider.newFactory();
121 			}
122 			catch (SQLException ex) {
123 				throw new IllegalStateException("Cannot create RowSetFactory through RowSetProvider", ex);
124 			}
125 		}
126 
127 		@Override
128 		public CachedRowSet createCachedRowSet() throws SQLException {
129 			return this.rowSetFactory.createCachedRowSet();
130 		}
131 	}
132 
133 
134 	/**
135 	 * Inner class to avoid a hard dependency on Sun's CachedRowSetImpl class.
136 	 */
137 	private static class SunCachedRowSetFactory implements CachedRowSetFactory {
138 
139 		private static final Class<?> implementationClass;
140 
141 		static {
142 			try {
143 				implementationClass = ClassUtils.forName("com.sun.rowset.CachedRowSetImpl",
144 						SqlRowSetResultSetExtractor.class.getClassLoader());
145 			}
146 			catch (Throwable ex) {
147 				throw new IllegalStateException(ex);
148 			}
149 		}
150 
151 		@Override
152 		public CachedRowSet createCachedRowSet() throws SQLException {
153 			try {
154 				return (CachedRowSet) implementationClass.newInstance();
155 			}
156 			catch (Throwable ex) {
157 				throw new IllegalStateException(ex);
158 			}
159 		}
160 	}
161 
162 }