利用反射及 JDBC 元数据编写通用的查询方法.

1.观察之前的查询方法

  1. 方法返回值不一定,要根据具体的运行时类确定
  2. 创建的对象不一定,要根据具体的运行时类确定
  3. 具体对象的属性与结果集的字段之间没有什么关系:
    如何获取结果集中的字段?并给字段对应的对象的属性赋值?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
private Student getStudent(String sql,Object ... args) {
//1.方法返回值不一定,要根据具体的运行时类确定
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
Student stu = null;//2.创建的对象不一定,要根据具体的运行时类确定
try {
conn = JDBCTools.getConnection();
ps = conn.prepareStatement(sql);
for(int i = 0;i < args.length;i ++) {
ps.setObject(i+1, args[i]);
}
rs = ps.executeQuery();
if(rs.next()) {
stu = new Student(
rs.getInt(1),
rs.getString(2).trim(),
rs.getString(3).trim(),
rs.getDate(4));
//3.具体对象的属性与结果集的字段之间没有什么关系,
//如何获取结果集中的字段?并给字段对应对象的属性复制?
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCTools.release(rs, ps, conn);
}
return stu;
}

2.利用反射创建对象

之前我们知道 Java 的反射是动态语言的关键.

忘记如何用反射的可以看一下 , Java反射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public static <T> T get(Class<T> clazz,String sql,Object ... args) {
//1.由于不能确定返回值,利用泛型实现,通过传过来的clazz参数确定返回值
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
T entity = null;
try {
conn = JDBCTools.getConnection();
ps = conn.prepareStatement(sql);
for(int i = 0;i < args.length;i ++) {
ps.setObject(i+1, args[i]);
}
rs = ps.executeQuery();
if(rs.next()) {
//2.利用反射创建对象
entity = clazz.newInstance();
//3.通过解析SQL语句来判断到底选择查询了哪些列,
//以及需要为 entity 对象的哪些属性赋值
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCTools.release(rs, ps, conn);
}
return entity;
}

3.对应列与属性

注意到: SQL查询的数据表的列与对象的属性没有什么关系.

我们要修改一下查询时的SQL语句, 查询时为每一列起一个别名, 该别名与对象的属性一一对应.

JDBC_12

4.获取列名(别名)

A.数据库元数据

  • Java 通过JDBC获得连接以后,得到一个Connection 对象,可以从这个对象获得有关数据库管理系统的各种信息,包括数据库中的各个表,表中的各个列,数据类型,触发器,存储过程等各方面的信息。根据这些信息,JDBC可以访问一个实现事先并不了解的数据库。

  • 获取这些信息的方法都是在DatabaseMetaData类的对象上实现的,而DatabaseMetaData对象是在Connection对象上获得的.

  • 可通过Connection 对象的 getMetaData()方法获取 DatabaseMetaData 对象

  • DatabaseMetaData 类中提供了许多方法用于获得数据源的各种信息,通过这些方法可以非常详细的了解数据库的信息:
      –getURL(): 返回一个String类对象,代表数据库的URL。
      –getUserName(): 返回连接当前数据库管理系统的用户名。
      –isReadOnly(): 返回一个boolean值,指示数据库是否只允许读操作。
      –getDatabaseProductName(): 返回数据库的产品名称。
      –getDatabaseProductVersion(): 返回数据库的版本号。
      –getDriverName(): 返回驱动驱动程序的名称。
      –getDriverVersion(): 返回驱动程序的版本号。
      –getCatalogs(): 返回数据库连接对应的数据库的结果集。

B.结果集元数据

ResultSetMetaData

  • 可用于获取关于 ResultSet 对象中列的类型和属性信息的对象:

getColumnName(int column):获取指定列的名称

getColumnCount():返回当前 ResultSet 对象中的列数。

getColumnTypeName(int column):检索指定列的数据库特定的类型名称。

getColumnDisplaySize(int column):指示指定列的最大标准宽度,以字符为单位。

isNullable(int column):指示指定列中的值是否可以为 null。

isAutoIncrement(int column):指示是否自动为指定列进行编号,这样这些列仍然是只读的。

可通过 ResultSet 对象的 getMetaData()方法获取 ResultSetMetaData 对象

1
2
3
4
5
//rs为ResultSet对象
ResultSetMetaData rsmd = rs.getMetaData();
for (int i = 0; i < rsmd.getColumnCount(); i++) {
System.out.println(rsmd.getColumnName(i+1));//打印出列名
}

5.利用反射创建对象并赋值

A.反射工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
* 反射的 Utils 函数集合
* 提供访问私有变量, 获取泛型类型 Class, 提取集合中元素属性等 Utils 函数
* @author Administrator
*/
public class ReflectionUtils {
/**
* 通过反射, 获得定义 Class 时声明的父类的泛型参数的类型
* 如: public EmployeeDao extends BaseDao<Employee, String>
* @param clazz
* @param index
* @return
*/
@SuppressWarnings("unchecked")
public static Class<Object> getSuperClassGenricType(Class<Object> clazz, int index){
Type genType = clazz.getGenericSuperclass();
if(!(genType instanceof ParameterizedType)){
return Object.class;
}
Type [] params = ((ParameterizedType)genType).getActualTypeArguments();
if(index >= params.length || index < 0){
return Object.class;
}
if(!(params[index] instanceof Class)){
return Object.class;
}
return (Class<Object>) params[index];
}
/**
* 通过反射, 获得 Class 定义中声明的父类的泛型参数类型
* 如: public EmployeeDao extends BaseDao<Employee, String>
* @param <T>
* @param clazz
* @return
*/
public static Class<Object> getSuperGenericType(Class<Object> clazz){
return getSuperClassGenricType(clazz, 0);
}
/**
* 循环向上转型, 获取对象的 DeclaredMethod
* @param object
* @param methodName
* @param parameterTypes
* @return
*/
public static Method getDeclaredMethod(Object object, String methodName, Class<?>[] parameterTypes){
for(Class<?> superClass = object.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()){
try {
//superClass.getMethod(methodName, parameterTypes);
return superClass.getDeclaredMethod(methodName, parameterTypes);
} catch (NoSuchMethodException e) {
//Method 不在当前类定义, 继续向上转型
}
//..
}
return null;
}
/**
* 使 filed 变为可访问
* @param field
*/
public static void makeAccessible(Field field){
if(!Modifier.isPublic(field.getModifiers())){
field.setAccessible(true);
}
}
/**
* 循环向上转型, 获取对象的 DeclaredField
* @param object
* @param filedName
* @return
*/
public static Field getDeclaredField(Object object, String filedName){
for(Class<?> superClass = object.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()){
try {
return superClass.getDeclaredField(filedName);
} catch (NoSuchFieldException e) {
//Field 不在当前类定义, 继续向上转型
}
}
return null;
}
/**
* 直接调用对象方法, 而忽略修饰符(private, protected)
* @param object
* @param methodName
* @param parameterTypes
* @param parameters
* @return
* @throws InvocationTargetException
* @throws IllegalArgumentException
*/
public static Object invokeMethod(Object object, String methodName, Class<?> [] parameterTypes,
Object [] parameters) throws InvocationTargetException{
Method method = getDeclaredMethod(object, methodName, parameterTypes);
if(method == null){
throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + object + "]");
}
method.setAccessible(true);
try {
return method.invoke(object, parameters);
} catch(IllegalAccessException e) {
System.out.println("不可能抛出的异常");
}
return null;
}
/**
* 直接设置对象属性值, 忽略 private/protected 修饰符, 也不经过 setter
* @param object
* @param fieldName
* @param value
*/
public static void setFieldValue(Object object, String fieldName, Object value){
Field field = getDeclaredField(object, fieldName);
if (field == null)
throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + object + "]");
makeAccessible(field);
try {
field.set(object, value);
} catch (IllegalAccessException e) {
System.out.println("不可能抛出的异常");
}
}
/**
* 直接读取对象的属性值, 忽略 private/protected 修饰符, 也不经过 getter
* @param object
* @param fieldName
* @return
*/
public static Object getFieldValue(Object object, String fieldName){
Field field = getDeclaredField(object, fieldName);
if (field == null)
throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + object + "]");
makeAccessible(field);
Object result = null;
try {
result = field.get(object);
} catch (IllegalAccessException e) {
System.out.println("不可能抛出的异常");
}
return result;
}
}

B.利用反射赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class JDBCTools {
/**
* 通用的查询方法,SQL为select类型.
* 查询结果最多是一行记录.
* @param clazz
* @param sql
* @param args
* @return
*/
public static <T> T get(Class<T> clazz,String sql,Object ... args) {
//由于不能确定返回值,利用泛型实现,通过传过来的clazz参数确定返回值
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
T entity = null;
try {
conn = JDBCTools.getConnection();
ps = conn.prepareStatement(sql);
for(int i = 0;i < args.length;i ++) {
ps.setObject(i+1, args[i]);
}
rs = ps.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
//用HashMap暂时存放查询结果
//键:列的别名即对象属性名; 值:记录中对应列的值
Map<String, Object> result = new HashMap<>();
if(rs.next()) {
for (int i = 0; i < rsmd.getColumnCount(); i++) {
result.put(rsmd.getColumnLabel(i+1), rs.getObject(i+1));
}
}
if(result.size()>0) {
entity = clazz.newInstance();
//利用反射创建对象
for(Map.Entry<String, Object> entry:result.entrySet()) {
//利用反射为对象的各属性赋值
String fieldName = entry.getKey();
Object value = entry.getValue();
setFieldValue(entity, fieldName, value);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCTools.release(rs, ps, conn);
}
return entity;
}
}

评论