将CachedRowSet中的数据转储到对象中
虽然还有很多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 }