1. 程式人生 > 實用技巧 >Java筆記17 - JDBC程式設計

Java筆記17 - JDBC程式設計

JDBC簡介

  • java程式訪問資料庫的標準介面
  • JDBC介面通過JDBC驅動實現真正對資料庫的訪問
  • 編寫一套程式碼, 訪問不同的資料庫
  • App.class -> java.sql.* -> mysql-xxx.jar -> (TCP) -> mysql

JDBC查詢

  • java.sql.*放的是一組介面
  • 用哪個資料庫, 就用哪個資料庫的實現類
  • 某個資料庫實現了JDBC介面的jar包稱為JDBC依賴

JDBC連線

  • Connection代表一個JDBC連線, 相當於Java程式到資料庫的連線.
  • 開啟一個Connection, 需要準備URL, 使用者名稱, 口令
  • DriverManager
    會自動掃描classpath, 找到所有的JDBC驅動, 然後根據我們傳入的URL自動挑選一個合適的驅動
  • JDBC是一種昂貴的資源, 使用後要及時釋放, 使用try (resource)自動釋放JDBC連線

JDBC查詢詳解

  • 第一步: 使用Connection提供的createStatement()方法建立一個Statement物件, 用於執行一個查詢;
  • 第二步: 執行Statement物件提供的executeQuery("SELECT * FROM students")並傳入SQL語句. 查詢執行並獲得返回的結果集
  • 第三部: 反覆呼叫ResultSetnext()方法並讀取每一行結果
      try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) {
        System.out.println(conn);
        try (Statement stmt = conn.createStatement()) {
          System.out.println(stmt);
          try (ResultSet rs = stmt.executeQuery("SELECT id, name, gender FROM students WHERE gender='F'")) {
            System.out.println(rs);
            while (rs.next()) {
              long id = rs.getLong(1); // 索引從1開始
              System.out.println(id);
            }
          }
        }
      }

SQL注入

  • 使用精心構造的字串, 拼接出不同SQL.
  • PreparedStatement可以完全避免SQL注入
  • 始終使用?作為佔位符, 並把資料連同SQL本身傳給資料庫, 保證每次傳給資料庫的SQL語句先溝通那個. 只是站位資料不同
  • 高效利用資料對查詢的查詢.
    String sql = "SELECT * FROM user WHERE lgoin=? AND pass=?";
    PreparedStatement ps = conn.prepareStatement(sql);
    ps.setObject(1, name);
    ps.setObject(2, pass);
  • 必須首先呼叫setObject()設定每個佔位符?的值, 最後仍然獲取的仍然是ResultSet物件

資料型別

  • JDBC定義了一組常量表示如何對映SQL資料型別, 經常用到的轉換:

  • SQL資料型別 / Java資料型別

  • BIT,BOOL / boolean

  • INTEGER / int

  • BIGINT / long

  • REAL / float

  • FLOAT,DOUBLE / double

  • CHAR,VARCHAR / String

  • DECIMAL / BigDecimal

  • DATE / java.sqal.Date,LocalDate

  • TIME / java.sql.Time,LocalTime

  • 只有最新的JDBC驅動才支援LocalDateLocalTime

JDBC更新

插入

  • 使用PreparedStatement執行sql語句, 使用executeUpdate()

  • 返回成功插入的數量

  • 建立PreparedStatement時, 同時指定RETURN_GENERATED_KEYS關鍵字, 自增後就能返回, 驅動就能返回自增關鍵則了.

  • 返回的是多行成功值

  • 插入, 更新, 刪除都是使用executeUpdate()

  • 只是使用的sql語句不同

JDBC事務

  • 資料庫事務具有ACID特性
  • 資料庫從安全性考慮, 對事物進行了四種安全特性
  • 資料庫事務, 保證程式結果正常
  • JDBC中執行事務, 就是多條sql包裹在一個數據庫事務中
  Connection conn = openConnection();
  try {
    // 關閉自動提交
    conn.setAutoCommit(false);
    // 執行多個sql
    insert();
    update();
    delete();
    conn.commit();
  } catch (SQLException e) {
    // 回滾
    conn.rollback();
  } finally {
    conn.setAutoCommit(true);
    conn.close();
  }
  • 資料庫預設使用REPEATABLE_READ

JDBC-Batch

  • 一次性生成批量優惠卷等場景
  • 迴圈生成PreparedStatement效率低
  • 反覆呼叫addPatch, 相當於給一個sql加上了多組引數, 變成多行sql
  • 因為是多行, 返回一個數組, 表示每一條影響的sql數量
try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) {
    try (PreparedStatement ps = conn.prepareStatement(
        "INSERT INTO students (name, gender, grade, score) VALUES (?, ?, ?, ?)"
      ))
    {
      for (String name: names) {
        ps.setString(1, name);
        ps.setInt(2, 0); // gender
        ps.setInt(3, 4); // grade
        ps.setInt(4, 99); // score
        ps.addBatch(); // 新增到batch
      }
      int[] ns = ps.executeBatch();
      for (int n : ns) {
        System.out.println(n + " inserted.");
      }
    }
}

JDBC連線池

  • JDBC執行緒連線是昂貴的操作
  • jdbc執行緒池標準介面 javax.sql.DataSource
  • 建立DataSource是非常昂貴操作, 所以通常DataSource例項總是作為一個全域性變數儲存, 並貫穿整個應用程式
    config.setJdbcUrl(JDBC_URL);
    config.setUsername(JDBC_USER);
    config.setPassword(JDBC_PASSWORD);
    config.addDataSourceProperty("connectionTimeout", "1000"); // 連線超時 1s
    config.addDataSourceProperty("idleTimeout", "60000"); // 空閒超時 60s
    config.addDataSourceProperty("maximumPoolSize", "10"); // 最大連線數 10
    DataSource ds = new HikariDataSource(config);

    try (Connection conn = ds.getConnection()) {
      // ...
    }
  • 第一次呼叫ds.getConnection()會先在連線池內部建立一個Connection
  • 呼叫conn.close()時, 並不會真的關閉, 釋放到連線池中.
  • 再次呼叫getConnection()並不會建立, 而是返回空閒的連線