JDBC
一、引言
1.1 如何操作數據
使用客戶端工具訪問數據庫,需要手工建立鏈接,輸入用戶名和密碼登錄,編寫SQL語句,點擊執行,查看操作結果(結果集或受影響行數)。
1.2 實際開發中,會採用客戶端操作數據庫嗎?
在實際開發過程中,當用戶的數據發生改變時,不可能通過客戶端操作執行SQL語句,因爲操作量過大,無法保證效率和正確性
二、JDBC(Java DataBase Connectivity)
2.1 什麼是JDBC?
JDBC(Java DataBase Connectivity) Java連接數據庫,可以使用Java語言連接數據庫完成CRUD操作
2.2 JDBC核心思想
Java中定義了訪問數據庫的接口,可以爲多種關係型數據庫提供統一的訪問方式。
由數據庫廠商提供驅動實現類(Driver數據庫驅動)
2.3 JDBC API
JDBC 是由多個接口和類進行功能實現
類型 | 全限定名 | 簡介 |
---|---|---|
class | java.sql.DriverManager | 管理多個數據庫驅動類,提供了獲取數據庫連接的方法 |
interface | java.sql.Connection | 代表一個數據庫連接(當Connection不是NULL時,表示已連接一個數據庫) |
interface | java.sql.Statement | 發送SQL語句到數據庫的工具 |
interface | java.sql.ResultSet | 保存SQL查詢語句的結果數據(結果集) |
class | java.sql.SQLException | 處理數據庫應用程序時所發生的異常 |
2.4 環境搭建
- 在項目下新建 lib 文件夾,用於存放 jar 文件
- 將MySQL驅動文件mysql-connector-java-5.1.25-bin.jar 複製到項目的lib文件夾中
- 選中lib文件夾 右鍵選擇 add as library,點擊OK
三、JDBC開發步驟
3.1 註冊驅動
使用Class.forName(“com.mysql.jdbc.Driver”); 手動加載字節碼文件到JVM中
Class.forName("com.mysql.jdbc.Driver");
3.2 連接數據庫
- 通過DriverManager.getConnection(url,user,password);獲得數據庫連接對象
- URL:jdbc:mysql://localhost:3306/database
- user:root
- password:1234
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/database
?useUnicode=true&characterEncoding=utf8","root","1234");
- URL(Uniform Resource Locator)統一資源定位符:由協議、IP、端口、SID(程序實例名稱)組成
3.3 獲取發送SQL的對象
通過Connection對象獲得Statement對象,用於對數據庫進行通用訪問的
Statement statement = connection.createStatement();
3.4 執行SQL語句
編寫SQL語句,並執行,接收執行後的結果
int result = statement.executeUpdate("update stu set where ");
- 注意:在編寫DML語句時,一定要注意字符串參數的符號是單引號 ‘值’
- DML語句:增、刪、改時,執行的結果是受影響行數(int類型)。
- DQL語句:查詢時,返回的是數據結果集(ResultSet結果集)
3.5 處理結果
接收並處理操作結果
if(result > 0){
System.out.println("執行成功");
}
- 受影響行數:邏輯判斷,方法返回
- 查詢結果集:迭代、依次獲取
3.6 釋放資源
遵循的是先開後關的原則,釋放過程中用到的所有資源對象
statement.close();
connection.close();
四、 ResultSet(結果集)
在執行查詢SQL後,存放查詢到的結果集數據
4.1 接收結果集
ResultSet rs = statement.executeQuery(sql)
ResultSet rs = statement.executeQuery("SELECT * FROM stu");
4.2 遍歷ResultSet中的數據
ResultSet以表(Table)結構進行臨時結果的存儲,需要通過JDBC API將其中的數據進行依次獲取
-
數據行指針:初始位置在第一行數據前,每調用一次boolean next()方法,ResultSet中指針向下移動一行,結果爲true,表示當前行有數據
-
rs.getXxx(“列名”); 根據列名獲得數據
-
rs.getXxx(整數下標); 代表根據列的編號順序獲得!從1開始
boolean next() throws SQLException;//判斷rs結果集中下一行是否有數據
4.2.1 遍歷方法
int getInt(int columnIndex) throws SQLException;//獲得當前行的第N列的int值
int getInt(String columnLabel) throws SQLException;//獲得當前行columnLabel列的int值
注意:列的編號從1開始
4.3 綜合案例
對stu表所有的數據進行遍歷
4.3.1 根據列的名稱獲取
public class Test {
public static void main(String[] args) throws Exception{
//1.註冊驅動
Class.forName("com.mysql.jdbc.Driver");
//2.獲得連接
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/companydb?useUnicode=true&characterEncoding=utf8","root","root");
//3.獲取執行SQL的對象
Statement statement = connection.createStatement();
//4.編寫SQL語句
String sql = "select student_id,student_name,sex,birthday,phone,GradeId from stu;";
ResultSet resultSet = statement.executeQuery(sql);
//5.處理結果 (結果集!)
while(resultSet.next()){//判斷結果集中是否有下一行!
//根據列名獲取當前行每一列的數據
String student_id = resultSet.getString("student_id");
String student_name = resultSet.getString("student_name");
String sex = resultSet.getString("sex");
String birthday = resultSet.getString("birthday");
String phone = resultSet.getString("phone");
int gradeId = resultSet.getInt("gradeId");
System.out.println(student_id+"\t"+student_name+"\t"+sex+"\t"+birthday+"\t"+phone+"\t"+gradeId);
}
//6.釋放資源
resultSet.close();
statement.close();
connection.close();
}
}
4.3.2 根據列的下標獲取
//5.處理結果 (結果集!)
while(resultSet.next()){//判斷結果集中是否有下一行!
//根據列的編號獲取當前行每一列的數據
String student_id = resultSet.getString(1);
String student_name = resultSet.getString(2);
String sex = resultSet.getString(3);
String birthday = resultSet.getString(4);
String phone= resultSet.getString(5);
int gradeId = resultSet.getInt(6);
System.out.println(student_id+"\t"+student_name+"\t"+sex+"\t"+birthday+"\t"+phone+"\t"+gradeId);
}
五、常見錯誤
- java.lang.ClassNotFoundException 找不到類(類名書寫錯誤、沒有導入jar包)
- com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException 與SQL語句相關的錯誤(表名列名書寫錯誤、約束錯誤、插入的值是String類型,但是沒有加單引號)建議:在客戶端工具中測試sql語句後,再粘貼到代碼中來
- com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry ‘S1003’ for key ‘PRIMARY’ 原因:主鍵值已存在!更改要插入的主鍵值
- com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException:Unknown column ‘password’ in
- 可能輸入的值的類型不對,確定插入元素時,對應的值的類型是否爭取
六、SQL注入問題
6.1 什麼是SQL注入
當用戶輸入的數據中有SQL關鍵字或語法時,並且參與了SQL語句的編譯,導致SQL語句編譯後條件結果爲true,一直得到正確的結果。稱爲SQL注入
6.2如何避免SQL注入
由於編寫的SQL語句,是在用戶輸入數據後,整合後再編譯成SQL語句。所以爲了避免SQL注入的問題,使SQL語句在用戶輸入數據前,SQL語句已經完成編譯,成爲了完整的SQL語句,再進行填充數據
七、 PreparedStatement
PreparedStatement接口繼承了Statement接口。執行SQL語句的方法沒有區別。
7.1 PreparedStatement的應用
作用:1.預編譯SQL語句,效率高!
2.安全,避免SQL注入
3.可以動態的填充數據,執行多個同構的SQL語句
7.1.1 參數標記
//1.預編譯SQL語句
PreparedStatement pstmt = connection.prepareStatement(sql);
注意:PreparedStatement應用時,SQL字符串的參數都由?符號站位,被稱爲參數標記。在執行該SQL語句前,要爲每個?參數賦值
7.1.2 動態參數綁定
pstmt.setXxx(下標,值); 參數下標是從1開始,爲指定佔位符下標綁定值
//2.爲佔位符下標賦值
pstmt.setString(1,username);
pstmt.setString(2,password);