1. 程式人生 > 實用技巧 >JDBC-Java語言操作資料庫

JDBC-Java語言操作資料庫

目錄

一、介紹

概念:Java DataBase Connectivity (JDBC,Java資料庫連線,即使用Java語言操作資料庫)

本質:官方(sun公司)定義的一套操作所有關係型資料庫的規則,即介面。各個資料庫廠商實現這套介面,提供資料庫驅動jar包。使用JDBC介面程式設計,真正執行的程式碼是驅動jar包中的實現類。

快速入門步驟

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;

public class JdbcQuickStart {
    public static void main(String[] args) throws Exception {
        //1. 匯入驅動jar包【mysql-connector-java-5.1.37-bin.jar】
            // * 將jar包複製到專案的libs目錄(自己建立的)
            //* 在該目錄上右鍵 --> Add As Library (將 jar 包加入到專案中)
        //2. 註冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        //3. 獲取資料庫連線物件 Connection
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db3", "root","mysql");
        //4. 定義sql(sql語句後面不要加分號)
        String sql = "update account set balance = 500 where id = 1";
        //5. 獲取執行sql語句的物件 Statement
        Statement statement = conn.createStatement();
        //6. 執行sql,接受返回結果
        int count = statement.executeUpdate(sql);
        //7. 處理結果
        System.out.println(count);  // 1
        //8. 釋放資源
        statement.close();
        conn.close();
    }
}

二、JDBC各個類詳解

2.1 DriverManager 驅動管理物件

1)註冊驅動:告訴程式該使用哪一個資料庫驅動 jar 包

static void registerDriver(Driver driver) :向 DriverManager 註冊給定驅動程式。
寫程式碼時使用:Class.forName("com.mysql.jdbc.Driver");

通過檢視原始碼發現,在 com.mysql.jdbc.Driver 類中存在靜態程式碼塊:

public class Driver implements java.sql.Driver {

      public Driver() throws SQLException {
      }

      static {
            try {
                  DriverManager.registerDriver(new Driver()); //註冊資料庫驅動
            } catch (SQLException var1) {
                  throw new RuntimeException("Can't register driver!");
            }
      }
}

注:在MySQL 5 版本後,可以省略註冊驅動的步驟,即不使用程式碼 Class.forName("com.mysql.jdbc.Driver");,其可以自動註冊驅動。

2)獲取資料庫的連線

static Connection getConnection(String url, String user, String password) :試圖建立到給定資料庫 URL 的連線。

  • String url:指定連線的路徑,語法 jdbc:mysql://ip地址(域名):埠號/資料庫
    如果連線的是本機 mysql 伺服器,並且 mysql 伺服器的預設埠是 3306,則 url 可以簡化為jdbc:mysql:///資料庫

  • String user:使用者名稱

  • String password:密碼

2.2 Connection 資料庫連線物件

1)獲取執行 sql 的物件

Statement createStatement() :建立一個 Statement 物件來將 SQL 語句傳送到資料庫。

PreparedStatement prepareStatement(String sql) :建立一個 PreparedStatement 物件來將引數化的 SQL 語句傳送到資料庫。

2)管理事務

開啟事務

void setAutoCommit(boolean autoCommit) 呼叫該方法設定引數為false,即開啟事務。

提交事務

void commit() 使所有上一次提交/回滾後進行的更改成為持久更改,並釋放此 Connection 物件當前持有的所有資料庫鎖。

回滾事務

void rollback() 取消在當前事務中進行的所有更改,並釋放此 Connection 物件當前持有的所有資料庫鎖。

2.3 Statement 執行靜態 SQL 語句

用於執行【靜態 SQL 語句】並返回它所生成結果的物件。

靜態 SQL 語句:引數都是給定值。

執行 sql 的三種方法

boolean execute(String sql) 可以執行任意的 sql,該語句可能返回多個結果。(瞭解,用的不多)

  • 如果第一個結果為 ResultSet 物件,則返回 true;如果其為更新計數或者不存在任何結果,則返回 false。

int executeUpdate(String sql) 執行 DML(INSERT、UPDATE、DELETE) 語句、DDL(CREATE、ALTER、DROP)語句

  • 返回值:影響的行數。可以通過影響的行數判斷,增刪改查的語句是否執行成功。返回值大於0,則執行成功。
  • DDL 語句一般不用此方法,直接使用 mysql 語句執行,因為使用該語句時,一般已經連線了某一資料庫,對資料庫/表的增刪改查不方便用此語句。

ResultSet executeQuery(String sql) 執行給定的 DQL(SELECT) 語句,該語句返回單個 ResultSet 物件。

練習

練習一:account表 新增記錄【insert 語句】

package cn.itcast.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

// account表 新增一條記錄 insert 語句
public class JDBCDemo2 {

public static void main(String[] args) {
    Statement stmt = null;
    Connection conn = null;
    try {
        //1. 註冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        //2. 定義sql
        String sql = "insert into account values(null,'王五',3000)";
        //3.獲取Connection物件
        conn = DriverManager.getConnection("jdbc:mysql:///db3", "root", "root");
        //4.獲取執行sql的物件 Statement
        stmt = conn.createStatement();
        //5.執行sql
        int count = stmt.executeUpdate(sql);//影響的行數
        //6.處理結果
        System.out.println(count);
        if(count > 0){
            System.out.println("新增成功!");
        }else{
            System.out.println("新增失敗!");
        }
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (SQLException e) {
        e.printStackTrace();
    }finally {
        //stmt.close();  有可能在conn階段出現異常,stmt未被賦值,呼叫close()造成空指標異常
        //7. 釋放資源
        //避免空指標異常
        if(stmt != null){
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

}

練習二:account表 修改記錄【update 語句】

package cn.itcast.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

// account表 修改記錄
public class JDBCDemo3 {

public static void main(String[] args) {
    Connection conn = null;
    Statement stmt = null;
    try {
        //1. 註冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        //2.獲取連線物件
        conn = DriverManager.getConnection("jdbc:mysql:///db3", "root", "root");
        //3.定義sql
        String sql  = "update account set balance = 1500 where id = 3";
        //4.獲取執行sql物件
        stmt = conn.createStatement();
        //5.執行sql
        int count = stmt.executeUpdate(sql);
        //6.處理結果
        System.out.println(count);
        if(count > 0){
            System.out.println("修改成功!");
        }else{
            System.out.println("修改失敗");
        }
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        //7.釋放資源
        if(stmt != null){
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

}

練習三:account表 刪除記錄【delete 語句】

package cn.itcast.jdbc;
import cn.itcast.util.JDBCUtils;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

// account表 刪除一條記錄
public class JDBCDemo4 {

public static void main(String[] args) {
    Connection conn = null;
    Statement stmt = null;
    try {
        //1. 註冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        //2.獲取連線物件
        conn = DriverManager.getConnection("jdbc:mysql:///db3", "root", "root");
       //conn = JDBCUtils.getConnection("jdbc:mysql:///db3", "root", "root");
        //3.定義sql
        String sql  = "delete from account where id = 3";
        //4.獲取執行sql物件
        stmt = conn.createStatement();
        //5.執行sql
        int count = stmt.executeUpdate(sql);
        //6.處理結果
        System.out.println(count);
        if(count > 0){
            System.out.println("刪除成功!");
        }else{
            System.out.println("刪除失敗");
        }
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        //7.釋放資源
        if(stmt != null){
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

}

執行DDL語句 update建立表

package cn.itcast.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

// 執行DDL語句
public class JDBCDemo5 {

public static void main(String[] args) {
    Connection conn = null;
    Statement stmt = null;
    try {
        //1. 註冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        //2.獲取連線物件
        conn = DriverManager.getConnection("jdbc:mysql:///db3", "root", "root");
        //3.定義sql
        String sql  = "create table student (id int , name varchar(20))";
        //4.獲取執行sql物件
        stmt = conn.createStatement();
        //5.執行sql
        int count = stmt.executeUpdate(sql);
        //6.處理結果
        System.out.println(count);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        //7.釋放資源
        if(stmt != null){
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

}

2.4 ResultSet 結果集物件,封裝查詢的結果

游標初始指向第一行(無資料可獲取),呼叫next()函式指向下一行,不斷獲取資料。每次只能讀取一行的一列資料。

基本使用

1)boolean next() 將游標從當前位置向下移一行,並判斷當前行是否是最後一行之後(是否有資料)。如果是,返回false,說明沒有資料了。如果不是,返回true,說明有資料。

2)getXXX(引數) 獲取資料,其中XXX為根據實際情況的資料型別。

  // 以 Java 程式語言中 int 的形式獲取此 ResultSet 物件的當前行中指定列的值。 

  int getInt(int columnIndex) // 引數代表列的編號,從 1 開始,如 getInt(1);

  int getInt(String columnLabel) // 引數為列名稱,如 getInt("balance"); 

  // 以 Java 程式語言中 String 的形式獲取此 ResultSet 物件的當前行中指定列的值。 

  String getNString(int columnIndex) 

  String getNString(String columnLabel) 

3)ResultSet 結果集也是資源,使用後需要釋放。

一個基本使用示例

package cn.itcast.jdbc;
import java.sql.*;

public class JDBCDemo6 {

public static void main(String[] args) {
    Connection conn = null;
    Statement stmt = null;
    ResultSet rs = null;
    try {
        //1. 註冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        //2.獲取連線物件
        conn = DriverManager.getConnection("jdbc:mysql:///db3", "root", "root");
        //3.定義sql
        String sql  = "select * from account";
        //4.獲取執行sql物件
        stmt = conn.createStatement();
        //5.執行sql
        rs = stmt.executeQuery(sql);
        //6.處理結果
        //6.1 讓遊標向下移動一行
        rs.next();
        //6.2 獲取資料
        int id = rs.getInt(1);
        String name = rs.getString("name");
        double balance = rs.getDouble(3);
        System.out.println(id + "---" + name + "---" + balance);
        //6.1 讓遊標向下移動一行
        rs.next();
        //6.2 獲取資料
        int id2 = rs.getInt(1);
        String name2 = rs.getString("name");
        double balance2 = rs.getDouble(3);
        System.out.println(id2 + "---" + name2 + "---" + balance2);    
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        //7.釋放資源
        if(rs != null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(stmt != null){
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

}

存在問題:上述讀取資料,是基於已知資料庫該表中有兩條 資料,但對於未知資料集,讀取資料時要判斷資料是否存在。

使用步驟:

  1. 遊標向下移動一行
  2. 判斷是否有資料
  3. 獲取資料
while(rs.next()){
      int id = rs.getInt(1);
      String name = rs.getString("name");
      double balance = rs.getDouble(3);

      System.out.println(id + "---" + name + "---" + balance);
}

使用例項

emp 表結構

emp 表資料

定義一個方法,查詢 emp 表的資料,將其封裝為物件,然後裝載在集合中,返回,列印。

  1. 定義 Emp 類
  2. 定義方法 public List findAll(){}
  3. 實現方法
定義 Emp 類

package cn.itcast.domain;
import java.util.Date;

//封裝Emp表資料的JavaBean
public class Emp {

private int id;
private String ename;
private int job_id;
private int mgr;
private Date joindate;
private double salary;
private double bonus;
private int dept_id;


public int getId() {
    return id;
}

public void setId(int id) {
    this.id = id;
}

public String getEname() {
    return ename;
}

public void setEname(String ename) {
    this.ename = ename;
}

public int getJob_id() {
    return job_id;
}

public void setJob_id(int job_id) {
    this.job_id = job_id;
}

public int getMgr() {
    return mgr;
}

public void setMgr(int mgr) {
    this.mgr = mgr;
}

public Date getJoindate() {
    return joindate;
}

public void setJoindate(Date joindate) {
    this.joindate = joindate;
}

public double getSalary() {
    return salary;
}

public void setSalary(double salary) {
    this.salary = salary;
}


public int getDept_id() {
    return dept_id;
}

public void setDept_id(int dept_id) {
    this.dept_id = dept_id;
}


public double getBonus() {
    return bonus;
}

public void setBonus(double bonus) {
    this.bonus = bonus;
}

@Override
public String toString() {
    return "Emp{" +
            "id=" + id +
            ", ename='" + ename + '\'' +
            ", job_id=" + job_id +
            ", mgr=" + mgr +
            ", joindate=" + joindate +
            ", salary=" + salary +
            ", bonus=" + bonus +
            ", dept_id=" + dept_id +
            '}';
}

}

使用例項

package cn.itcast.jdbc;
import cn.itcast.domain.Emp;
import cn.itcast.util.JDBCUtils;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

//定義一個方法,查詢emp表的資料將其封裝為物件,然後裝載集合,返回。
public class JDBCDemo8 {

public static void main(String[] args) {
    List<Emp> list = new JDBCDemo8().findAll();
    System.out.println(list);
    System.out.println(list.size());
}

public List<Emp> findAll(){
    Connection conn = null;
    Statement stmt = null;
    ResultSet rs = null;
    List<Emp> list = null;
    try {
        //1.註冊驅動
        Class.forName("com.mysql.jdbc.Driver");
        //2.獲取連線
        conn = DriverManager.getConnection("jdbc:mysql:///db3", "root", "root");
        //3.定義sql
        String sql = "select * from emp";
        //4.獲取執行sql的物件
        stmt = conn.createStatement();
        //5.執行sql
        rs = stmt.executeQuery(sql);
        //6.遍歷結果集,封裝物件,裝載集合
        Emp emp = null;
        list = new ArrayList<Emp>();
        while(rs.next()){
            //獲取資料
            int id = rs.getInt("id");
            String ename = rs.getString("ename");
            int job_id = rs.getInt("job_id");
            int mgr = rs.getInt("mgr");
            Date joindate = rs.getDate("joindate");
            double salary = rs.getDouble("salary");
            double bonus = rs.getDouble("bonus");
            int dept_id = rs.getInt("dept_id");
            // 建立emp物件,並賦值
            emp = new Emp();
            emp.setId(id);
            emp.setEname(ename);
            emp.setJob_id(job_id);
            emp.setMgr(mgr);
            emp.setJoindate(joindate);
            emp.setSalary(salary);
            emp.setBonus(bonus);
            emp.setDept_id(dept_id);

            //裝載集合
            list.add(emp);
        }

    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (SQLException e) {
        e.printStackTrace();
    }finally {
        if(rs != null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if(stmt != null){
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if(conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    return list;
}

}

2.5 抽取 JDBC 工具類:JDBCUtils

目的:程式碼重複度太高(註冊驅動、釋放資源等),通過抽取工作類簡化書寫。

分析

  1. 抽取註冊驅動方法

  2. 抽取一個方法獲得連線物件
    需求:不想傳遞引數(太麻煩),不能寫死(保證工具類的通用性)
    解決:配置檔案

    // jdbc.properties
    
    url=jdbc:mysql:///db3
    user=root
    password=root
    driver=com.mysql.jdbc.Driver
    
  3. 抽取一個方法釋放資源

工具類特點:所有方法是靜態的,方便呼叫。

靜態程式碼塊
靜態程式碼塊隨著類的載入而載入,只會執行一次。
只有靜態變數才能被靜態方法訪問,被靜態程式碼塊訪問。
靜態程式碼塊只能處理異常,不能丟擲異常。丟擲異常需要方法。

JDBC工具類 JDBCUtils

package cn.itcast.util;

import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.sql.*;
import java.util.Properties;

// JDBC工具類
public class JDBCUtils {
private static String url;
private static String user;
private static String password;
private static String driver;

// 檔案的讀取,只需要讀取一次即可拿到這些值。使用靜態程式碼塊
static{
    //讀取資原始檔,獲取值。
    try {
        //1. 建立Properties集合類。
        Properties pro = new Properties();

        //獲取src路徑下的檔案的方式--->ClassLoader 類載入器
        ClassLoader classLoader = JDBCUtils.class.getClassLoader();
        URL res  = classLoader.getResource("jdbc.properties");
        String path = res.getPath();
        // System.out.println(path);///D:/IdeaProjects/itcast/out/production/day04_jdbc/jdbc.properties
	   
        //2. 載入檔案
        pro.load(new FileReader(path));

        //3. 獲取資料,賦值
        url = pro.getProperty("url");
        user = pro.getProperty("user");
        password = pro.getProperty("password");
        driver = pro.getProperty("driver");
        //4. 註冊驅動
        Class.forName(driver);
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}

// 獲取連線
public static Connection getConnection() throws SQLException {

    return DriverManager.getConnection(url, user, password);
}

// 釋放資源
public static void close(Statement stmt,Connection conn){
    if( stmt != null){
        try {
            stmt.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    if( conn != null){
        try {
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}


// 釋放資源
public static void close(ResultSet rs,Statement stmt, Connection conn){
    if( rs != null){
        try {
            rs.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    if( stmt != null){
        try {
            stmt.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    if( conn != null){
        try {
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

}

使用示例

package cn.itcast.jdbc;
import cn.itcast.domain.Emp;
import cn.itcast.util.JDBCUtils;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

//定義一個方法,查詢emp表的資料將其封裝為物件,然後裝載集合,返回。
public class JDBCDemo8 {

public static void main(String[] args) {
    List<Emp> list = new JDBCDemo8().findAll2();
    System.out.println(list);
    System.out.println(list.size());
}

// 演示JDBC工具類
public List<Emp> findAll2(){
    Connection conn = null;
    Statement stmt = null;
    ResultSet rs = null;
    List<Emp> list = null;
    try {
        //1.註冊驅動
        //2.獲取連線
        conn = JDBCUtils.getConnection();
        //3.定義sql
        String sql = "select * from emp";
        //4.獲取執行sql的物件
        stmt = conn.createStatement();
        //5.執行sql
        rs = stmt.executeQuery(sql);
        //6.遍歷結果集,封裝物件,裝載集合
        Emp emp = null;
        list = new ArrayList<Emp>();
        while(rs.next()){
            //獲取資料
            int id = rs.getInt("id");
            String ename = rs.getString("ename");
            int job_id = rs.getInt("job_id");
            int mgr = rs.getInt("mgr");
            Date joindate = rs.getDate("joindate");
            double salary = rs.getDouble("salary");
            double bonus = rs.getDouble("bonus");
            int dept_id = rs.getInt("dept_id");
            // 建立emp物件,並賦值
            emp = new Emp();
            emp.setId(id);
            emp.setEname(ename);
            emp.setJob_id(job_id);
            emp.setMgr(mgr);
            emp.setJoindate(joindate);
            emp.setSalary(salary);
            emp.setBonus(bonus);
            emp.setDept_id(dept_id);

            //裝載集合
            list.add(emp);
        }

    } catch (SQLException e) {
        e.printStackTrace();
    }finally {          
        JDBCUtils.close(rs,stmt,conn);
    }
    return list;
}

}

練習

  1. 通過鍵盤錄入使用者名稱與密碼

  2. 判斷使用者是否登入成功(有一張表存放使用者與密碼)

    • select * from User where username="輸入的使用者名稱" and password="輸入的密碼"; 如果這個 sql 查詢有結果,則成功。
-- 建立資料庫 USER 表
CREATE TABLE USER(
      id INT PRIMARY KEY AUTO_INCREMENT,
      username VARCHAR(32),
      PASSWORD VARCHAR(32)
);

INSERT INTO USER VALUES(NULL, 'zhangsan', '123');
INSERT INTO USER VALUES(NULL, 'lisi', '234');

select * from USER;
判斷使用者是否登入成功

package cn.itcast.jdbc;
import cn.itcast.util.JDBCUtils;
import java.sql.*;
import java.util.Scanner;

public class JDBCDemo9 {

public static void main(String[] args) {
    //1.鍵盤錄入,接受使用者名稱和密碼
    Scanner sc = new Scanner(System.in);
    System.out.println("請輸入使用者名稱:");
    String username = sc.nextLine();
    System.out.println("請輸入密碼:");
    String password = sc.nextLine();
    //2.呼叫方法
    boolean flag = new JDBCDemo9().login(username, password);
    //3.判斷結果,輸出不同語句
    if(flag){
        //登入成功
        System.out.println("登入成功!");
    }else{
        System.out.println("使用者名稱或密碼錯誤!");
    }
}

public boolean login(String username ,String password){
    if(username == null || password == null){
        return false;
    }
    //連線資料庫判斷是否登入成功
    Connection conn = null;
    Statement stmt =  null;
    ResultSet rs = null;
    //1.獲取連線
    try {
        conn =  JDBCUtils.getConnection();
        //2.定義sql
        String sql = "select * from user where username = '"+username+"' and password = '"+password+"' ";
        System.out.println(sql);
        //3.獲取執行sql的物件
        stmt = conn.createStatement();
        //4.執行查詢
        rs = stmt.executeQuery(sql);
        //5.判斷
       return rs.next();//如果有下一行,則返回true

    } catch (SQLException e) {
        e.printStackTrace();
    }finally {
        JDBCUtils.close(rs,stmt,conn);
    }
    return false;
}

}

上述【判斷使用者是否登入成功】程式碼存在問題,由 SQL 注入問題引起。

2.6 PreparedStatement 執行 sql 的物件(功能更強大)

SQL注入問題:在拼接 sql 時,有一些 sql 的特殊關鍵字參與字串的拼接,會造成安全性問題。

在【判斷使用者是否登入成功】的練習中,隨便輸入使用者,輸入密碼:a' or 'a' = 'a,會生成 sql 語句 select * from user where username ='fhdsjkf' and password = 'a' or 'a' = 'a' false and false or true --> true,因此可以查詢到所有資料。

解決SQL注入問題:使用 PreparedStatement 物件。

public interface PreparedStatement extends Statement 表示預編譯的 SQL 語句的物件。

預編譯的 SQL:引數使用作為佔位符,執行 sql 時,給賦值。
如:PreparedStatement pstmt = con.prepareStatement("UPDATE EMPLOYEES SET SALARY = ? WHERE ID = ?"); 語句不會受傳入字串的關鍵字影響。

步驟

  1. 匯入驅動jar包【mysql-connector-java-5.1.37-bin.jar】
  2. 註冊驅動
  3. 獲取資料庫連線物件 Connection
  4. 定義sql(sql語句後面不要加分號)
    • sql的引數使用?作為佔位符。如 select * from user where username = ? and password = ?;
  5. 獲取執行sql語句的物件 PreparedStatement
    • PreparedStatement prepareStatement(String sql) throws SQLException
    • conn.prepareStatement(sql) 需要傳遞sql引數,而 conn.createStatement()無需傳參。
  6. 給?賦值
    • setXxx(引數1, 引數2) 其中,引數1表示?的位置,從1開始;引數2表示?的值。
  7. 執行sql,接受返回結果。
    • 此時無序再傳遞 sql 語句,在 PreparedStatement 使用時已經傳遞。
  8. 處理結果
  9. 釋放資源
PreparedStatement 判斷使用者是否登入成功

package cn.itcast.jdbc;

import cn.itcast.util.JDBCUtils;

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

public class JDBCDemo9 {

public static void main(String[] args) {
    //1.鍵盤錄入,接受使用者名稱和密碼
    Scanner sc = new Scanner(System.in);
    System.out.println("請輸入使用者名稱:");
    String username = sc.nextLine();
    System.out.println("請輸入密碼:");
    String password = sc.nextLine();
    //2.呼叫方法
    boolean flag = new JDBCDemo9().login2(username, password);
    //3.判斷結果,輸出不同語句
    if(flag){
        //登入成功
        System.out.println("登入成功!");
    }else{
        System.out.println("使用者名稱或密碼錯誤!");
    }
}

// 登入方法,使用PreparedStatement實現
public boolean login2(String username ,String password){
    if(username == null || password == null){
        return false;
    }
    //連線資料庫判斷是否登入成功
    Connection conn = null;
    PreparedStatement pstmt =  null;
    ResultSet rs = null;
    //1.獲取連線
    try {
        conn =  JDBCUtils.getConnection();
        //2.定義sql
        String sql = "select * from user where username = ? and password = ?";
        //3.獲取執行sql的物件
        pstmt = conn.prepareStatement(sql);
        //給?賦值
        pstmt.setString(1,username);
        pstmt.setString(2,password);
        //4.執行查詢,不需要傳遞sql
        rs = pstmt.executeQuery();
        //5.判斷
        return rs.next();//如果有下一行,則返回true

    } catch (SQLException e) {
        e.printStackTrace();
    }finally {
        JDBCUtils.close(rs,pstmt,conn);
    }
    return false;
}

}

注意:後期都會使用PreparedStatement來完成增刪改查的所有操作。

  • 可以放置 SQL 注入
  • 效率更高

三、JDBC管理事務

銀行轉賬案例

展示程式碼

package cn.itcast.jdbc;
import cn.itcast.util.JDBCUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

// 事務操作
public class JDBCDemo10 {

public static void main(String[] args) {
    Connection conn = null;
    PreparedStatement pstmt1 = null;
    PreparedStatement pstmt2 = null;

    try {
        //1.獲取連線
        conn = JDBCUtils.getConnection();
        //開啟事務
        conn.setAutoCommit(false);

        //2.定義sql
        //2.1 張三 - 500
        String sql1 = "update account set balance = balance - ? where id = ?";
        //2.2 李四 + 500
        String sql2 = "update account set balance = balance + ? where id = ?";
        //3.獲取執行sql物件
        pstmt1 = conn.prepareStatement(sql1);
        pstmt2 = conn.prepareStatement(sql2);
        //4. 設定引數
        pstmt1.setDouble(1,500);
        pstmt1.setInt(2,1);

        pstmt2.setDouble(1,500);
        pstmt2.setInt(2,2);
        //5.執行sql
        pstmt1.executeUpdate();
        // 手動製造異常
        int i = 3/0;

        pstmt2.executeUpdate();
        //提交事務
        conn.commit();
    } catch (Exception e) {
        //事務回滾
        try {
            if(conn != null) {
                conn.rollback();
            }
        } catch (SQLException e1) {
            e1.printStackTrace();
        }
        e.printStackTrace();
    }finally {
        JDBCUtils.close(pstmt1,conn);
        JDBCUtils.close(pstmt2,null);
    }
}

}

四、資料庫連線池

五、JDBCTemplat