前言
本文是對JDBC的操作資料庫實作增刪改查操作的簡單介紹,在做這個之前要先配好驅動哦,可以參考我上一篇文章: JDBC初體驗_秋雨綿綿-CSDN博客

目錄
一、操作和訪問資料庫
二、PreparedStatement的使用
2.1、PreparedStatement的介紹
2.2、使用PreparedStatement的好處
2.3、java與SQL對應資料型別轉換表
2.4、使用PreparedStatement實作增、刪、改操作
2.5、使用PreparedStatement實作查詢操作
三、ResultSet與ResultSetMetaData
3.1、ResultSet
3.2、ResultMetaData
一、操作和訪問資料庫
資料庫連接被用于向資料庫服務器發送命令和 SQL 陳述句,并接受資料庫服務器回傳的結果,其實一個資料庫連接就是一個Socket連接,
在 java.sql 包中有 3 個介面分別定義了對資料庫的呼叫的不同方式:
Statement:用于執行靜態 SQL 陳述句并回傳它所生成結果的物件,
PrepatedStatement:SQL 陳述句被預編譯并存盤在此物件中,可以使用此物件多次高效地執行該陳述句,
CallableStatement:用于執行 SQL 存盤程序
這三個介面第一個介面只做了解,因為使用他有幾個弊端
-問題一:存在拼串操作,繁瑣
- 問題二:存在SQL注入問題
SQL 注入是利用某些系統沒有對用戶輸入的資料進行充分的檢查,而在用戶輸入資料中注入非法的 SQL 陳述句段或命令
例如:
SELECT user, password FROM user_table WHERE user='a' OR 1 = ' AND password = ' OR '1' = '1'
該陳述句就會使得用戶密碼即使不正確也能登入,大家可以試試
那么解決辦法就是采用PreparedStatement介面來做,
那么使用Statement的其他問題,如下方代碼所示
public class StatementTest {
// 使用Statement的弊端:需要拼寫sql陳述句,并且存在SQL注入的問題
@Test
public void testLogin() {
Scanner scan = new Scanner(System.in);
System.out.print("用戶名:");
String userName = scan.nextLine();
System.out.print("密 碼:");
String password = scan.nextLine();
// SELECT user,password FROM user_table WHERE USER = '1' or ' AND PASSWORD = '='1' or '1' = '1';
String sql = "SELECT user,password FROM user_table WHERE USER = '" + userName + "' AND PASSWORD = '" + password
+ "'";
User user = get(sql, User.class);
if (user != null) {
System.out.println("登陸成功!");
} else {
System.out.println("用戶名或密碼錯誤!");
}
}
// 使用Statement實作對資料表的查詢操作
public <T> T get(String sql, Class<T> clazz) {
T t = null;
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
// 1.加載組態檔
InputStream is = StatementTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties pros = new Properties();
pros.load(is);
// 2.讀取配置資訊
String user = pros.getProperty("user");
String password = pros.getProperty("password");
String url = pros.getProperty("url");
String driverClass = pros.getProperty("driverClass");
// 3.加載驅動
Class.forName(driverClass);
// 4.獲取連接
conn = DriverManager.getConnection(url, user, password);
st = conn.createStatement();
rs = st.executeQuery(sql);
// 獲取結果集的元資料
ResultSetMetaData rsmd = rs.getMetaData();
// 獲取結果集的列數
int columnCount = rsmd.getColumnCount();
if (rs.next()) {
t = clazz.newInstance();
for (int i = 0; i < columnCount; i++) {
// //1. 獲取列的名稱
// String columnName = rsmd.getColumnName(i+1);
// 1. 獲取列的別名
String columnName = rsmd.getColumnLabel(i + 1);
// 2. 根據列名獲取對應資料表中的資料
Object columnVal = rs.getObject(columnName);
// 3. 將資料表中得到的資料,封裝進物件
Field field = clazz.getDeclaredField(columnName);
field.setAccessible(true);
field.set(t, columnVal);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 關閉資源
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (st != null) {
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return null;
}
}
二、PreparedStatement的使用
2.1、PreparedStatement的介紹
- 可以通過呼叫 Connection 物件的 **preparedStatement(String sql)** 方法獲取 PreparedStatement 物件
- PreparedStatement 介面是 Statement 的子介面,它表示一條預編譯過的 SQL 陳述句
- PreparedStatement 物件所代表的 SQL 陳述句中的引數用問號(?)來表示,呼叫 PreparedStatement 物件的 setXxx() 方法來設定這些引數. setXxx() 方法有兩個引數,第一個引數是要設定的 SQL 陳述句中的引數的索引(從 1 開始),第二個是設定的 SQL 陳述句中的引數的值
2.2、使用PreparedStatement的好處
1、提高了代碼的可讀性和可維護性,
2、PreparedStatement 能最大可能提高性能
3、PreparedStatement 可以防止 SQL 注入
2.3、java與SQL對應資料型別轉換表
| 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 |
2.4、使用PreparedStatement實作增、刪、改操作
代碼如下(自己注意導包):
/**
* @Date 2021/12/15 10:35
* @Param
* @param sql
* @param args
* @Return void
* @MetodName update
* @Author wang
* @Description:通用的資料庫增刪改操作,適用于組態檔中當前資料庫的所有表
*/
public void update(String sql,Object ...args) {
Connection conn = null;
PreparedStatement ps = null;
try {
//1.獲取資料庫的連接
conn = JdbcUtils.getConnection();
//2.預編譯SQL陳述句,回傳PreparedStatement的實體
ps = conn.prepareStatement(sql);
//3.填充占位符
for(int i = 0;i<args.length;i++) {
//注意,這里的索引是從0開始,但是資料庫中索引是從1開始,所以要加一
//args中的索引是從0開始,所以就以i開始
ps.setObject(i+1,args[i]);
}
//4.執行
ps.execute();
} catch (Exception e) {
e.printStackTrace();
} finally {
//5.資源的關閉
JdbcUtils.closeResource(conn,ps);
}
}
2.5、使用PreparedStatement實作查詢操作
代碼如下:
/**
* @Date 2021/12/16 16:55
* @Param
* @param clazz
* @param sql
* @param args
* @Return List<T>
* @MetodName getForList
* @Author wang
* @Description 查詢表通用操作,多條陳述句
*/
public <T> List<T> getForList(Class<T> clazz,String sql,Object ...args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
//獲取資料庫連接
conn = JdbcUtils.getConnection();
//預編譯SQL陳述句
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();
//獲取列數
int columnCount = rsmd.getColumnCount();
//創建集合物件
ArrayList<T> list = new ArrayList<T>();
while(rs.next()){
T t = clazz.newInstance();
for(int i= 0;i<columnCount;i++) {
//獲取每個列的列值,通過ResultSet
Object columnValue = rs.getObject(i + 1);
//獲取每個列的列名,通過ResultSetMetaData
// String columnName = rsmd.getColumnName(i + 1);
//獲取列的別名:getColumnLabel(i+1);在類的屬性名與列的欄位名不一致時使用
//推薦使用該方法,因為該方法默認就是欄位的列名,即便沒有其別名,因此推薦使用該方法
String columnLabelName = rsmd.getColumnLabel(i + 1);
//通過反射,將指定物件名columnLabelName的屬性賦值為指定的
//值columnValue
Field field = clazz.getDeclaredField(columnLabelName);
field.setAccessible(true);
field.set(t,columnValue);
}
list.add(t);
}
return list;
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtils.closeResource(conn,ps,rs);
}
return null;
}
對于以上代碼都是經過多步得來的,具體大家可以參考一些工具書或者看視頻加以理解,
三、ResultSet與ResultSetMetaData
3.1、ResultSet
查詢需要呼叫PreparedStatement 的 executeQuery() 方法,查詢結果是一個ResultSet 物件
- ResultSet 物件以邏輯表格的形式封裝了執行資料庫操作的結果集,ResultSet 介面由資料庫廠商提供實作
- ResultSet 回傳的實際上就是一張資料表,有一個指標指向資料表的第一條記錄的前面,- ResultSet 物件維護了一個指向當前資料行的游標,初始的時候,游標在第一行之前,可以通過 ResultSet 物件的 next() 方法移動到下一行,呼叫 next()方法檢測下一行是否有效,若有效,該方法回傳 true,且指標下移,相當于Iterator物件的 hasNext() 和 next() 方法的結合體,
- 當指標指向一行時, 可以通過呼叫 getXxx(int index) 或 getXxx(int columnName) 獲取每一列的值,
3.2、ResultMetaData
可用于獲取關于 ResultSet 物件中列的型別和屬性資訊的物件
用該介面可以解決一個問題就是未知的列名去給物件賦值的問題,
- getColumnName(int column):獲取指定列的名稱
- getColumnLabel(int column):獲取指定列的別名
- getColumnCount():回傳當前 ResultSet 物件中的列數,- getColumnTypeName(int column):檢索指定列的資料庫特定的型別名稱,
- getColumnDisplaySize(int column):指示指定列的最大標準寬度,以字符為單位,
- isNullable(int column):指示指定列中的值是否可以為 null,- isAutoIncrement(int column):指示是否自動為指定列進行編號,這樣這些列仍然是只讀的,
本次分享就到這里啦,本人還要繼續修煉,歡迎大家一起討論,互通有無哦,

轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/389250.html
標籤:其他
