针对返回一条记录的通用的查询操作
这里我们来写一个通用的编程,实现对数据库的查询操作
- 注意: 我们这里仅仅只对查询的结果集中只有一条记录的情况进行编写
那么在写这个通用的查询操作的过程中具体会有什么问题?
-
首先就是填充占位符的操作会有问题,这里我们要通过可变形参的长度来确定有几个字段
- 因为我们要实现统一的编程,这个时候对于查询的SQL语句我们一定是要通过形参位置传入,那么我们传入这个查询语句的时候我们就要一起传入一个可变形参 Object …args,我们传入的这个可变形参就是为了填充占位符使用,那么可变形参的长度当然就是占位符的个数,然后我们就可以使用这个占位符的长度确定填充占位符时的循环的次数,因为我们填充占位符不一定填充一个,我们要通过循环来控制次数
-
我们在获取到了结果集之后如何将结果集封装到一个类中?
-
这类又可以细分为两个问题:
-
我们如何知道查询了几个字段?
-
我们如何知道有没有对应的这几个字段的构造方法(因为我们要由这几个字段构成的结果集封装到对应的javaBean中,那么我们如果要调用构造方法进行封装,我们如何知道有没有对应的这几个属性的构造方法 —> 我们可能创建对应的JavaBean的时候并没有提供对应的这几种属性的构造方法)
- 这里其实我们不需要知道这个Java类中有没有对应形参列表的构造方法,因为这里我们要实现同一编程,我们要通过反射来将对应的结果集中的数据封装到对象中
- 我们要先创建一个对应的JavaBean的对象,然后通过结果集的元数据调用getColumnName(int columnIndex)方法获取结果集中的指定的字段名,我们获得到指定的字段名之后就可以通过对应的JavaBean的Class实例(也就是类名.class)调用getDeclaredField(String fieldname)方法,获得对应的属性名的Field对象(一个java中的Field类的对象就表示java中的一个属性),获得到这个对象之后按理来说我们就要通过这个Field对象去给指定对象的此属性赋值了,但是这个时候其实不对,我们并不知道这个类对应的这个属性是不是私有的,如果这个属性是私有的那么我们就没有权限来给这个属性赋值,因为私有属性的操作只能是在本类中
- 所以这个时候我们就要通过这个Field类的对象调用setAccessible(boolean b)方法,调用这个方法,并且在setAccessible(boolean b)方法中传入参数true,执行了这样的操作之后我们获得了对这个属性的操作权限,即使是私有的属性也可以操作,然后我们再执行执行对象的该字段的赋值操作,这个setAccessible(boolean b)其实就是设置是否可以操作类中的该字段的
- 我们之后再通过这个Field类的对象去调用set(Object obj,object value)方法,通过这个方法就可以给获得Field类的运行时类的指定对象的Field对应字段赋值为value
- 所以这个时候我们就要通过这个Field类的对象调用setAccessible(boolean b)方法,调用这个方法,并且在setAccessible(boolean b)方法中传入参数true,执行了这样的操作之后我们获得了对这个属性的操作权限,即使是私有的属性也可以操作,然后我们再执行执行对象的该字段的赋值操作,这个setAccessible(boolean b)其实就是设置是否可以操作类中的该字段的
- 我们要先创建一个对应的JavaBean的对象,然后通过结果集的元数据调用getColumnName(int columnIndex)方法获取结果集中的指定的字段名,我们获得到指定的字段名之后就可以通过对应的JavaBean的Class实例(也就是类名.class)调用getDeclaredField(String fieldname)方法,获得对应的属性名的Field对象(一个java中的Field类的对象就表示java中的一个属性),获得到这个对象之后按理来说我们就要通过这个Field对象去给指定对象的此属性赋值了,但是这个时候其实不对,我们并不知道这个类对应的这个属性是不是私有的,如果这个属性是私有的那么我们就没有权限来给这个属性赋值,因为私有属性的操作只能是在本类中
- 这里其实我们不需要知道这个Java类中有没有对应形参列表的构造方法,因为这里我们要实现同一编程,我们要通过反射来将对应的结果集中的数据封装到对象中
-
-
注意: 如果我们在通用编程中遇到了getXxx()方法或者setXxx()方法,我们一般都是选择将Xxx指定为Object
具体代码如下:
package jdbc.针对返回一条记录的通用查询操作;
import com.ffyc.bean.Student;
import com.ffyc.util.JDBCUtils;
import java.io.IOException;
import java.lang.reflect.Field;
import java.sql.*;
/**
* 针对于返回的结果集中只有一条SQL语句的通用查询操作
*/
public class OneForQuery {
public static Student queryOneForQuery(String sql,Object ...args) throws SQLException, IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
//1. 获取数据库连接
Connection conn = JDBCUtils.getConnection();
//2. 预编译SQL语句
PreparedStatement ps = conn.prepareStatement(sql);
//3. 填充占位符
for(int i = 0;i < args.length;i++){
ps.setObject(i+1,args[i]);
}
//4.执行查询操作,返回一个ResultSet接口的实现类的对象 --- 也就是返回结果集
ResultSet rs = ps.executeQuery();
//5. 获取结果集对应的元数据
ResultSetMetaData rsmd = rs.getMetaData();
//通过ResultSetMetaData类的对象(也就是结果集元数据对象)调用getColumnCount()方法就可以获得结果集中查询的列数(字段数)
int columnCount = rsmd.getColumnCount();
/*
这里我们只需要执行一次next()方法,因为我们这里是针对于查询的结果集中只有一条记录的操作的通用编程,
如果结果集中有数据那么就进入了代码块中,然后将获取的值封装起来,封装到指定的JavaBean中
*/
if(rs.next()){
Student student = new Student();
//处理结果集中一行数据中的每个列
for(int i = 0;i < columnCount;i++){
//获取列值
Object columnValue = rs.getObject(i+1);
//获取每个列的列名
String columnName = rsmd.getColumnName(i+1);
//给Student类的名为student的对象的名为columnName的属性赋值为columnValue
Field field = Student.class.getDeclaredField(columnName);
field.setAccessible(true);
field.set(student,columnValue);
}
return student;
}
return null;
}
}
接下来我们进行程序的测试,测试程序是否可以实现查询结果集只有一条记录的通用编程
public class OneForQueryTest{
public static void main(String[] args) throws ClassNotFoundException, SQLException, NoSuchFieldException, IllegalAccessException, IOException {
String sql = "select no,name,sex from student where id = ?";
Student student = OneForQuery.queryOneForQuery(sql,10);
System.out.println(student);
}
}