JDBC——实现通用的查询
引言
上一篇jdbc的文章《JDBC——概述與JDBC的使用》介紹了JDBC的概念和背景知識(shí),同時(shí)也討論了獲取數(shù)據(jù)庫連接的方式,以及簡單的實(shí)現(xiàn)了入庫操作(更新、刪除同理)。
本篇博客將會(huì)聚焦 PreparedStatement 的查詢操作、以及 ResultSet 的結(jié)果集處理邏輯,結(jié)合 ResultSetMetaData 和反射技術(shù)實(shí)現(xiàn)通用的查詢方法。
一、Java與SQL對應(yīng)數(shù)據(jù)類型轉(zhuǎn)換表
| Java類型 | SQL類型 |
| boolean | BIT |
| byte | TINYINT |
| short | SMALLINT |
| int | INTEGER |
| long | BIGINT |
| String | CHAR、VARCHAR、LONGVARCHAR |
| byte array | BINARY、VAR BINARY |
| java.sql.Date | DATE |
| java.sql.Time | TIME |
| java.sql.timestamp | TIMESTAMP |
?
?
?
?
?
?
?
?
?
?
?
二、相關(guān)類
JDBCUtils 封裝了獲取連接和關(guān)閉資源等通用操作(封裝邏輯見《JDBC——概述與JDBC的使用》):
import java.io.InputStream; import java.sql.*; import java.util.Properties;public class JDBCUtils {public static Connection getConnection() {Connection connection = null;try {// 默認(rèn)的識(shí)別路徑就是 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");// 加載驅(qū)動(dòng)類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 {// 獲取數(shù)據(jù)連接connection = JDBCUtils.getConnection();// 獲取預(yù)編譯語句對象ps = connection.prepareStatement(sql);// 填充屬性值,注意下標(biāo)從 1 開始for (int i = 0; i < args.length; i++) {ps.setObject(i + 1, args[i]);}// 執(zhí)行查詢,獲取結(jié)果集rs = ps.executeQuery();// 獲取結(jié)果集的元數(shù)據(jù):ResultSetMetaDataResultSetMetaData rsmd = rs.getMetaData();// 通過元數(shù)據(jù)獲取結(jié)果集中的列數(shù)int columnCount = rsmd.getColumnCount();// rs.next()方法判斷是否存在下一條,相當(dāng)于集合迭代器的 hasNext()if (rs.next()) {// 實(shí)體類必須包含空參構(gòu)造器,才可以正常執(zhí)行 newInstance()T t = clazz.newInstance();for (int i = 0; i < columnCount; i++) {// 獲取別名(getColumnName()是獲取列名,不建議使用)// 下標(biāo)同樣是從 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())?即可,并通過構(gòu)造一個(gè) List 存儲(chǔ)取得的對象元素。
總結(jié)
只要掌握了獲取一條數(shù)據(jù)的通用方法,列表的處理邏輯就不在話下了。
需要關(guān)注一些重點(diǎn):
1、PreparedStatement 填充占位符時(shí),下標(biāo)都是從 1 開始,通用的情況往往不知道填充的參數(shù)是何種類型,通過 setObject(..) 即可
2、與增刪改不同的是,執(zhí)行查詢時(shí),我們需要使用 executeQuery() 方法,并接收返回的 ResultSet 對象
3、ResultSet 并沒有直接存儲(chǔ)返回列的數(shù)量,我們需要獲取到 ResultSetMetaData 獲取列的數(shù)量,以及列的別名
4、rs.next() 實(shí)例方法用于判斷結(jié)果集中是否存在下一條數(shù)據(jù),它的作用相當(dāng)于集合迭代器中的 hasNext() ,同時(shí)帶有指針下移的操作
5、在使用 clazz.newInstance() 方法獲取對象時(shí),一定要記得實(shí)體類必須要有空參構(gòu)造器
6、使用反射方法封裝對象是唯一通用的方法,這樣才能保證不論我們封裝的是何種類型,都可以在運(yùn)行時(shí)獲取到對應(yīng)的屬性信息
?
總結(jié)
以上是生活随笔為你收集整理的JDBC——实现通用的查询的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android OpenGL Canno
- 下一篇: js中for循环调用回调函数,一直循环最