第一章:JDBC快速入門
1.1-客戶端操作MySQL的方式
01-使用第三方客戶端來訪問 MySQL:SQLyog、Navicat、SQLWave、MyDB Studio、EMS SQL Manager for MySQL 。
02-使用 MySQL 自帶的命令行方式 。
03-通過 Java 來訪問 MySQL 數據庫,本篇要講解的內容
1.2-JDBC概述
什么是JDBC
JDBC (Java Database Connectivity,簡稱JDBC )是 Java 訪問數據庫的標准規范,真正怎么操作數據庫還需要具體的實現類,也就是數據庫驅動。每個 數據庫廠商根據自家數據庫的通信格式編寫好自己數據庫的驅動。所以我們只需要會調用 JDBC 接口中的方法即可,數據庫驅動由數據庫廠商提供。
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 語句發送給數據庫服務器。 |
PreparedStatemen 接口 |
一個 SQL 語句對象,是 Statement 的子接口 |
ResultSet 接口 |
用於封裝數據庫查詢的結果集,返回給客戶端 Java 程序 |
1.4-導入驅動jar包
下載
鏈接:https://pan.baidu.com/s/1igjB1mTP7GBLBAZGP_bsGA
提取碼: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 源代碼:
Driver 接口,所有數據庫廠商必須實現的接口,表示這是一個驅動類。
注意事項
從 JDBC3 開始,目前已經普遍使用的版本。可以不用注冊驅動而直接使用。Class.forName 這句話可以省略。
為了向下兼容,建議不要省略。
第二章:DriverManager類
2.1-DriverManager作用
- 管理和注冊驅動
- 創建數據庫的連接
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訪問數據庫的步驟
- 注冊和加載驅動(可以省略)
- 獲取連接
- Connection 獲取 Statement 對象
- 使用 Statement 對象執行 SQL 語句
- 返回結果集
- 釋放資源
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-釋放資源
- 需要釋放的對象:ResultSet 結果集,Statement 語句,Connection 連接
- 釋放原則:先開的后關,后開的先關。ResultSet →Statement →Connection
- 放在哪個代碼塊中: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 條記錄,主鍵是自動增長
步驟
- 創建連接對象
- 創建 Statement 語句對象
- 執行 SQL 語句:executeUpdate(sql)
- 返回影響的行數
- 釋放資源
代碼
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 條以上的記錄,查詢所有的學員信息
步驟
- 得到連接對象
- 得到語句對象
- 執行 SQL 語句得到結果集 ResultSet 對象
- 循環遍歷取出每一條記錄
- 輸出的控制台上
- 釋放資源
代碼
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使用注意事項
- 如果光標在第一行之前,使用 rs.getXX()獲取列值,報錯:Before start of result set
- 如果光標在最后一行之后,使用 rs.getXX()獲取列值,報錯:After end of result set
- 使用完畢以后要關閉結果集 ResultSet,再關閉 Statement,再關閉 Connection
第五章:數據庫工具類 JdbcUtils
如果一個功能經常要用到,我們建議把這個功能做成一個工具類,可以在不同的地方重用。
需求
上面寫的代碼中出現了很多重復的代碼,可以把這些公共代碼抽取出來
代碼實現
-
可以把幾個字符串定義成常量:用戶名,密碼,URL,驅動類
-
得到數據庫的連接:getConnection()
-
關閉所有打開的資源:
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-需求
-
有一張用戶表
-
添加幾條用戶記錄
-
使用 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-步驟
- 得到用戶從控制台上輸入的用戶名和密碼來查詢數據庫
- 寫一個登錄的方法
- 通過工具類得到連接
- 創建語句對象,使用拼接字符串的方式生成 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 的執行原理
-
因為有預先編譯的功能,提高 SQL 的執行效率。
-
可以有效的防止 SQL 注入的問題,安全性更高。
7.3-PreparedStatement 對象
創建
常用方法
作用
- prepareStatement()會先將 SQL 語句發送給數據庫預編譯。PreparedStatement 會引用着預編譯后的結果。 可以多次傳入不同的參數給 PreparedStatement 對象並執行。減少 SQL 編譯次數,提高效率。
- 安全性更高,沒有 SQL 注入的隱患。
- 提高了程序的可讀性
7.4-使用 PreparedStatement 的步驟
-
編寫 SQL 語句,未知內容使用?占位:
"SELECT * FROM user WHERE name=? AND password=?"
; -
獲得 PreparedStatement 對象
-
設置實際參數:setXxx(占位符的位置, 真實的值)
-
執行參數化 SQL 語句
-
關閉資源
使用 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 來操作銀行轉賬的事務
7.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);
7.2-API介紹
7.3-開發步驟
- 獲取連接
- 開啟事務
- 獲取到 PreparedStatement
- 使用 PreparedStatement 執行兩次更新操作
- 正常情況下提交事務
- 出現異常回滾事務
- 最后關閉資源
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包下的。
數據庫廠商實現接口,常用的有以下兩種:
C3P0
:數據庫連接池技術Druid
:數據庫連接池實現技術,由阿里巴巴提供的
9.4-C3P0
准備資料
下載
鏈接:https://pan.baidu.com/s/1XAstwgWObgRM2G1J0iLTLA
提取碼:450q
使用步驟
01-導入jar包 (兩個) c3p0-0.9.5.2.jar
和 mchange-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/1nWIFTscCI-UODwAEaAy8NQ
提取碼: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/1JLru45Sb3Xog35U_xv7nNA
提取碼:73vt
使用步驟
- 導入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
- 創建
JdbcTemplate
對象。依賴於數據源DataSource。 - 調用JdbcTemplate的方法來完成CRUD的操作
update()
:執行DML語句。增、刪、改語句queryForMap()
:查詢結果將結果集封裝為map集合,將列名作為key,將值作為value 將這條記錄封裝為一個map集合- 注意:這個方法查詢的結果集長度只能是1
queryForList()
:查詢結果將結果集封裝為list集合- 注意:將每一條記錄封裝為一個Map集合,再將Map集合裝載到List集合中
query()
:查詢結果,將結果封裝為JavaBean對象- query的參數:RowMapper
- 一般我們使用BeanPropertyRowMapper實現類。可以完成數據到JavaBean的自動封裝
- new BeanPropertyRowMapper
<
類型>
(類型.class)
- query的參數:RowMapper
- 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);
}
}