001/* 002 * Copyright 2002-2015 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; 018 019import java.sql.ResultSet; 020import java.sql.SQLException; 021import javax.sql.rowset.CachedRowSet; 022import javax.sql.rowset.RowSetFactory; 023import javax.sql.rowset.RowSetProvider; 024 025import org.springframework.jdbc.support.rowset.ResultSetWrappingSqlRowSet; 026import org.springframework.jdbc.support.rowset.SqlRowSet; 027import org.springframework.lang.UsesJava7; 028import org.springframework.util.ClassUtils; 029 030/** 031 * {@link ResultSetExtractor} implementation that returns a Spring {@link SqlRowSet} 032 * representation for each given {@link ResultSet}. 033 * 034 * <p>The default implementation uses a standard JDBC CachedRowSet underneath. 035 * This means that JDBC RowSet support needs to be available at runtime: 036 * by default, Sun's {@code com.sun.rowset.CachedRowSetImpl} class on Java 6, 037 * or the {@code javax.sql.rowset.RowSetProvider} mechanism on Java 7+ / JDBC 4.1+. 038 * 039 * @author Juergen Hoeller 040 * @since 1.2 041 * @see #newCachedRowSet 042 * @see org.springframework.jdbc.support.rowset.SqlRowSet 043 * @see JdbcTemplate#queryForRowSet(String) 044 * @see javax.sql.rowset.CachedRowSet 045 */ 046public class SqlRowSetResultSetExtractor implements ResultSetExtractor<SqlRowSet> { 047 048 private static final CachedRowSetFactory cachedRowSetFactory; 049 050 static { 051 if (ClassUtils.isPresent("javax.sql.rowset.RowSetProvider", 052 SqlRowSetResultSetExtractor.class.getClassLoader())) { 053 // using JDBC 4.1 RowSetProvider, available on JDK 7+ 054 cachedRowSetFactory = new StandardCachedRowSetFactory(); 055 } 056 else { 057 // JDBC 4.1 API not available - fall back to Sun CachedRowSetImpl on JDK 6 058 cachedRowSetFactory = new SunCachedRowSetFactory(); 059 } 060 } 061 062 063 @Override 064 public SqlRowSet extractData(ResultSet rs) throws SQLException { 065 return createSqlRowSet(rs); 066 } 067 068 /** 069 * Create a SqlRowSet that wraps the given ResultSet, 070 * representing its data in a disconnected fashion. 071 * <p>This implementation creates a Spring ResultSetWrappingSqlRowSet 072 * instance that wraps a standard JDBC CachedRowSet instance. 073 * Can be overridden to use a different implementation. 074 * @param rs the original ResultSet (connected) 075 * @return the disconnected SqlRowSet 076 * @throws SQLException if thrown by JDBC methods 077 * @see #newCachedRowSet 078 * @see org.springframework.jdbc.support.rowset.ResultSetWrappingSqlRowSet 079 */ 080 protected SqlRowSet createSqlRowSet(ResultSet rs) throws SQLException { 081 CachedRowSet rowSet = newCachedRowSet(); 082 rowSet.populate(rs); 083 return new ResultSetWrappingSqlRowSet(rowSet); 084 } 085 086 /** 087 * Create a new CachedRowSet instance, to be populated by 088 * the {@code createSqlRowSet} implementation. 089 * <p>The default implementation uses JDBC 4.1's RowSetProvider 090 * when running on JDK 7 or higher, falling back to Sun's 091 * {@code com.sun.rowset.CachedRowSetImpl} class on older JDKs. 092 * @return a new CachedRowSet instance 093 * @throws SQLException if thrown by JDBC methods 094 * @see #createSqlRowSet 095 */ 096 protected CachedRowSet newCachedRowSet() throws SQLException { 097 return cachedRowSetFactory.createCachedRowSet(); 098 } 099 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}