JDBC基礎語法、statement對象和PreparedStatement對象、SQL注入及解決、數據庫連接池等詳解

一、JDBC

1、數據庫驅動

數據庫驅動是應用程序與數據庫之間溝通的橋樑,我們的程序通過數據庫驅動與數據庫打交道。

在這裏插入圖片描述

2、JDBC

JDBC(java database connectivity)驅動程序是對JDBC規範完整的實現,它的存在在JAVA程序與數據庫系統之間建立了一條通信的渠道。由數據庫廠家負責實現

需要導入數據庫驅動包 mysql-connector-java-5.1.47.jar

在這裏插入圖片描述

3、第一個JDBC程序

創建測試數據庫

CREATE DATABASE jdbcStudy CHARACTER SET utf8 COLLATE utf8_general_ci;

USE jdbcStudy;

CREATE TABLE users(   
    id INT PRIMARY KEY,    
    NAME VARCHAR(40), 
    PASSWORD VARCHAR(40),  
    email VARCHAR(60),   
    birthday DATE 
);

INSERT INTO users(id,NAME,PASSWORD,email,birthday) 
VALUES(1,'zhansan','123456','[email protected]','1980-12-04'),
(2,'lisi','123456','[email protected]','1981-12-04'), 
(3,'wangwu','123456','[email protected]','1979-12-04');

導入數據庫驅動包

  1. 創建一個項目
  2. 導入數據庫驅動包
  3. 添加依賴(add as library)

編寫測試代碼

import java.sql.*;

// 我的第一個JDBC程序 
public class JdbcFirstDemo {   
    public static void main(String[] args) throws ClassNotFoundException, SQLException {    
        //1. 加載驅動    
        Class.forName("com.mysql.jdbc.Driver"); // 固定寫法,加載驅動
        //2. 用戶信息和url    
        // useUnicode=true&characterEncoding=utf8&useSSL=true  
        String url = "jdbc:mysql://localhost:3306/jdbcstudy? useUnicode=true&characterEncoding=utf8&useSSL=true";     
        String username = "root";
        String password = "123456";    
        
        //3. 連接成功,數據庫對象  Connection 代表數據庫   
        Connection connection = DriverManager.getConnection(url, username, password);
        
        //4. 執行SQL的對象 Statement 執行sql的對象     
        Statement statement = connection.createStatement();
        
        //5. 執行SQL的對象 去 執行SQL,可能存在結果,查看返回結果  
        String sql = "SELECT * FROM users";
        
        ResultSet resultSet = statement.executeQuery(sql); //返回的結果集,結果 集中封裝了我們全部的查詢出來的結果
        
        while (resultSet.next()){   
            System.out.println("id=" + resultSet.getObject("id"));          
            System.out.println("name=" + resultSet.getObject("NAME"));   
            System.out.println("pwd=" + resultSet.getObject("PASSWORD"));   
            System.out.println("email=" + resultSet.getObject("email"));    
            System.out.println("birth=" + resultSet.getObject("birthday"));   
       }
        
        //6、釋放連接   
        resultSet.close();   
        statement.close();   
        connection.close();
    } 
}

步驟

  1. 加載驅動
  2. 連接數據庫 DriverManager
  3. 獲得執行sql的對象 Statement
  4. 獲得返回的結果集
  5. 釋放連接

DriverManager

// DriverManager.registerDriver(new com.mysql.jdbc.Driver()); 
Class.forName("com.mysql.jdbc.Driver"); // 固定寫法,加載驅動 
Connection connection = DriverManager.getConnection(url, username, password);

// connection 代表數據庫
// 數據庫設置自動提交
// 事務提交 
// 事務滾回 
connection.rollback(); 
connection.commit();
connection.setAutoCommit();

URL

String url = "jdbc:mysql://localhost:3306/jdbcstudy? useUnicode=true&characterEncoding=utf8&useSSL=true";

// mysql -- 3306 
// 協議 ://主機地址:端口號/數據庫名?參數1&參數2&參數3
// oralce -- 1521 
//jdbc:oracle:thin:@localhost:1521:sid

Statement 執行SQL 的對象 PrepareStatement 執行SQL 的對象

String sql = "SELECT * FROM users"; // 編寫SQL

statement.executeQuery(); //查詢操作返回 ResultSet 
statement.execute(); // 執行任何SQL
statement.executeUpdate();  // 更新、插入、刪除。都是用這個,返回一個受影響的行數

ResultSet 查詢的結果集:封裝了所有的查詢結果

獲得指定的數據類型

resultSet.getObject(); // 在不知道列類型的情況下使用 
// 如果知道列的類型就使用指定的類型
resultSet.getString(); 
resultSet.getInt();
resultSet.getFloat();
resultSet.getDate();
resultSet.getObject(); 
....

遍歷、指針

resultSet.beforeFirst();  // 移動到前面 
resultSet.afterLast();   // 移動到後面
resultSet.next(); //移動到下一個數據
resultSet.previous(); //移動到前一行
resultSet.absolute(row); //移動到指定行

釋放資源

resultSet.close(); 
statement.close();
connection.close(); // 耗資源,用完關掉!

4、statement對象

Jdbc中的statement對象用於向數據庫發送SQL語句,想完成對數據庫的增刪改查,只需要通過這個對象 向數據庫發送增刪改查語句即可。

Statement對象的executeUpdate方法,用於向數據庫發送增、刪、改的sql語句,executeUpdate執行 完後,將會返回一個整數(即增刪改語句導致了數據庫幾行數據發生了變化)。

Statement對象的Statement.executeQuery方法用於向數據庫發送查詢語句,executeQuery方法返回代表查詢結果的 ResultSet對象。

create操作

使用executeUpdate(String sql)方法完成數據添加操作

Statement st = conn.createStatement(); 
String sql = "insert into user(….) values(…..) ";
int num = st.executeUpdate(sql); 
if(num>0){   
    System.out.println("插入成功!!!"); 
}

delete操作

使用executeUpdate(String sql)方法完成數據刪除操作

Statement st = conn.createStatement(); 
String sql = "delete from user where id=1";
int num = st.executeUpdate(sql);
if(num>0){  
    System.out.println(“刪除成功!!!"); 
}

update操作

使用executeUpdate(String sql)方法完成數據修改操作

Statement st = conn.createStatement(); 
String sql = "update user set name='' where name=''"; 
int num = st.executeUpdate(sql); 
if(num>0){   
    System.out.println(“修改成功!!!");
}

read操作

使用executeQuery(String sql)方法完成數據查詢操作

Statement st = conn.createStatement();
String sql = "select * from user where id=1";
ResultSet rs = st.executeQuery(sql); 
while(rs.next()){   
    //根據獲取列的數據類型,分別調用rs的相應方法映射到java對象中
}

代碼實現

在 src 下建立資源文件 db.properties

driver=com.mysql.jdbc.Driver 
url=jdbc:mysql://localhost:3306/jdbcstudy? useUnicode=true&characterEncoding=utf8&useSSL=true 
username=root 
password=123456
  1. 提取連接工具類
import java.io.IOException;
import java.io.InputStream;
import java.sql.*; 
import java.util.Properties;

public class JdbcUtils {
    
    private static String driver = null; 
    private static String url = null;
    private static String username = null; 
    private static String password = null;
    
    static {      
        try{      
            InputStream in =  JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");    
            Properties properties = new Properties();   
            properties.load(in);
            
            driver = properties.getProperty("driver"); 
            url = properties.getProperty("url"); 
            username = properties.getProperty("username");  
            password = properties.getProperty("password");
            
            //1.驅動只用加載一次        
            Class.forName(driver);    
        } catch (Exception e) {  
            e.printStackTrace();   
        }
 }
    
    //獲取連接  
    public static Connection getConnection() throws SQLException {    
        return DriverManager.getConnection(url, username, password);   
    }
    
    //釋放連接資源   
    public static void release(Connection conn, Statement st, ResultSet rs){   
        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();   
            }      
        }  
    }
}
  1. 編寫增刪改的方法, executeUpdate
//導入上面的連接工具類
//插入類

import java.sql.Connection;
import java.sql.ResultSet; 
import java.sql.SQLException; 
import java.sql.Statement;

public class TestInsert {  
    public static void main(String[] args) {
        
        Connection conn = null;  
        Statement st = null;      
        ResultSet rs = null;
        
        try {
            conn = JdbcUtils.getConnection(); //獲取數據庫連接
            st = conn.createStatement(); //獲得SQL的執行對象
            
            String sql = "INSERT INTO users(id,`NAME`,`PASSWORD`,`email`,`birthday`)" +                    "VALUES(4,'kuangshen','123456','[email protected]','202001-01')";
            
            int i = st.executeUpdate(sql);   
            if (i>0){          
                System.out.println("插入成功!");   
            }
        } catch (SQLException e) {    
            e.printStackTrace();    
        } finally {        
            JdbcUtils.release(conn,st,rs);    
        }
    }
}
//導入上面的連接工具類
//刪除類

import java.sql.Connection; 
import java.sql.ResultSet;
import java.sql.SQLException; 
import java.sql.Statement;

public class TestDelete {  
    public static void main(String[] args) {
        
        Connection conn = null;    
        Statement st = null;     
        ResultSet rs = null;
        
        try {        
            conn = JdbcUtils.getConnection(); //獲取數據庫連接  
            st = conn.createStatement(); //獲得SQL的執行對象
            
            String sql = "DELETE FROM users WHERE id = 4";
            
            int i = st.executeUpdate(sql);    
            if (i>0){           
                System.out.println("刪除成功!");    
            }
        } catch (SQLException e) {    
            e.printStackTrace();   
        } finally {         
            JdbcUtils.release(conn,st,rs);   
        }
    }
}
//導入上面的連接工具類
//更新類

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class TestUpdate { 
    public static void main(String[] args) {    
        Connection conn = null;     
        Statement st = null;    
        ResultSet rs = null;
        
        try {          
            conn = JdbcUtils.getConnection(); //獲取數據庫連接  
            st = conn.createStatement(); //獲得SQL的執行對象
            
            String sql = "UPDATE users SET `NAME`='kuangshen',`email`='[email protected]' WHERE id=1";
            
            int i = st.executeUpdate(sql);     
            if (i>0){         
                System.out.println("更新成功!"); 
            }
        } catch (SQLException e) {    
            e.printStackTrace();    
        } finally {        
            JdbcUtils.release(conn,st,rs);  
        }
    } 
}
  1. 查詢executeQuery
//導入上面的連接工具類
//查詢類

import java.sql.Connection; 
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class TestSelect {   
    public static void main(String[] args) { 
        Connection conn =null;     
        Statement st = null;     
        ResultSet rs = null; 
        try {           
            conn = JdbcUtils.getConnection();  
            st = conn.createStatement();
            //SQL         
            String sql = "select * from users where id = 1";
            rs = st.executeQuery(sql); //查詢完畢會返回一個結果集 
            while (rs.next()){     
                System.out.println(rs.getString("NAME"));  
            }                  
        } catch (SQLException e) {    
            e.printStackTrace();    
        } finally {        
            JdbcUtils.release(conn,st,rs); 
        }
    } 
}

SQL注入問題

sql 存在漏洞,會被攻擊導致數據泄露,SQL會被拼接 or

import java.sql.Connection; 
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class SQLInjection {   
    public static void main(String[] args) {
        
        // login("kuangshen","123456");    
        login(" 'or '1=1"," 'or'1=1"); // 技巧
    }
    
    // 登錄業務   
    public static void login(String username,String password){
        
        Connection conn =null;    
        Statement st = null;   
        ResultSet rs = null; 
        try {        
            conn = JdbcUtils.getConnection(); 
            st = conn.createStatement();
            
            // SELECT * FROM users WHERE `Name` = 'kuangshen' AND `password` = '123456';
            // SELECT * FROM users WHERE `Name` = '' or '1=1' AND `password` = '' or '1=1';     
            String sql = "select * from users where `NAME`='"+username+"' AND `password` ='"+password+"'";
            
            rs = st.executeQuery(sql); //查詢完畢會返回一個結果集 
            while (rs.next()){    
                System.out.println(rs.getString("NAME"));  
                System.out.println(rs.getString("password")); 
            }
        } catch (SQLException e) { 
            e.printStackTrace();   
        } finally {        
            JdbcUtils.release(conn,st,rs);   
        }
    }
}

5、PreparedStatement對象

PreparedStatement 可以防止SQL注入。效率更高

import java.sql.Connection; 
import java.util.Date;
import java.sql.PreparedStatement; 
import java.sql.SQLException;

public class TestInsert {  
    public static void main(String[] args) {  
        Connection conn = null; 
        PreparedStatement st = null;
        
        try {          
            conn = JdbcUtils.getConnection();         
            // 區別        
            // 使用? 佔位符代替參數       
            String sql = "insert into users(id,`NAME`,`PASSWORD`,`email`,`birthday`) values(?,?,?,?,?)";
            
            st = conn.prepareStatement(sql); //預編譯SQL,先寫sql,然後不執行
            
            // 手動給參數賦值     
            st.setInt(1,4); //id
            st.setString(2,"zhangsan"); 
            st.setString(3,"1232112"); 
            st.setString(4,"[email protected]");  
            // 注意點: sql.Date   數據庫   java.sql.Date()  
            // util.Date  Java   new Date().getTime() 獲得時間戳 
            st.setDate(5,new java.sql.Date(new Date().getTime()));
            
            //執行       
            int i = st.executeUpdate();    
            if (i>0){          
                System.out.println("插入成功!");    
            }
        } catch (SQLException e) {  
            e.printStackTrace();  
        } finally {       
            JdbcUtils.release(conn,st,null);  
        }  
    } 
}
import java.sql.Connection; 
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Date;

public class TestDelete { 
    public static void main(String[] args) {  
        Connection conn = null;   
        PreparedStatement st = null;
        
        try {         
            conn = JdbcUtils.getConnection();    
            // 區別        
            // 使用? 佔位符代替參數      
            String sql = "delete from users where id=?";
            
            st = conn.prepareStatement(sql); //預編譯SQL,先寫sql,然後不執行
            
            // 手動給參數賦值         
            st.setInt(1,4);
            
            //執行         
            int i = st.executeUpdate();    
            if (i>0){      
                System.out.println("刪除成功!");  
            }
        } catch (SQLException e) {
             e.printStackTrace();   
        } finally {      
            JdbcUtils.release(conn,st,null);  
        }  
    }
}
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class TestUpdate { 
    public static void main(String[] args) {  
        Connection conn = null;
        PreparedStatement st = null;
        
        try {       
            conn = JdbcUtils.getConnection();  
            // 區別       
            // 使用? 佔位符代替參數      
            String sql = "update users set `NAME`=?  where id=?;";
            
            st = conn.prepareStatement(sql); //預編譯SQL,先寫sql,然後不執行
            
            // 手動給參數賦值  
            st.setString(1,"zhangsan");    
            st.setInt(2,1);
            
            //執行     
            int i = st.executeUpdate();    
            if (i>0){       
                System.out.println("更新成功!");   
            }
        } catch (SQLException e) {   
            e.printStackTrace();   
        } finally {     
            JdbcUtils.release(conn,st,null); 
        }
    }
}
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet; 
import java.sql.SQLException;

public class TestSelect { 
    public static void main(String[] args) {
        
        Connection conn = null;    
        PreparedStatement st = null;  
        ResultSet rs = null;
        
        try {         
            conn = JdbcUtils.getConnection();
            
            String sql = "select * from users where id = ?"; // 編寫SQL
            
            st = conn.prepareStatement(sql); // 預編譯
            st.setInt(1,2); //傳遞參數
            rs = st.executeQuery(); //執行
            
            if (rs.next()){   
                
                System.out.println(rs.getString("NAME"));                    }
       
        } catch (SQLException e) {   
            e.printStackTrace();
        } finally {      
            JdbcUtils.release(conn,st,rs); 
        }  
    }
}
  1. 防止SQL注入
import java.sql.*;

public class SQLInjection {    
    public static void main(String[] args) {
        
        // login("lisi","123456");   
        login("'' or 1=1","123456"); 
    }
    
    // 登錄業務
 public static void login(String username,String password){
     
     Connection conn =null;    
     PreparedStatement st = null;  
     ResultSet rs = null;  
     try {         
         conn = JdbcUtils.getConnection();   
         // PreparedStatement 防止SQL注入的本質,把傳遞進來的參數當做字符          
         // 假設其中存在轉義字符,比如說 ' 會被直接轉義 
         String sql = "select * from users where `NAME`=? and `PASSWORD`=?"; // Mybatis
         
         st = conn.prepareStatement(sql);     
         st.setString(1,username);        
         st.setString(2,password);
         rs = st.executeQuery(); //查詢完畢會返回一個結果集   
         
         while (rs.next()){     
             System.out.println(rs.getString("NAME"));     
             System.out.println(rs.getString("password"));  
         }
         
        } catch (SQLException e) {  
         e.printStackTrace();    
     } finally {     
         JdbcUtils.release(conn,st,rs);   
     }
  }
}

6、使用IDEA連接數據庫

  1. 點擊右側邊欄的database
  2. 點擊MySQL
  3. 連接成功後可以選擇數據庫
  4. 可以印行可視化操作或使用SQL命令操作

7、事務

要麼都成功、要麼都失敗

事務原則

  • 原子性:要麼全部完成,要麼都不完成

  • 一致性:總數不變

  • 隔離性:多個進程互不干擾

  • 持久性:一旦提交不可逆,持久化到數據庫了

隔離性的問題

  • 髒讀:一個事務讀取了另一個沒有提交的事務
  • 不可重複讀:在同一個事務內,重複讀取表中的數據,表數據發生了改變
  • 虛讀(幻讀):在一個事務內,讀取到了別人插入的數據,導致前後讀出來結果不一致

代碼實現

  1. 開啓事務 conn.setAutoCommit(false);
  2. 一組業務執行完畢,提交事務
  3. 可以在catch 語句中顯示的定義 回滾語句,默認失敗就會回滾
import java.sql.Connection; 
import java.sql.PreparedStatement; 
import java.sql.ResultSet;
import java.sql.SQLException;

public class TestTransaction2 { 
    public static void main(String[] args) {  
        Connection conn = null;  
        PreparedStatement st = null;   
        ResultSet rs = null;      
        
        try {      
            conn = JdbcUtils.getConnection();  
            // 關閉數據庫的自動提交,自動會開啓事務           
            conn.setAutoCommit(false); // 開啓事務
            
            String sql1 = "update account set money = money-100 where name = 'A'";       
            st = conn.prepareStatement(sql1);  
            st.executeUpdate();
            
            int x = 1/0; // 報錯
            
            String sql2 = "update account set money = money+100 where name = 'B'";        
            st = conn.prepareStatement(sql2);  
            st.executeUpdate();
            
            //業務完畢,提交事務    
            conn.commit();      
            System.out.println("成功!");
            
        } catch (SQLException e) {          
   
            // 若果失敗,則默認回滾 
            try { 
                conn.rollback();  // 如果失敗則回滾事務
            } catch (SQLException e1) {
                e1.printStackTrace(); 
            }        
            e.printStackTrace();  
        } finally {      
            JdbcUtils.release(conn,st,rs); 
        }
    } 
}

8、數據庫連接池

數據庫連接——>數據庫連接——>執行完畢——>釋放

連接——>釋放 十分浪費系統資源

池化技術:準備一些預先的資源,過來就連接預先準備好的

最小連接數: 10

最大連接數: 15

等待超時:100ms

編寫連接池:實現接口 DataSource

開源數據源實現

  • DBCP

  • C3P0

  • Druid:阿里巴巴

使用了這些數據庫連接池後,在開發中就不需要編寫連接數據庫的代碼了

DBCP

需要的Jar包

commons-dbcp-1.4 、 commons-pool-1.6

C3P0

需要的Jar 包
c3p0-0.9.5.5、mchange-commons-java-0.2.19

不管什麼數據源,本質都是實現DataSource接口,方法一樣

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章