雖然還有很多bug,但湊合能用,就是將CachedRowSet中的數據轉換成對象或List。省去了繁瑣難看的一系列get/set方法。
先說調用:
注:
cachedRowSet是查詢的結果集
Student是對應於Student表的實體類
1. 從數據庫Student表中查詢出多行數據,要將其存入ArrayList<Student>中:
ArrayList<Student> student = Convert.RStoList(cachedRowSet , Student.class ) ;
2. 從數據庫Student表中查詢出單行數據,並將其存入Student對象中:
cachedRowSet.next() ;
Student student = Convert.RStoObject(cachedRowSet , Student.class) ;
1 package util; 2 3 import java.lang.reflect.InvocationTargetException; 4 import java.lang.reflect.Method; 5 import java.sql.ResultSetMetaData; 6 import java.sql.SQLException; 7 import java.text.ParseException; 8 import java.text.SimpleDateFormat; 9 import java.util.ArrayList; 10 import java.util.Date; 11 12 import javax.sql.rowset.CachedRowSet; 13 14 import com.sun.rowset.CachedRowSetImpl; 15 16 /** 17 * 18 * @author caiyao 19 * 20 * @function 類型轉換 21 * 22 * @version 2.0 23 * 24 * @modifyTime : 2015-9-1 25 */ 26 public class Convert { 27 /** 28 * 將CachedRowSetImpl中的多條記錄存儲進List中 29 * @param cachedRS CachedRowSetImpl結果集 30 * @param c List中元素類型 31 * @return ArrayList對象 32 * @throws SQLException cachedRS.next()導致的異常 33 * @throws RuntimeException 34 * @throws InvocationTargetException 35 * @throws ClassNotFoundException 36 * @throws NoSuchMethodException 37 * @throws IllegalAccessException 38 * @throws InstantiationException 39 */ 40 public static <T> ArrayList<T> RStoList(CachedRowSet cachedRS , Class<T> c) 41 throws SQLException, InstantiationException, IllegalAccessException, 42 NoSuchMethodException, ClassNotFoundException, InvocationTargetException, 43 RuntimeException{ 44 if(!cachedRS.next()){ 45 return null ; 46 } 47 cachedRS.previous() ;// 由於上面執行了next,所有這里需要將指針移動到上一行 48 ArrayList<T> store = new ArrayList<T>() ; 49 while(cachedRS.next()){ 50 T object = RStoObject(cachedRS , c ) ; 51 store.add(object) ; 52 } 53 return store ; 54 } 55 56 /** 57 * 將ResultSet類型的結果集轉換成Object類型的對象 58 * 注: 59 * 1. 該方法只能轉換只有一條結果的CachedRowSet。 60 * 2.rs結果集中得到的值xxx,在類中要有相應的setXxx方法 61 * 62 * @param rs 63 * 結果集 64 * @param c 65 * Class類型的值,可以通過Class.forName(類名)獲取,要確定該類有無參的構造方法, 66 * 否則會出現InstantiationException異常 67 * @return 轉換之后的對象 68 * @throws InstantiationException 69 * @throws IllegalAccessException 70 * @throws SQLException 71 * @throws RuntimeException 72 * @throws NoSuchMethodException 73 * @throws ClassNotFoundException 74 * @throws InvocationTargetException 75 * 注: 76 * 1. 該方法只能轉換只有一條結果的CachedRowSet。 77 * 2.rs結果集中得到的值xxx,在類中要有相應的setXxx方法 78 * 3. 在單獨調用這個方法的時候(不是通過RStoList的間接調用)。 79 * 需要在傳遞CachedRowSet這個參數之前調用rs的next()方法。否則會出現指針無效的錯誤 80 * 4. 使用該方法將結果集中的數據轉儲到對象中時,要對象中屬性get/set方法要和數據庫中 81 * 列名相對應。例如數據庫列名為user_name,那么對象的方法就應該是setUserName/getUserName 82 */ 83 public static <T> T RStoObject(CachedRowSet rs , Class<T> c) 84 throws InstantiationException, IllegalAccessException, 85 SQLException, NoSuchMethodException, RuntimeException, 86 ClassNotFoundException, InvocationTargetException{ 87 // 要確定該類有無參的構造方法 88 T instance = c.newInstance() ; 89 // 獲取元數據,以便得到列的信息 90 ResultSetMetaData metaData = rs.getMetaData() ; 91 // 獲取列數 92 int columnNum = metaData.getColumnCount() ; 93 94 for(int i = 1 ; i <= columnNum ; i ++ ){ 95 String columnName = getColumnName(metaData.getColumnName(i)) ; 96 Class<?> columnClassType = SQLTypeToClass(metaData.getColumnType(i)) ; 97 columnClassType = getColumnClassType(columnName , columnClassType , c ) ; 98 String columnTypeName = columnTypeToGetter(metaData.getColumnTypeName(i)) ; 99 // 反射獲取對象的set方法 100 Method objectMethod = c.getMethod( 101 "set"+columnName.substring(0,1).toUpperCase()+columnName.substring(1), 102 columnClassType 103 ) ; 104 // 反射獲取CachedRowSetImpl的get方法 105 Method RSGetter = CachedRowSetImpl.class.getMethod( 106 "get"+columnTypeName.substring(0,1).toUpperCase()+columnTypeName.substring(1), 107 int.class) ; 108 // 執行RS的get方法獲取屬性值 109 Object value = RSGetter.invoke(rs,i) ; 110 // 執行Object的set方法為對象賦值 111 objectMethod.invoke(instance, value) ; 112 } 113 // 返回對象 114 return instance ; 115 } 116 117 /** 118 * 判斷類c是否存在set'columnName'('columnClassType' x)方法。 119 * 如果不存在,再尋找是否存在set'columnName'('cloumnClassType的父類' x)方法,如果存在返回cloumnClassType的父類。 120 * 如果存在,則返回cloumnClassType原值。 121 * @param columnName 類c的屬性名 122 * @param columnClassType 類c set方法的參數類型 123 * @param c 類c 124 * @return set方法的屬性類型 125 */ 126 private static Class<?> getColumnClassType(String columnName , Class<?> columnClassType , Class<?> c ){ 127 /* 128 * 寫該方法的原因: 129 * int/double 在oracle數據庫中都是用Number表示。 130 * 所以從數據庫中查詢出來的數據無法辨別是int還是double, 也就導致無法正確通過反射獲取對象的set方法,因為方法參數無法確定。 131 * 例如: Student表中有兩個屬性 int a , double b ; 132 * 存入數據庫中就是都是Number,從數據庫中取出也都是Number。 133 * 如果將Number對應於java的int,那么SQLTypeToClass()方法返回的就是int.class, 134 * 無論從數據庫中查詢出來的是int還是double,因此,下面獲取set方法就只能獲取以int為參數的方法。 所以就無法獲取到double 135 * b 的set方法。 反射獲取方法是確定的,不會在找不到int參數方法時轉而去找double參數方法。 136 * 該方法為了在找不到int為參數的方法時,繼而找以double類型參數的方法。 137 * 或者以后會添加找不到子類類型為參數的方法繼而找以其父類為參數的方法。 138 */ 139 try { 140 c.getMethod("set"+columnName.substring(0,1).toUpperCase()+columnName.substring(1), columnClassType).getName() ; 141 } catch (NoSuchMethodException e) { 142 return getSuperColumnClassType(columnClassType) ; 143 } catch (SecurityException e) {} 144 return columnClassType ; 145 } 146 /** 147 * 獲取columnClassType的父類 148 * @param columnClassType 149 * @return 150 */ 151 private static Class<?> getSuperColumnClassType(Class<?> columnClassType){ 152 if(columnClassType.equals(int.class)){ 153 return double.class ; 154 }else{ 155 return columnClassType.getSuperclass() ; 156 } 157 158 } 159 /** 160 * 獲取與數據庫列名對應的屬性名。 161 * 例如: 162 * 數據庫列名 屬性名 163 * user_name/username userName 164 * @param rawColumnName 數據庫列名 165 * @return 與數據庫列名對應的對象屬性名 166 */ 167 public static String getColumnName(String rawColumnName ){ 168 if(rawColumnName.matches(".*_.*")){ 169 String[] words = rawColumnName.split("_") ; 170 String firstWord = words[0].toLowerCase() ; 171 String lastWord = words[1].substring(0, 1).toUpperCase() + words[1].substring(1).toLowerCase() ; 172 String attributeName = firstWord + lastWord ; 173 return attributeName ; 174 }else{ 175 /* 176 * 如果數據庫列名不包含下划線,有可能只有一個單詞,那么將首字母大寫然后直接返回就行。 177 * 如果不止一個單詞那就沒轍了: 怎樣讓第二個單詞首字母大寫? 178 * 這里就暫時讓其直接返回吧 179 * TODO : 怎樣讓第二個單詞首字母大寫 180 */ 181 return rawColumnName.substring(0,1).toUpperCase() + rawColumnName.substring(1).toLowerCase() ; 182 } 183 } 184 public static String columnTypeToGetter(String columnType){ 185 /** 186 * mysql數據庫中存儲字符串只有varchar,但是ResultSet接口中沒有定義getVarchar()方法所以將varchar轉換成String即可, 187 */ 188 if(columnType.equals("VARCHAR")){ 189 return "String" ; 190 } 191 if(columnType.equals("NVARCHAR2")){ 192 return "String" ; 193 } 194 // oracle數據庫NUMBER字段類型對應Java中int 195 if(columnType.equals("NUMBER")){ 196 return "int" ; 197 } 198 if(columnType.toLowerCase().equals("timestamp")){ 199 return "object" ; 200 } 201 return columnType.toLowerCase() ; 202 } 203 /** 204 * 將封裝后的原始類型名稱轉換成原始類型的class 205 * @param columnType 封裝后的對象名稱例如"java.lang.Integer" , "java.lang.Float" , "java.lang.Double" 206 * @return 原始類型class類型 例如int.class float.class double.class 207 * @throws ClassNotFoundException 208 */ 209 public static Class<?> getColumnClassType(String columnType) throws ClassNotFoundException{ 210 /** 211 * 數據庫中的int、float、double等原始列類型當通過getColumnClassName()方法獲取時,會將原始類型打包成其基礎類型封裝對象 212 * 例如 int --> Integer float --> Float double --> Double 213 * 該方法是為了將獲取的基礎類型封裝類名轉換成原始類型的class 214 */ 215 if(columnType.equals("java.lang.Integer")){ 216 return int.class ; 217 } 218 if(columnType.equals("java.lang.Float")){ 219 return float.class ; 220 } 221 if(columnType.equals("java.lang.Double")){ 222 return double.class ; 223 } 224 return Class.forName(columnType) ; 225 } 226 /** 227 * 將java.sql.Types類型轉化成相應的Java中對應的Class 228 * @param SQLType java.sql.Types類型 229 * @return Class類型的實例例如 int.class 230 */ 231 public static Class<?> SQLTypeToClass(int SQLType){ 232 switch(SQLType){ 233 case java.sql.Types.ARRAY : return String.class ; 234 235 case java.sql.Types.BIGINT : return int.class ; 236 237 case java.sql.Types.BIT : return byte.class ; 238 239 case java.sql.Types.BOOLEAN : return boolean.class ; 240 241 case java.sql.Types.CHAR : return char.class ; 242 243 case java.sql.Types.DOUBLE : return double.class ; 244 245 case java.sql.Types.FLOAT : return float.class ; 246 247 case java.sql.Types.INTEGER : return int.class ; 248 249 case java.sql.Types.LONGNVARCHAR : return String.class ; 250 251 case java.sql.Types.LONGVARCHAR : return String.class ; 252 253 case java.sql.Types.NCHAR : return String.class ; 254 255 case java.sql.Types.NVARCHAR :return String.class ; 256 257 case java.sql.Types.VARCHAR : return String.class ; 258 // 通過rs.getDate()獲取到的java.sql.Date能夠直接將其賦值到java.util.Date會進行自動轉換 259 case java.sql.Types.DATE : return Date.class ; 260 // oracle數據庫的Number字段類型對應NUMERIC類型, 261 //但是在數據庫中整數和浮點數都是Number, 262 //所以在應用中無法判斷從數據庫中取出的是整數還是浮點數. 263 //處理方法是:在應用中都使用浮點數 264 case java.sql.Types.NUMERIC : return int.class ; 265 // 由於sun包中提供的CachedRowSetImpl類存在bug,當getTimestamp()字段時會出現類轉換異常。 266 // 解決方案有: 267 // 1. 換類。oracle驅動包oracle.jdbc.rowset.OracleCachedRowSet類(大概是這個名字)可以正確執行。 268 // 2. 換oracle驅動版本。10.0以上,這個我沒有試過,有些人說可以。 269 // 3. 如果使用sun提供的這個類,獲取Timestamp時使用getObject()。然后再轉換。 270 // 因為考慮到該類的通用性,所以采用第三種解決方案。 271 case java.sql.Types.TIMESTAMP : return Object.class ; 272 273 default :return String.class ; 274 } 275 } 276 public static Date StringTodate(String dateString , String format ) throws ParseException{ 277 SimpleDateFormat sdf = new SimpleDateFormat(format); 278 // 將時間字符串轉換成java.util.Date對象 279 Date date = sdf.parse(dateString); 280 return date ; 281 } 282 public static java.sql.Date utildateTosqldate(Date date){ 283 return new java.sql.Date(date.getTime()); 284 } 285 }
