JDBC——实现通用的查询

引言

上一篇jdbc的文章《JDBC——概述与JDBC的使用》介绍了JDBC的概念和背景知识,同时也讨论了获取数据库连接的方式,以及简单的实现了入库操作(更新、删除同理)。

本篇博客将会聚焦 PreparedStatement 的查询操作、以及 ResultSet 的结果集处理逻辑,结合 ResultSetMetaData 和反射技术实现通用的查询方法。

一、Java与SQL对应数据类型转换表

Java类型SQL类型
booleanBIT
byteTINYINT
shortSMALLINT
intINTEGER
longBIGINT
StringCHAR、VARCHAR、LONGVARCHAR
byte arrayBINARY、VAR BINARY
java.sql.DateDATE
java.sql.TimeTIME
java.sql.timestampTIMESTAMP

 

 

 

 

 

 

 

 

 

 

 

二、相关类

JDBCUtils 封装了获取连接关闭资源等通用操作(封装逻辑见《JDBC——概述与JDBC的使用》):

import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

public class JDBCUtils {
    
    public static Connection getConnection() {
        Connection connection = null;
        try {
            // 默认的识别路径就是 src 目录下
            InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
            Properties props = new Properties();
            props.load(is);
            String url = props.getProperty("url");
            String username = props.getProperty("username");
            String password = props.getProperty("password");
            String driverName = props.getProperty("driverName");
            // 加载驱动类
            Class.forName(driverName);
            // 获取连接
            connection = DriverManager.getConnection(url, username, password);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return connection;
    }

    public static void closeResource(Connection conn, Statement statement, ResultSet rs) {
        try {
            if (rs != null) {
                rs.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            if (statement != null) {
                statement.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            if (conn != null) {
                conn.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

二、查询一条记录

public static <T> T selectOne(Class<T> clazz, String sql, Object... args) {
	Connection connection = null;
	PreparedStatement ps = null;
	ResultSet rs = null;
	try {
		// 获取数据连接
		connection = JDBCUtils.getConnection();
		// 获取预编译语句对象
		ps = connection.prepareStatement(sql);
		// 填充属性值,注意下标从 1 开始
		for (int i = 0; i < args.length; i++) {
			ps.setObject(i + 1, args[i]);
		}
		// 执行查询,获取结果集
		rs = ps.executeQuery();
		// 获取结果集的元数据:ResultSetMetaData
		ResultSetMetaData rsmd = rs.getMetaData();
		// 通过元数据获取结果集中的列数
		int columnCount = rsmd.getColumnCount();
		// rs.next()方法判断是否存在下一条,相当于集合迭代器的 hasNext()
		if (rs.next()) {
			// 实体类必须包含空参构造器,才可以正常执行 newInstance()
			T t = clazz.newInstance();
			for (int i = 0; i < columnCount; i++) {
				// 获取别名(getColumnName()是获取列名,不建议使用)
				// 下标同样是从 1 开始
				String columnLabel = rsmd.getColumnLabel(i + 1);
				// 获取列值
				Object columnVal = rs.getObject(i + 1);
				// 通过反射封装对象
				Field field = clazz.getDeclaredField(columnLabel);
				field.setAccessible(true);
				field.set(t, columnVal);
			}
			return t;
		}
	} catch (Exception e) {
		e.printStackTrace();
	} finally {
		JDBCUtils.closeResource(connection, ps, rs);
	}
	return null;
}

三、查询集合

将 if (rs.next()) 换成 while(rs.next()) 即可,并通过构造一个 List 存储取得的对象元素。

总结

只要掌握了获取一条数据的通用方法,列表的处理逻辑就不在话下了。

需要关注一些重点:

1、PreparedStatement 填充占位符时,下标都是从 1 开始,通用的情况往往不知道填充的参数是何种类型,通过 setObject(..) 即可

2、与增删改不同的是,执行查询时,我们需要使用 executeQuery() 方法,并接收返回的 ResultSet 对象

3、ResultSet 并没有直接存储返回列的数量,我们需要获取到 ResultSetMetaData 获取列的数量,以及列的别名

4、rs.next() 实例方法用于判断结果集中是否存在下一条数据,它的作用相当于集合迭代器中的 hasNext() ,同时带有指针下移的操作

5、在使用 clazz.newInstance() 方法获取对象时,一定要记得实体类必须要有空参构造器

6、使用反射方法封装对象是唯一通用的方法,这样才能保证不论我们封装的是何种类型,都可以在运行时获取到对应的属性信息

 

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页