MySQL第04篇:JDBC

核心概述:在實際開發中,咱們須要經過Java程序來操做數據庫,此時Java給咱們提供了Java訪問數據庫的標準規範就是JDBC,咱們能夠經過調用JDBC接口中的方法實現操做數據庫。
java

第一章:JDBC快速入門

1.1-客戶端操做MySQL的方式

01-使用第三方客戶端來訪問 MySQL:SQLyog、Navicat、SQLWave、MyDB Studio、EMS SQL Manager for MySQL 。
mysql

02-使用 MySQL 自帶的命令行方式 。程序員

03-經過 Java 來訪問 MySQL 數據庫,本篇要講解的內容spring

1.2-JDBC概述

什麼是JDBC

JDBC (Java Database Connectivity,簡稱JDBC )是 Java 訪問數據庫的標準規範,真正怎麼操做數據庫還須要具體的實現類,也就是數據庫驅動。每一個 數據庫廠商根據自家數據庫的通訊格式編寫好本身數據庫的驅動。因此咱們只須要會調用 JDBC 接口中的方法便可,數據庫驅動由數據庫廠商提供。sql

JDBC的好處

01-程序員若是要開發訪問數據庫的程序,只須要會調用 JDBC 接口中的方法便可,不用關注類是如何實現的。數據庫

02-使用同一套 Java 代碼,進行少許的修改就能夠訪問其餘 JDBC 支持的數據庫 。安全

使用 JDBC 開發使用到的包
會使用到的包 說明
java.sql 全部與 JDBC 訪問數據庫相關的接口和類
javax.sql 數據庫擴展包,提供數據庫額外的功能。如:鏈接池
數據庫的驅動 由各大數據庫廠商提供,須要額外去下載,是對 JDBC 接口實現的類

1.3-JDBC核心API

接口或類 做用
DriverManager 類 1) 管理和註冊數據庫驅動
2) 獲得數據庫鏈接對象
Connection 接口 一個鏈接對象,可用於建立 Statement 和 PreparedStatement 對象
Statement 接口 一個 SQL 語句對象,用於將 SQL 語句發送給數據庫服務器。
PreparedStatement 接口 一個 SQL 語句對象,是 Statement 的子接口
ResultSet 接口 用於封裝數據庫查詢的結果集,返回給客戶端 Java 程序

1.4-導入驅動jar包

下載

連接:https://pan.baidu.com/s/1igjB...
提取碼:v72g服務器

導入步驟

1.5-加載和註冊驅動

加載方法
加載和註冊驅動的方法 描述
Class.forName(數據庫驅動實現類) 加載和註冊數據庫驅動,數據庫驅動由 mysql 廠商"com.mysql.jdbc.Driver"
具體實現
public class Demo1 {
 public static void main(String[] args) throws ClassNotFoundException {
     //拋出類找不到的異常,註冊數據庫驅動
     Class.forName("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!");
         }
     }
}

com.mysql.jdbc.Driver 源代碼:app

Driver 接口,全部數據庫廠商必須實現的接口,表示這是一個驅動類。

注意事項

從 JDBC3 開始,目前已經廣泛使用的版本。能夠不用註冊驅動而直接使用。Class.forName 這句話能夠省略。

爲了向下兼容,建議不要省略。

第二章:DriverManager類

2.1-DriverManager做用

  1. 管理和註冊驅動
  2. 建立數據庫的鏈接

2.2-類中的方法

DriverManager 類中的靜態方法 描述
Connection getConnection (String url, String user, String password) 經過鏈接字符串,用戶名,密碼來獲得數據庫的鏈接對象
Connection getConnection (String url, Properties info) 經過鏈接字符串,屬性對象來獲得鏈接對象

2.3-鏈接數據庫的四個參數

參數 說明
用戶名 登陸的用戶名
密碼 登陸的密碼
鏈接字符串 URL 不一樣的數據庫 URL 是不一樣的
mysql 的寫法jdbc:mysql://localhost:3306/數據庫[?參數名=參數值]
驅動類的字符串名 com.mysql.jdbc.Driver

2.4-鏈接數據庫URL

格式
協議名:子協議://服務器名或 IP 地址:端口號/數據庫名?參數=參數值
MySQL格式

MySQL簡寫格式

前提:必須是本地服務器,端口號是 3306 。

簡寫格式:jdbc:mysql:///數據庫名

亂碼的處理

若是數據庫出現亂碼,能夠指定參數: ?characterEncoding=utf8,表示讓數據庫以 UTF-8 編碼來處理數據。

jdbc:mysql://localhost:3306/數據庫?characterEncoding=utf8

2.5-代碼演示

方式1:使用用戶名、密碼、URL 獲得鏈接對象
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* 獲得鏈接對象
*/
public class Demo2 {
 public static void main(String[] args) throws SQLException {
     String url = "jdbc:mysql://localhost:3306/db3";
     //1) 使用用戶名、密碼、URL 獲得鏈接對象
     Connection connection = DriverManager.getConnection(url, "root", "root");
     //com.mysql.jdbc.JDBC4Connection@68de145
     System.out.println(connection);
 }
}
方式2:使用屬性文件和 url 獲得鏈接對象
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
public class Demo3 {
 public static void main(String[] args) throws SQLException {
     //url 鏈接字符串
     String url = "jdbc:mysql://localhost:3306/db3";
     //屬性對象
     Properties info = new Properties();
     //把用戶名和密碼放在 info 對象中
     info.setProperty("user","root");
     info.setProperty("password","root");
     Connection connection = DriverManager.getConnection(url, info);
     //com.mysql.jdbc.JDBC4Connection@68de145
     System.out.println(connection);
 }
}

第三章:Connection接口

做用

Connection 接口,具體的實現類由數據庫的廠商實現,表明一個鏈接對象。

方法
方法 描述
Statement createStatement() 建立一條 SQL 語句對象

第四章:Statement 接口

4.1-JDBC訪問數據庫的步驟

  1. 註冊和加載驅動(能夠省略)
  2. 獲取鏈接
  3. Connection Statement 對象
  4. 使用 Statement 對象執行 SQL 語句
  5. 返回結果集
  6. 釋放資源

4.2-Statement 做用

表明一條語句對象,用於發送 SQL 語句給服務器,用於執行靜態 SQL 語句並返回它所生成結果的對象。

4.3-Statement 中的方法

Statement 接口中的方法 描述
int executeUpdate(String sql) 用於發送 DML 語句,增刪改的操做(insert、update、delete) 參數:SQL 語句 返回值:返回對數據庫影響的行數
ResultSet executeQuery(String sql) 用於發送 DQL 語句,執行查詢的操做。 select 參數:SQL 語句 返回值:查詢的結果集

4.4-釋放資源

  1. 須要釋放的對象:ResultSet 結果集,Statement 語句,Connection 鏈接
  2. 釋放原則:先開的後關,後開的先關。ResultSet →Statement →Connection
  3. 放在哪一個代碼塊中:finally 塊

4.5-執行DDL

需求:使用 JDBC 在 MySQL 的數據庫中建立一張學生表

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
/**
* 建立一張學生表
*/
public class Demo4DDL {
 public static void main(String[] args) {
     //1. 建立鏈接
     Connection conn = null;
     Statement statement = null;
     try {
         conn = DriverManager.getConnection("jdbc:mysql:///db3", "root", "root");
         //2. 經過鏈接對象獲得語句對象
         statement = conn.createStatement();
         //3. 經過語句對象發送 SQL 語句給服務器
         //4. 執行 SQL
         statement.executeUpdate("create table student (id int PRIMARY key auto_increment, " +
         "name varchar(20) not null, gender boolean, birthday date)");
         //5. 返回影響行數(DDL 沒有返回值)
         System.out.println("建立表成功");
     } catch (SQLException e) {
         e.printStackTrace();
     }
     //6. 釋放資源
     finally {
         //關閉以前要先判斷
         if (statement != null) {
         try {
             statement.close();
         } catch (SQLException e) {
             e.printStackTrace();
         }
          }
         if (conn != null) {
             try {
                 conn.close();
             } catch (SQLException e) {
                 e.printStackTrace();
             }
         }
     }
 }
}

4.6-執行DML

需求

向學生表中添加 4 條記錄,主鍵是自動增加

步驟
  1. 建立鏈接對象
  2. 建立 Statement 語句對象
  3. 執行 SQL 語句:executeUpdate(sql)
  4. 返回影響的行數
  5. 釋放資源
代碼
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
/**
* 向學生表中添加 4 條記錄,主鍵是自動增加
*/
public class Demo5DML {
 public static void main(String[] args) throws SQLException {
    // 1) 建立鏈接對象
     Connection connection = DriverManager.getConnection("jdbc:mysql:///day24", "root",
    "root");
    // 2) 建立 Statement 語句對象
     Statement statement = connection.createStatement();
    // 3) 執行 SQL 語句:executeUpdate(sql)
     int count = 0;
    // 4) 返回影響的行數
     count += statement.executeUpdate("insert into student values(null, '孫悟空', 1, '1993-03-
    24')");
     count += statement.executeUpdate("insert into student values(null, '白骨精', 0, '1995-03-
    24')");
     count += statement.executeUpdate("insert into student values(null, '豬八戒', 1, '1903-03-
    24')");
     count += statement.executeUpdate("insert into student values(null, '嫦娥', 0, '1993-03-
    11')");
     System.out.println("插入了" + count + "條記錄");
    // 5) 釋放資源
     statement.close();
     connection.close();
 }
}

4.7-執行DQL

ResultSet 接口

做用:封裝數據庫查詢的結果集,對結果集進行遍歷,取出每一條記錄。

接口中的方法

其餘方法

經常使用的數據類型轉換表

java.sql.Date、Time、Timestamp(時間戳),三個共同父類是:java.util.Date

需求

確保數據庫中有 3 條以上的記錄,查詢全部的學員信息

步驟
  1. 獲得鏈接對象
  2. 獲得語句對象
  3. 執行 SQL 語句獲得結果集 ResultSet 對象
  4. 循環遍歷取出每一條記錄
  5. 輸出的控制檯上
  6. 釋放資源
代碼
import java.sql.*;
/**
* 查詢全部的學生信息
*/
public class Demo6DQL {
 public static void main(String[] args) throws SQLException {
     //1) 獲得鏈接對象
     Connection connection =
    DriverManager.getConnection("jdbc:mysql://localhost:3306/day24","root","root");
     //2) 獲得語句對象
     Statement statement = connection.createStatement();
     //3) 執行 SQL 語句獲得結果集 ResultSet 對象
     ResultSet rs = statement.executeQuery("select * from student");
     //4) 循環遍歷取出每一條記錄
     while(rs.next()) {
         int id = rs.getInt("id");
         String name = rs.getString("name");
         boolean gender = rs.getBoolean("gender");
         Date birthday = rs.getDate("birthday");
         //5) 輸出的控制檯上
         System.out.println("編號:" + id + ", 姓名:" + name + ", 性別:" + gender + ", 生日:" +
        birthday);
     }
     //6) 釋放資源
     rs.close();
     statement.close();
     connection.close();
 }
}
結果

ResultSet使用注意事項
  1. 若是光標在第一行以前,使用 rs.getXX()獲取列值,報錯:Before start of result set
  2. 若是光標在最後一行以後,使用 rs.getXX()獲取列值,報錯:After end of result set
  3. 使用完畢之後要關閉結果集 ResultSet,再關閉 Statement,再關閉 Connection

第五章:數據庫工具類 JdbcUtils

若是一個功能常常要用到,咱們建議把這個功能作成一個工具類,能夠在不一樣的地方重用。

需求

上面寫的代碼中出現了不少重複的代碼,能夠把這些公共代碼抽取出來

代碼實現
  1. 能夠把幾個字符串定義成常量:用戶名,密碼,URL,驅動類
  2. 獲得數據庫的鏈接:getConnection()
  3. 關閉全部打開的資源: close(Connection conn, Statement stmt)close(Connection conn, Statement stmt, ResultSet rs)
import java.sql.*;
/**
* 訪問數據庫的工具類
*/
public class JdbcUtils {
 //能夠把幾個字符串定義成常量:用戶名,密碼,URL,驅動類
 private static final String USER = "root";
 private static final String PWD = "root";
 private static final String URL = "jdbc:mysql://localhost:3306/day24";
 private static final String DRIVER= "com.mysql.jdbc.Driver";
 /**
 * 註冊驅動
 */
 static {
     try {
        Class.forName(DRIVER);
     } catch (ClassNotFoundException e) {
         e.printStackTrace();
     }
 }
 /**
 * 獲得數據庫的鏈接
 */
 public static Connection getConnection() throws SQLException {
     return DriverManager.getConnection(URL,USER,PWD);
 }
 /**
 * 關閉全部打開的資源
 */
 public static void close(Connection conn, Statement stmt) {
     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(Connection conn, Statement stmt, ResultSet rs) {
     if (rs!=null) {
     try {
         rs.close();
     } catch (SQLException e) {
         e.printStackTrace();
     }
     }
     close(conn, stmt);
 }
}

第六章:用戶登陸案例

6.1-需求

  1. 有一張用戶表
  2. 添加幾條用戶記錄
  3. 使用 Statement 字符串拼接的方式實現用戶的登陸, 用戶在控制檯上輸入用戶名和密碼。
create table user (
 id int primary key auto_increment,
 name varchar(20),
 password varchar(20)
)
insert into user values (null,'jack','123'),(null,'rose','456');
-- 登陸, SQL 中大小寫不敏感
select * from user where name='JACK' and password='123';
-- 登陸失敗
select * from user where name='JACK' and password='333';

6.2-步驟

  1. 獲得用戶從控制檯上輸入的用戶名和密碼來查詢數據庫
  2. 寫一個登陸的方法

    • 經過工具類獲得鏈接
    • 建立語句對象,使用拼接字符串的方式生成 SQL 語句
    • 查詢數據庫,若是有記錄則表示登陸成功,不然登陸失敗
    • 釋放資源
import com.it.utils.JdbcUtils;
import javax.xml.transform.Result;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;
public class Demo7Login {
 //從控制檯上輸入的用戶名和密碼
 public static void main(String[] args) {
     Scanner sc = new Scanner(System.in);
     System.out.println("請輸入用戶名:");
     String name = sc.nextLine();
     System.out.println("請輸入密碼:");
     String password = sc.nextLine();
     login(name, password);
 }
 /**
 * 登陸的方法
 */
 public static void login(String name, String password) {
     //a) 經過工具類獲得鏈接
     Connection connection = null;
     Statement statement = null;
     ResultSet rs = null;
     try {
         connection = JdbcUtils.getConnection();
         //b) 建立語句對象,使用拼接字符串的方式生成 SQL 語句
         statement = connection.createStatement();
         //c) 查詢數據庫,若是有記錄則表示登陸成功,不然登陸失敗
         String sql = "select * from user where name='" + name + "' and password='" + password
        + "'";
         System.out.println(sql);
         rs = statement.executeQuery(sql);
         if (rs.next()) {
            System.out.println("登陸成功,歡迎您:" + name);
         } else {
            System.out.println("登陸失敗");
         }
     } catch (SQLException e) {
         e.printStackTrace();
     } finally {
        //d) 釋放資源
        JdbcUtils.close(connection, statement, rs);
     }
 }
}

6.3-SQL 注入問題

現象

當咱們輸入如下密碼,咱們發現咱們帳號和密碼都不對居然登陸成功了

請輸入用戶名:
newboy
請輸入密碼:
a' or '1'='1
select * from user where name='newboy' and password='a' or '1'='1'
登陸成功,歡迎您:newboy
問題分析
select * from user where name='newboy' and password='a' or '1'='1'
-- name='newboy' and password='a' 爲假
-- '1'='1' 真
-- 至關於
select * from user where true; -- 查詢了全部記錄

咱們讓用戶輸入的密碼和 SQL 語句進行字符串拼接。用戶輸入的內容做爲了 SQL 語句語法的一部分,改變了 原有 SQL 真正的意義,以上問題稱爲 SQL 注入。要解決 SQL 注入就不能讓用戶輸入的密碼和咱們的 SQL 語句進 行簡單的字符串拼接。

第七章:PreparedStatement 接口

7.1-繼承結構與做用

PreparedStatement 是 Statement 接口的子接口,繼承於父接口中全部的方法。它是一個預編譯的 SQL 語句

7.2-PreparedSatement 的執行原理

  1. 由於有預先編譯的功能,提升 SQL 的執行效率。
  2. 能夠有效的防止 SQL 注入的問題,安全性更高。

7.3-PreparedStatement 對象

建立

經常使用方法

做用
  1. prepareStatement()會先將 SQL 語句發送給數據庫預編譯。PreparedStatement 會引用着預編譯後的結果。 能夠屢次傳入不一樣的參數給 PreparedStatement 對象並執行。減小 SQL 編譯次數,提升效率。
  2. 安全性更高,沒有 SQL 注入的隱患。
  3. 提升了程序的可讀性

7.4-使用 PreparedStatement 的步驟

  1. 編寫 SQL 語句,未知內容使用?佔位:"SELECT * FROM user WHERE name=? AND password=?";
  2. 得到 PreparedStatement 對象
  3. 設置實際參數:setXxx(佔位符的位置, 真實的值)
  4. 執行參數化 SQL 語句
  5. 關閉資源

使用 PreparedStatement 改寫上面的登陸程序,看有沒有 SQL 注入的狀況

import com.it.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Scanner;
/**
* 使用 PreparedStatement
*/
public class Demo8Login {
 //從控制檯上輸入的用戶名和密碼
 public static void main(String[] args) throws SQLException {
     Scanner sc = new Scanner(System.in);
     System.out.println("請輸入用戶名:");
     String name = sc.nextLine();
     System.out.println("請輸入密碼:");
     String password = sc.nextLine();
     login(name, password);
 }
 /**
 * 登陸的方法
 * @param name
 * @param password
 */
 private static void login(String name, String password) throws SQLException {
     Connection connection = JdbcUtils.getConnection();
     //寫成登陸 SQL 語句,沒有單引號
     String sql = "select * from user where name=? and password=?";
     //獲得語句對象
     PreparedStatement ps = connection.prepareStatement(sql);
     //設置參數
     ps.setString(1, name);
     ps.setString(2,password);
     ResultSet resultSet = ps.executeQuery();
     if (resultSet.next()) {
         System.out.println("登陸成功:" + name);
     }
     else {
         System.out.println("登陸失敗");
     }
     //釋放資源,子接口直接給父接口
     JdbcUtils.close(connection,ps,resultSet);
 }
}

7.5-表與類的關係

案例:使用 PreparedStatement 查詢一條數據,封裝成一個學生 Student 對象
import com.it.entity.Student;
import com.it.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class Demo9Student {
     public static void main(String[] args) throws SQLException {
         //建立學生對象
         Student student = new Student();
         Connection connection = JdbcUtils.getConnection();
         PreparedStatement ps = connection.prepareStatement("select * from student where id=?");
         //設置參數
         ps.setInt(1,2);
         ResultSet resultSet = ps.executeQuery();
         if (resultSet.next()) {
         //封裝成一個學生對象
         student.setId(resultSet.getInt("id"));
         student.setName(resultSet.getString("name"));
         student.setGender(resultSet.getBoolean("gender"));
         student.setBirthday(resultSet.getDate("birthday"));
         }
         //釋放資源
         JdbcUtils.close(connection,ps,resultSet);
         //能夠數據
         System.out.println(student);
     }
}
案例:將多條記錄封裝成集合 List,集合中每一個元素是一個 JavaBean 實體類

需求: 查詢全部的學生類,封裝成 List<student>返回

import com.it.entity.Student;
import com.it.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class Demo10List {
 public static void main(String[] args) throws SQLException {
     //建立一個集合
     List<Student> students = new ArrayList<>();
     Connection connection = JdbcUtils.getConnection();
     PreparedStatement ps = connection.prepareStatement("select * from student");
     //沒有參數替換
     ResultSet resultSet = ps.executeQuery();
     while(resultSet.next()) {
         //每次循環是一個學生對象
         Student student = new Student();
         //封裝成一個學生對象
         student.setId(resultSet.getInt("id"));
         student.setName(resultSet.getString("name"));
         student.setGender(resultSet.getBoolean("gender"));
         student.setBirthday(resultSet.getDate("birthday"));
         //把數據放到集合中
         students.add(student);
     }
     //關閉鏈接
     JdbcUtils.close(connection,ps,resultSet);
     //使用數據
     for (Student stu: students) {
        System.out.println(stu);
     }
 }
}

7.6-PreparedStatement 執行 DML 操做

import com.it.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class Demo11DML {
 public static void main(String[] args) throws SQLException {
     //insert();
     //update();
     delete();
 }
 //插入記錄
 private static void insert() throws SQLException {
     Connection connection = JdbcUtils.getConnection();
     PreparedStatement ps = connection.prepareStatement("insert into student
    values(null,?,?,?)");
     ps.setString(1,"小白龍");
     ps.setBoolean(2, true);
     ps.setDate(3,java.sql.Date.valueOf("1999-11-11"));
     int row = ps.executeUpdate();
     System.out.println("插入了" + row + "條記錄");
     JdbcUtils.close(connection,ps);
 }
 //更新記錄: 換名字和生日
 private static void update() throws SQLException {
     Connection connection = JdbcUtils.getConnection();
     PreparedStatement ps = connection.prepareStatement("update student set name=?, birthday=?
    where id=?");
     ps.setString(1,"黑熊怪");
     ps.setDate(2,java.sql.Date.valueOf("1999-03-23"));
     ps.setInt(3,5);
     int row = ps.executeUpdate();
     System.out.println("更新" + row + "條記錄");
     JdbcUtils.close(connection,ps);
 }
 //刪除記錄: 刪除第 5 條記錄
 private static void delete() throws SQLException {
     Connection connection = JdbcUtils.getConnection();
     PreparedStatement ps = connection.prepareStatement("delete from student where id=?");
     ps.setInt(1,5);
     int row = ps.executeUpdate();
     System.out.println("刪除了" + row + "條記錄");
     JdbcUtils.close(connection,ps);
 }
}

第八章:JDBC處理事務

以前咱們是使用 MySQL 的命令來操做事務。接下來咱們使用 JDBC 來操做銀行轉帳的事務

8.1-準備數據

CREATE TABLE account (
    id INT PRIMARY KEY AUTO_INCREMENT,
    NAME VARCHAR(10),
    balance DOUBLE
);
-- 添加數據
INSERT INTO account (NAME, balance) VALUES ('Jack', 1000), ('Rose', 1000);

8.2-API介紹

8.3-開發步驟

  1. 獲取鏈接
  2. 開啓事務
  3. 獲取到 PreparedStatement
  4. 使用 PreparedStatement 執行兩次更新操做
  5. 正常狀況下提交事務
  6. 出現異常回滾事務
  7. 最後關閉資源
import com.it.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class Demo12Transaction {
 //沒有異常,提交事務,出現異常回滾事務
 public static void main(String[] args) {
     //1) 註冊驅動
     Connection connection = null;
     PreparedStatement ps = null;
     try {
         //2) 獲取鏈接
         connection = JdbcUtils.getConnection();
         //3) 開啓事務
         connection.setAutoCommit(false);
         //4) 獲取到 PreparedStatement
         //從 jack 扣錢
         ps = connection.prepareStatement("update account set balance = balance - ? where
        name=?");
         ps.setInt(1, 500);
         ps.setString(2,"Jack");
         ps.executeUpdate();
         //出現異常
         System.out.println(100 / 0);
         //給 rose 加錢
         ps = connection.prepareStatement("update account set balance = balance + ? where
        name=?");
         ps.setInt(1, 500);
         ps.setString(2,"Rose");
         ps.executeUpdate();
         //提交事務
         connection.commit();
         System.out.println("轉帳成功");
     } catch (Exception e) {
         e.printStackTrace();
         try {
         //事務的回滾
         connection.rollback();
         } catch (SQLException e1) {
         e1.printStackTrace();
         }
         System.out.println("轉帳失敗");
     }
     finally {
         //7) 關閉資源
         JdbcUtils.close(connection,ps);
     }
 }
}

第九章:數據庫鏈接池

9.1-爲何要學習數據庫鏈接池

數據庫鏈接是一種關鍵的、有限的、昂貴的資源,這一點在多用戶的網頁應用程序中體現得尤其突出。
一個數據庫鏈接對象均對應一個物理數據庫鏈接,每次操做都打開一個物理鏈接,使用完都關閉鏈接,這樣形成系統的 性能低下
數據庫鏈接池的解決方案是在應用程序啓動時創建足夠的數據庫鏈接,並將這些鏈接組成一個鏈接池(簡單說:在一個「池」裏放了好多數據庫鏈接對象),由應用程序動態地對池中的鏈接進行申請、使用和釋放。
對於多於鏈接池中鏈接數的併發請求,應該在請求隊列中排隊等待。而且應用程序能夠根據池中鏈接的使用率,動態增長或減小池中的鏈接數。
鏈接池技術儘量多地重用了消耗內存地資源,大大節省了內存,提升了服務器地服務效率,可以支持更多的客戶服務。經過使用鏈接池,將大大提升程序運行效率,同時,咱們能夠經過其自身的管理機制來監視數據庫鏈接的數量、使用狀況等。

9.2-什麼是數據庫鏈接池

數據庫鏈接池負責分配、管理和釋放數據庫鏈接,它容許應用程序重複使用一個現有的數據庫鏈接,而不是再從新創建一個;釋放空閒時間超過最大空閒時間的數據庫鏈接來避免由於沒有釋放數據庫鏈接而引發的數據庫鏈接遺漏。這項技術能明顯提升對數據庫操做的性能。

9.3-如何使用數據庫鏈接池

Java提供了數據庫鏈接池接口:DataSource javax.sql包下的。

數據庫廠商實現接口,經常使用的有如下兩種:

  1. C3P0:數據庫鏈接池技術
  2. Druid:數據庫鏈接池實現技術,由阿里巴巴提供的

9.4-C3P0

準備資料

下載

連接:https://pan.baidu.com/s/1XAst...
提取碼:450q

使用步驟

01-導入jar包 (兩個) c3p0-0.9.5.2.jarmchange-commons-java-0.2.12.jar 。不要忘記導入數據庫驅動jar包

02-定義配置文件。

  • 名稱:c3p0.properties 或者 c3p0-config.xml
  • 路徑:直接將文件放在src目錄下便可。

03-建立核心對象 數據庫鏈接池對象 ComboPooledDataSource。

04-獲取數據庫鏈接對象。

c3p0-config.xml配置文件
<c3p0-config>
  <!-- 使用默認的配置讀取鏈接池對象 -->
  <default-config>
      <!--  鏈接參數 -->
    <!--驅動-->
    <property name="driverClass">com.mysql.jdbc.Driver</property>
    <!--數據庫地址-->
    <property name="jdbcUrl">jdbc:mysql://localhost:3306/db2</property>
    <!--用戶名-->
    <property name="user">root</property>
    <!--密碼-->
    <property name="password">root</property>
    
    <!-- 鏈接池參數 -->
    <!--初始化對象鏈接個數-->
    <property name="initialPoolSize">5</property>
    <!--最大個數-->
    <property name="maxPoolSize">10</property>
    <!--超時時長-->
    <property name="checkoutTimeout">3000</property>
  </default-config>
  <!--指定名稱讀取鏈接池對象-->
  <named-config name="otherc3p0"> 
    <!--  鏈接參數 -->
    <property name="driverClass">com.mysql.jdbc.Driver</property>
    <property name="jdbcUrl">jdbc:mysql://localhost:3306/day25</property>
    <property name="user">root</property>
    <property name="password">root</property>
    
    <!-- 鏈接池參數 -->
    <property name="initialPoolSize">5</property>
    <property name="maxPoolSize">8</property>
    <property name="checkoutTimeout">1000</property>
  </named-config>
</c3p0-config>
代碼演示
public class Test01 {
  public static void main(String[] args) throws SQLException {
    // 建立數據庫鏈接池對象
    DataSource ds = new ComboPooledDataSource();
    // 獲取數據庫鏈接對象
    Connection con = ds.getConnection();
    System.out.println(con);
  }
}

9.5-Druid

準備資料

下載

連接:https://pan.baidu.com/s/1nWIF...
提取碼:f43e

使用步驟
1. 導入jar包 druid-1.0.9.jar
    2. 定義配置文件:
        * 是properties形式的
        * 能夠叫任意名稱,能夠放在任意目錄下
    3. 加載配置文件。Properties
    4. 獲取數據庫鏈接池對象:經過工廠來來獲取  DruidDataSourceFactory
    5. 獲取鏈接:getConnection
druid.properties配置文件
# 數據庫驅動
driverClassName=com.mysql.jdbc.Driver
# 數據庫鏈接地址
url=jdbc:mysql://127.0.0.1:3306/db2
# 用戶名
username=root
# 密碼
password=root
# 初始化鏈接對象個數
initialSize=5
# 最大個數
maxActive=10
# 超時等待時長
maxWait=3000
代碼
public class Test01 {
  public static void main(String[] args) throws Exception {
    Properties pro = new Properties();
    InputStream is  = Test01.class.getClassLoader().getResourceAsStream("druid.properties");
    pro.load(is);
    DataSource ds = DruidDataSourceFactory.createDataSource(pro);
    Connection con = ds.getConnection();
    System.out.println(con);
  }
}

9.6-DruidUtils

DruidUtils
package it.leilei.util;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class DruidUtils {
  private static DataSource ds;
  private static InputStream is;
  static {
    Properties pro = new Properties();
    try {
      // 讀取配置文件
      is = DruidUtils.class.getClassLoader().getResourceAsStream("druid.properties");
      pro.load(is);
      // 建立數據庫鏈接池對象
      ds = DruidDataSourceFactory.createDataSource(pro);
    } catch (IOException e) {
      e.printStackTrace();
    } catch (Exception e) {
      e.printStackTrace();
    }finally {
      if(is!=null){
        try {
          is.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }

  }
  // 獲取Connection對象
  public static Connection getConnection() throws SQLException {
   return ds.getConnection();
  }
  // 釋放資源
  public static void close(Statement sta, Connection con, ResultSet rs) {
    if(sta!=null){
      try {
        sta.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }

    }
    if(con!=null){
      try {
        con.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }
    if(rs!=null){
      try {
        rs.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }
  }
  public static void close(Statement sta, Connection con){
    close(sta,con,null);
  }
  // 獲取鏈接池對象
  public static DataSource getDataSource(){
    return ds;
  }
}
測試:向數據庫db2中的表account中添加一條新的數據
package it.leilei.DruidDemo;

import it.leilei.util.DruidUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class Test02 {
  public static void main(String[] args) {
    Connection con = null;
    PreparedStatement ps = null;
    try {
      // 獲取數據庫鏈接對象
      con = DruidUtils.getConnection();
      // 定義SQL
      String sql = "INSERT INTO account  VALUES(NULL,?,?)";
      // 建立執行SQL語句對象
      ps = con.prepareStatement(sql);
      // 給參數設置值
      ps.setString(1,"小龍女");
      ps.setDouble(2,8000);
      // 執行sql
      int count = ps.executeUpdate();
      if(count>0){
        System.out.println("添加成功");
      }else {
        System.out.println("添加失敗");
      }
    } catch (SQLException e) {
      e.printStackTrace();
    }finally {
      DruidUtils.close(ps,con);
    }
  }
}

第十章:Spring JDBC

10.1-概述

以上數據庫鏈接池確實優化並簡化了Java對SQL的操做,可是在針對增刪改查操做時相對仍是有些繁瑣,在實際開發中,咱們一般會使用框架中封裝好的JDBC來操做數據庫,能夠更加簡化Java對SQL的操做,提升開發效率。

Spring框架對JDBC的簡單封裝。提供了一個JDBCTemplate對象簡化JDBC的開發。

10.2-使用Spring JDBC

準備資料

下載

連接:https://pan.baidu.com/s/1JLru...
提取碼:73vt

使用步驟
  1. 導入jar包(lib目錄下全部jar包)

    • commons-logging-1.2.jar
    • spring-beans-5.0.0.RELEASE.jar
    • spring-core-5.0.0.RELEASE.jar
    • spring-jdbc-5.0.0.RELEASE.jar
    • spring-tx-5.0.0.RELEASE.jar
  2. 建立JdbcTemplate對象。依賴於數據源DataSource。
  3. 調用JdbcTemplate的方法來完成CRUD的操做

    • update():執行DML語句。增、刪、改語句
    • queryForMap()

      :查詢結果將結果集封裝爲map集合,將列名做爲key,將值做爲value 將這條記錄封裝爲一個map集合

      • 注意:這個方法查詢的結果集長度只能是1
    • queryForList()

      :查詢結果將結果集封裝爲list集合

      • 注意:將每一條記錄封裝爲一個Map集合,再將Map集合裝載到List集合中
    • query()

      :查詢結果,將結果封裝爲JavaBean對象

      • query的參數:RowMapper

        1. 通常咱們使用BeanPropertyRowMapper實現類。能夠完成數據到JavaBean的自動封裝
        2. new BeanPropertyRowMapper<類型>(類型.class)
    • queryForObject:查詢結果,將結果封裝爲對象

      • 通常用於聚合函數的查詢
代碼演示
package it.leilei.JDBCTemplateDemo;
import it.leilei.domain.Emp;
import it.leilei.util.DruidUtils;
import org.junit.Test;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ResultSetExtractor;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

public class Test01 {
  // 建立JdbcTemplate對象
  private  JdbcTemplate jt = new JdbcTemplate(DruidUtils.getDataSource());
  /*
    添加一條新的數據
  */
  @Test
  public void test01(){
    // 定義SQL
    String sql = "INSERT INTO emp(NAME,gender,salary)VALUES(?,?,?)";
    // 執行
    int i = jt.update(sql,"張三丰","男",90000);
    System.out.println(i);
  }
  /*
    更新一條數據
  */
  @Test
  public void test02(){
    String sql = "update emp set salary=? where id=1";
    int i = jt.update(sql,10000);
    System.out.println(i);
  }
  /*
    刪除一條數據
  */
  @Test
  public void test03(){
    String sql = "delete from emp where id=?";
    int i = jt.update(sql,8);
    System.out.println(i);
  }
  /*
  * 查詢一條數據返回map集合
  * */
  @Test
  public void test04(){
    String sql = "select * from emp where id=?";
    Map<String,Object>  map = jt.queryForMap(sql,1);
    System.out.println(map);
  }
  /*
   * 查詢多條數據返回List集合
   * */
  @Test
  public void test05(){
    String sql = "select * from emp where id=? or id=?";
    List<Map<String, Object>> list = jt.queryForList(sql,1,2);
    System.out.println(list);
  }
  /*
  * 查詢多條數據返回List<Emp>集合
  * */
  @Test
  public void test06(){
    String sql = "select * from emp where id=? or id=?";
    List<Emp> list = jt.query(sql,new BeanPropertyRowMapper<Emp>(Emp.class),1,2);
    System.out.println(list);
  }

  /*
  * 聚合函數
  * */
  @Test
  public void test07(){
    String sql = "select count(id) from emp";
    long l = jt.queryForObject(sql,Long.class);
    System.out.println(l);
  }

}
相關文章
相關標籤/搜索