将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 }

 

posted @ 2015-08-30 17:17  Birding  阅读(3027)  评论(0编辑  收藏  举报