JDBC :連接數據庫;JDBC相關API ;SQL注入問題解決;調用存儲過程和函數;事務

JDBC

一、連接數據庫

//JDBC:Java爲連接數據庫提供的一套接口(規範)
//1、導入數據庫廠商提供的驅動
//	導入jar包,依賴jar包
//2、加載驅動
//驅動在5.0以上,此步驟可以省略不寫

Class.forName("com.mysql.jdbc.Driver");

//3、建立連接
//URL:統一資源定位符
//格式:"主協議:子協議://ip:端口/資源"
//本地連接:localhost:3306可以省略不寫
//		即:"jdbc:mysql:///mydb" 

String url="jdbc:mysql://localhost:3306/mydb";
String username="root";
String password="123456";
Connection conn=DriverManager.getConnection(url,username,password);
//4、獲取操作對象
Statement st=conn.createStatement();
//5、編寫SQL語句
String sql="select * from student";	//SQL語句
6、執行SQL語句
st.executeQuery(sql);//執行SQL語句
7、釋放資源
conn.close();
st.close();

二、JDBC相關類及常用方法

DriverManager //驅動管理類
	getConnection(); //試圖建立到給定數據庫 URL 的連接
Connection//接口:與特定數據庫的連接(會話)
    createStatement(String sql);//創建一個 Statement 對象來將 SQL 語句發送到數據庫
	prepareStatement(String sql);
	//創建一個 PreparedStatement 對象來將參數化的 SQL 語句發送到數據庫
	prepareStatement(String sql, int autoGeneratedKeys)
    //創建一個默認 PreparedStatement 對象,該對象能獲取自動生成的鍵。
Statement//接口:用於執行靜態 SQL 語句並返回它所生成結果的對象
    executeUpdate();//執行DML語句,返回值是影響行數
	executeQuery();//執行DQL語句,返回值是查詢的結果集
	execute();//執行任何語句
	addBatch(String sql)//將給定的 SQL 命令添加到此 Statement 對象的當前命令列表中
    executeBatch()//將一批命令提交給數據庫來執行,如果全部命令執行成功,則返回更新計數組成的數組    
ResultSet//接口
/*表示數據庫結果集的數據表,通常通過執行查詢數據庫的語句生成;
ResultSet 對象具有指向其當前數據行的光標。最初,光標被置於第一行之前。next 方法將光標移動到下一行;
因爲該方法在 ResultSet 對象沒有下一行時返回 false,所以可以在 while 循環中使用它來迭代結果集。
beforeFirst() //將光標移動到此 ResultSet 對象的開頭,正好位於第一行之前。*/
        
//遍歷取出結果集中對象
ResultSet re=st.executeQuery(sql);    
while(re.next()){
    int id=re.getInt("id");//參數爲表頭序號或者表頭字段名
    String username=re.getString(2);
    /*處理這些零碎數據的方法:
		把查詢出來的數據封裝到類裏
		再把對象存到集合裏*/
}

演示一:模擬登錄

import java.sql.*;
import java.util.Scanner;

public class Example03 {
    public static void main(String[] args) throws Exception{
        Scanner sc = new Scanner(System.in);
        System.out.println("請輸入用戶名:");
        String username = sc.nextLine();
        System.out.println("請輸入密碼:");
        String password=sc.nextLine();
        Class.forName("com.mysql.jdbc.Driver");
        String url="jdbc:mysql:///mydb";
        Connection conn = DriverManager.getConnection(url, "root", "123456");
        String sql="select * from users where username=? and password=?";
        PreparedStatement ps = conn.prepareStatement(sql);
        ps.setString(1,username);
        ps.setString(2,password);
        ResultSet resultSet = ps.executeQuery();
        //如果查到結果,說明登錄成功
        if(resultSet.next()){
            System.out.println("登錄成功!");
        }else {
            System.out.println("登錄失敗!");
        }
        ps.close();
        resultSet.close();
        conn.close();
    }
}

演示二:批量操作

//部分代碼省略
String sql="insert into demo values(?)";
        PreparedStatement ps = connection.prepareStatement(sql);
        for (int i = 1; i < 10000; i++) {
            ps.setInt(1,i);
            ps.addBatch();
        }
        ps.executeBatch();

演示三:獲取自增長鍵的值

//部分代碼省略
//要獲取自增長鍵的值,需要在獲取操作對象時聲明一個參數 Statement.RETURN_GENERATED_KEYS
PreparedStatement preparedStatement = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
//獲取自增長鍵的結果集
ResultSet generatedKeys = preparedStatement.getGeneratedKeys();
while (generatedKeys.next()){
    keyValue = generatedKeys.getInt(1);
}

三、安全問題

SQL注入:通過把SQL命令插入到Web表單提交或輸入域名或頁面請求的查詢字符串,最終達到欺騙服務器執行惡意的SQL命令。

1、SQL注入案例:使用拼串的形式寫SQL語句

//部分代碼省略
Statement st=conn.createStatement();
String name="1'or'1'='1";
"select * from student name='"+name+"'";
st.executeQuery(sql);//執行SQL語句,此語句可以查出student表中所有信息

2、防止SQL注入:使用PrepareStatement 預編譯操作對象

//部分代碼省略
//SQL語句中的參數先用 ? 佔位
String name="張三";
String sql="select * from student where name=?"
PrepareStatement ps=conn.prepareStatement(sql);
//給 ? 賦值
ps.setString(1,name);//參一爲 ? 的索引,注意索引從1開始計算,參二是 ? 的值
ps.executeQuery();	//執行SQL語句,注意不再傳入SQL語句

四、調用存儲過程和函數

1、調用存儲過程

SQL語句格式 {call <procedure-name>[(<arg1>,<arg2>, ...)]}

Java代碼:部分省略

	String sql="{call myPro(?,?)}";
    CallableStatement callableStatement = connection.prepareCall(sql);
    callableStatement.setInt(1,-1);//給第一個問號賦值
	//確定第二個問號的數據類型
    callableStatement.registerOutParameter(1, Types.VARCHAR);
    callableStatement.execute();//執行SQL語句
    String r = callableStatement.getString(2);//獲取返回值
    System.out.println(r);

myPro 存儲過程代碼

DELIMITER $$
CREATE PROCEDURE pro_testIf(IN num INT,OUT str VARCHAR(2))
BEGIN
	IF num>0 THEN
		SET str='正數';		-- 注意要用分號結束
	ELSEIF num<0 THEN        --注意elseif 連寫
		SET str='負數';            
	ELSE
		SET str='零';
	END IF;         		 --注意要結束if,要寫分號
	END $$
DELIMITER;

2、調用函數

SQL語句格式: {?= call <procedure-name>[(<arg1>,<arg2>, ...)]}

Java代碼:部分省略

	String sql="{?=call myFun(?)}";
    CallableStatement callableStatement = connection.prepareCall(sql);
    callableStatement.setInt(2,1);//給第二個 ? 賦值,刪除 id=1 的人
    callableStatement.registerOutParameter(1,Types.INTEGER);//確定第一個 ? 數據的類型
    callableStatement.execute();//執行sql語句
    int r = callableStatement.getInt(1);//獲取函數返回值
    System.out.println(r);

myFun函數代碼

create function myFun(num int)
  returns int
  BEGIN
    declare i int default 0;-- 定義i;相當於java中 //int i=0;
    delete from mydemo where id=num;-- 刪除id=1的人
    select count(*) from mydemo into i;-- 統計剩餘人數,賦給i
    return i;-- 返回i
    END;

表格

在這裏插入圖片描述

五、事務

概述:事務是指一組最小邏輯操作單元,裏面有多個操作組成。組成事務的每一部分必須要同時提交成功,如果有一個操作失敗,整個操作就回滾。

1、事務四大特性(ACID):

原子性(Atomicity):原子性是指事務是一個不可分割的工作單位,事務中的操作要麼都發生,要麼都不發生。

一致性(Consistency):事務必須使數據庫從一個一致性狀態變換到另外一個一致性狀態。

隔離性(Isolation):
事務的隔離性是多個用戶併發訪問數據庫時,數據庫爲每一個用戶開啓的事務,不能被其他事務的操作數據所幹擾,多個併發事務之間相互隔離。

持久性(Durability):持久性是指一個事務一旦被提交,它對數據庫中數據的改變就是永久性的,接下來即使數據庫發生故障也不應該對其有任何影響。

2、事務的提交

默認情況下,Connection 對象處於自動提交模式,這意味着它在執行每個語句後都會自動提交更改,即它的所有 SQL 語句將被執行並作爲單個事務提交。
如果禁用了自動提交模式,那麼它的 SQL 語句將聚集到事務中,要提交更改就必須顯式調用 commit或rollback 方法,否則無法保存數據庫更改。
void setAutoCommit ( boolean autoCommit) //將此連接的自動提交模式設置爲給定狀態。
rollback()//取消在當前事務中進行的所有更改,並釋放此 Connection 對象當前持有的所有數據庫鎖。
commit()//使所有上一次提交/回滾後進行的更改成爲持久更改,並釋放此 Connection 對象當前持有的所有數據庫鎖。
setSavepoint()//在當前事務中創建一個未命名的保存點 (savepoint),並返回表示它的新 Savepoint 對象。

示例:簡易模擬轉賬

import utils.JDBC_Utils;
import java.sql.*;

public class Example00 {
    public static void main(String[] args){
        Connection conn=null;
        PreparedStatement statement1=null;
        PreparedStatement statement2=null;
        PreparedStatement statement3=null;
        PreparedStatement statement4=null;
        Savepoint savepoint=null;
        try {
            conn = JDBC_Utils.getConnection();//獲取Connection對象方法,篇幅有限,不作展示
            conn.setAutoCommit(false);//將conn 自動提交設置爲手動提交
            //模擬第一次轉賬
            String sql1 = "update bank set money=money-1000 where username='zhangsan'";
            statement1 = conn.prepareStatement(sql1);
            statement1.executeUpdate();
            //System.out.println(1/0);
            String sql2="update bank set money=money+1000 where username='lisi'";
            statement2 = conn.prepareStatement(sql2);
            statement2.executeUpdate();
            //將以上兩個操作看做一個事務,只有這兩操作都完成纔會提交保存,否則回滾至初始狀態
            savepoint = conn.setSavepoint();//創建一個保存點,可以指定回滾至此狀態

            //模擬第二次轉賬,假設在此過程出現異常
            String sql3 = "update bank set money=money-1000 where username='zhangsan'";
            statement3 = conn.prepareStatement(sql3);
            statement3.executeUpdate();
            System.out.println(1/0);//模擬轉賬過程中的異常
            String sql4="update bank set money=money+1000 where username='lisi'";
            statement4 = conn.prepareStatement(sql4);
            statement4.executeUpdate();
            //將以上兩個操作看做第二個事務
        } catch (Exception e) {
            try {
                if (savepoint != null) {
                    //如果savepoint不爲null且出現異常,說明第一次轉賬成功,第二次失敗,此時只需回滾至指定保存點即可
                    conn.rollback(savepoint);
                }else {
                    //如果savepoint爲null,說明第一次轉賬異常,則回滾至初始狀態,即第一次之前
                    conn.rollback();
                }
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            try {
                conn.commit();//手動提交事務,並釋放Conn 對象當前持有的所有數據庫鎖
                conn.close();
                if (statement1 != null) {
                    statement1.close();
                }
                if (statement2 != null) {
                    statement2.close();
                }
                if (statement3 != null) {
                    statement3.close();
                }
                if (statement4 != null) {
                    statement4.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

3、事務的隔離級別

1、Read uncommitted 讀未提交

當隔離級別設置爲Read uncommitted時,就可能出現髒讀。

2、Read committed 讀提交 ==>(Oracle默認級別)

當隔離級別設置爲Read committed時,避免了髒讀,但是可能會造成不可重複讀。

3、Repeatable read 重複讀 ==>(MySQL默認級別)

當隔離級別設置爲Repeatable read時,可避免髒讀、不可重複讀的發生,但可能會出現幻讀(錯誤讀取)。爲此級別。

4、Serializable 串行化

最高級別,可以避免所有問題,但效率很低,一般不使用

四種隔離級別的效率:

​ read uncommitted>read committed>repeatable read>serializable

四種隔離級別的安全性:

​ read uncommitted<read committed<repeatable read<serializable

設置查看事務的隔離級別

將數據庫的隔離級別設置成 讀未提交
	set session transaction isolation level read uncommitted;
查看數據庫的隔離級別
	select @@tx_isolation;
java中控制隔離級別: Connection
	void setTransactionIsolation(int level) //level是常量
發佈了55 篇原創文章 · 獲贊 31 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章