Jdbc 比較繁瑣的一個操作就是解析結果集ResultSet, 在實際開發時, 通常會將對結果集的解析封裝爲一個工具類. 需要注意的時, jdbc查詢出來的屬性可能不能直接轉換爲java的類型, 比如說java.sql.Date, 不能直接轉換爲java.util.Date 或LocalDate等類型, 需要自定義轉換器. 如果比較熟悉Mybatis的話, 會發現Mybatis底層也封裝了大量的類型轉換器.
1. 工具類源碼
筆者的工具類比較簡單, 只封裝了三個方法:
方法簽名 | 方法描述 | 參數說明 |
public static LinkedHashMap<String, Object> toPropertyMap(ResultSet resultSet) throws SQLException | 轉換單行結果集爲Map結構. key爲列別名, value爲列值 | resultSet: 查詢結果集 |
public static T toBean(ResultSet resultSet, Class clz) | 轉換結果集爲單行對象 | clz: 目標對象類型 resultSet: 結果集 |
public static List toBeans(ResultSet resultSet, Class clz) throws SQLException | 轉換結果集爲java對象集合. | clz: 要轉換的javaBean類 resultSet: 結果集 |
1.1 ResultSetUtil 源碼
/** 結果集解析工具類
* @since 1.0
* @author zongf
* @created 2019-07-18
*/
public class ResultSetUtil {
/** 轉換單行結果集爲Map結構. key爲列別名, value爲列值
* @param resultSet 查詢結果集
* @return 結果集中爲空時, 返回null
* @since 1.0
* @author zongf
* @created 2019-07-18
*/
public static LinkedHashMap<String, Object> toPropertyMap(ResultSet resultSet) throws SQLException {
// 如果結果集爲空,則返回null
if (!resultSet.next()) return null;
LinkedHashMap<String, Object> cloumnMap = new LinkedHashMap<>();
// 獲取結果集元信息
ResultSetMetaData metaData = resultSet.getMetaData();
// 獲取每一列列名與值
for (int i = 1; i <= metaData.getColumnCount(); i++) {
// 獲取列別名爲key
String columnLabel = metaData.getColumnLabel(i);
Object columnValue = resultSet.getObject(i);
cloumnMap.put(columnLabel, columnValue);
}
return cloumnMap;
}
/** 轉換結果集爲單行對象
* @param clz 目標對象類型
* @param resultSet 結果集
* @since 1.0
* @return null
* @author zongf
* @created 2019-07-18
*/
public static <T> T toBean(ResultSet resultSet, Class<T> clz) {
try {
LinkedHashMap<String, Object> propertyMap = toPropertyMap(resultSet);
return ReflectUtil.newInstance(clz, propertyMap, new DateTypeConverter());
} catch (SQLException e) {
throw new RuntimeException("sql 執行異常!", e);
}
}
/** 轉換結果集爲java對象集合.
* @param clz 要轉換的javaBean類
* @param resultSet 結果集
* @return 結果集中沒有數據時, 返回null
* @since 1.0
* @author zongf
* @created 2019-07-18
*/
public static <T> List<T> toBeans(ResultSet resultSet, Class<T> clz) throws SQLException {
List<T> list = new ArrayList<>();
LinkedHashMap<String, Object> propertyMap = null;
// 解析結果集
while ((propertyMap = toPropertyMap(resultSet)) != null) {
T t = (T) ReflectUtil.newInstance(clz, propertyMap, new DateTypeConverter());
if(t != null) list.add(t);
}
return list.size() > 0 ? list : null;
}
}
1.2 ReflectUtil 源碼
這是筆者對使用到的反射技術封裝的一個簡單工具類.
/** 反射工具類
* @since 1.0
* @author zongf
* @created 2019-07-18
*/
public class ReflectUtil {
/**爲對象屬性賦值
* @param target 目標對象
* @param property 屬性名
* @param property 屬性名
* @return value 屬性值
* @since 1.0
* @author zongf
* @created 2019-07-18
*/
public static void setPropertyValue(Object target, String property, Object value) {
try {
PropertyDescriptor descriptor = new PropertyDescriptor(property, target.getClass());
Method writeMethod = descriptor.getWriteMethod();
writeMethod.invoke(target, value);
} catch (Exception e) {
throw new RuntimeException("爲對象屬性賦值異常!",e);
}
}
/** 獲取對象屬性值
* @param target 目標對象
* @param property 屬性
* @return Object 返回對象屬性值
* @since 1.0
* @author zongf
* @created 2019-07-18
*/
public static Object getPropertyValue(Object target, String property) {
try {
PropertyDescriptor descriptor = new PropertyDescriptor(property, target.getClass());
Method readMethod = descriptor.getReadMethod();
return readMethod.invoke(target);
} catch (Exception e) {
throw new RuntimeException("獲取對象屬性異常!",e);
}
}
/** 反射創建對象
* @param clz 目標對象的類型
* @return propertiesMap 目標對象的屬性與值
* @since 1.0
* @author zongf
* @created 2019-07-18
*/
public static <T> T newInstance(Class<T> clz, HashMap<String, Object> propertiesMap, DateTypeConverter typeConverter){
// 如果屬性爲空, 則不進行創建, 返回null
if (propertiesMap == null || propertiesMap.isEmpty()) {
return null;
}
// 使用無參數構造方法創建對象
T t = null;
try {
t = clz.newInstance();
for (Map.Entry<String, Object> entry : propertiesMap.entrySet()) {
// 獲取對象屬性與值
String property = entry.getKey();
Object value = entry.getValue();
// 獲取屬性描述符
PropertyDescriptor propertyDescriptor = new PropertyDescriptor(property, clz);
// 獲取屬性類型
Class<?> propertyType = propertyDescriptor.getPropertyType();
// 使用類型轉換器轉換參數類型
value = typeConverter.convert(value, propertyType);
// 調用set方法, 賦值
Method writeMethod = propertyDescriptor.getWriteMethod();
writeMethod.invoke(t, value);
}
} catch (Exception e) {
throw new RuntimeException("反射創建對象失敗!", e);
}
return t;
}
}
1.3 DateTypeConverter 轉換器
筆者僅僅編寫了一個日期類型的轉換器, 在企業開發中, 可能需要用到的轉換器會更多.
/** 日期類型轉換器
* @since 1.0
* @author zongf
* @created 2019-07-18
*/
public class DateTypeConverter {
/** 轉換對象的類型
* @param value 值
* @param javaType java類型
* @return 轉換後的類型
* @since 1.0
* @author zongf
* @created 2019-07-18
*/
public Object convert(Object value, Class javaType) {
Object obj = value;
// 如果是java 日期
if(javaType.equals(Date.class)) {
java.sql.Date date = (java.sql.Date) value;
obj = date.toInstant().getEpochSecond();
// 如果是java8 日期
} else if(javaType.equals(LocalDate.class)){
obj = ((java.sql.Date) value).toLocalDate();
} else {
obj = value;
}
return obj;
}
}
2. 單元測試
2.1 創建javaBean
測試時, 需要創建t_user表和javabean, 筆者這邊僅給出javabean的定義.
public class UserPO {
private Integer id;
private String name;
private String password;
private LocalDate birthday;
// 省略setter/getter/toString 方法
}
2.2 測試用例
// 測試轉換單個對象爲Map 結構
@Test
public void toPropertyMap() throws SQLException {
String str = "select id uId, name , pwd, birthday from t_user where id = 1001";
Connection connection = DbConnUtil.getConnection(true);
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(str);
LinkedHashMap<String, Object> map = ResultSetUtil.toPropertyMap(resultSet);
map.forEach((key, val) -> System.out.println(key + ":" + val));
}
// 測試轉換對象爲單個bean
@Test
public void toBean() throws SQLException {
String str = "select * from t_user where id = 1002";
Connection connection = DbConnUtil.getConnection(true);
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(str);
UserPO userPO = ResultSetUtil.toBean(resultSet, UserPO.class);
System.out.println(userPO);
}
// 測試轉換對象爲bean列表
@Test
public void toBeans() throws SQLException {
String str = "select * from t_user ";
Connection connection = DbConnUtil.getConnection(true);
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(str);
List<UserPO> userPOList = ResultSetUtil.toBeans(resultSet, UserPO.class);
userPOList.forEach(System.out::println);
}