1. 程式人生 > 實用技巧 >loj數列分塊入門 1~9

loj數列分塊入門 1~9

第1節 回顧
1.1 表連線
內連線:隱式、顯式
隱式:沒有 join,使用 where
顯式:inner join..on
外連線:左連線和右連線
左連線:left outer join … on
右連線:right outer join … on
1.2 子查詢
三種情況:
1) 單行單列:比較運算子:>、<、=
2) 多行單列:使用 in 關鍵字
3) 多行多列:放在 from 後面,做為一張表再次查詢
1.3 備份與還原
備份:mysqldump -u 使用者名稱 -p 密碼 資料庫 > 檔名
還原:
登入使用資料庫
use 資料庫
source 檔名
1.4 事務
事務四個特性 ACID:原子性、一致性、隔離性、永續性

在 mysql 中有三條語句:
開啟事務:start transaction
提交事務:commit
回滾事務:rollback
設定事務回滾點:savepoint 名字
回到回滾點:rollback to 名字

第3節 JDBC 入門
3.1 客戶端操作 MySQL 資料庫的方式:
1) 使用第三方客戶端來訪問 MySQL:SQLyog、Navicat、SQLWave、MyDB Studio、EMS SQL Manager
for MySQL

2) 使用 MySQL 自帶的命令列方式
3) 通過 Java 來訪問 MySQL 資料庫,今天要學習的內容
3.1.1 什麼是 JDBC
JDBC 規範定義 介面 ,具體的實現由各大資料庫廠商來實現。

JDBC 是 Java 訪問資料庫的標準規範,真正怎麼操作資料庫還需要具體的實現類,也就是資料庫驅動。每個
資料庫廠商根據自家資料庫的通訊格式編寫好自己資料庫的驅動。所以我們只需要會呼叫 JDBC 介面中的方法即
可,資料庫驅動由資料庫廠商提供。
 使用 JDBC 的好處:
1) 程式設計師如果要開發訪問資料庫的程式,只需要會呼叫 JDBC 介面中的方法即可,不用關注類是如何實現的。
2) 使用同一套 Java 程式碼,進行少量的修改就可以訪問其他 JDBC 支援的資料庫

3.1.2 使用 JDBC 開發使用到的包:

3.2 JDBC 的核心 API介面或類

3.3 匯入驅動 Jar 包

3.4 載入和註冊驅動

疑問:為什麼這樣可以註冊驅動?

public class Demo1 {
public static void main(String[] args) throws ClassNotFoundException {
// 丟擲類找不到的異常,註冊資料庫驅動
Class.forName("com.mysql.jdbc.Driver");
}
}

com.mysql.jdbc.Driver 原始碼:

// 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!");
}
}
}

注:從 JDBC3 開始,目前已經普遍使用的版本。可以不用註冊驅動而直接使用。Class.forName這句話可以省略。

第4節 DriverManager 類
4.1 DriverManager 作用:
1) 管理和註冊驅動
2) 建立資料庫的連線
4.2 類中的方法:

4.3 使用 JDBC 連線資料庫的四個引數:

4.4 連線資料庫的 URL 地址格式:

4.4.1 MySQL 寫法:

4.4.2 MySQL 中可以簡寫:

前提:必須是本地伺服器,埠號是 3306
jdbc:mysql:/// 資料庫名

4.4.3 亂碼的處理

jdbc:mysql://localhost:3306/資料庫?characterEncoding=utf8

4.5 案例:得到 MySQL 的資料庫連線物件
1) 使用使用者名稱、密碼、URL 得到連線物件

package com.itheima;
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/day24";
//1) 使用使用者名稱、密碼、 URL 得到連線物件
Connection connection = DriverManager.getConnection(url, "root", "root");
//com.mysql.jdbc.JDBC4Connection@68de145
System.out.println(connection);
}
}

2) 使用屬性檔案和 url 得到連線物件

package com.itheima;
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/day24";
//屬性物件
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);
}
}

第5節 Connection 介面:
5.1 Connection 作用:
Connection 介面,具體的實現類由資料庫的廠商實現,代表一個連線物件。
5.2 Connection 方法:

第6節 Statement 介面
6.1 JDBC 訪問資料庫的步驟

1) 註冊和載入驅動(可以省略)
2) 獲取連線
3) Connection 獲取 Statement 物件
4) 使用 Statement 物件執行 SQL 語句
5) 返回結果集
6) 釋放資源
6.2 Statement 作用:
代表一條語句物件,用於傳送 SQL 語句給伺服器,用於執行靜態 SQL 語句並返回它所生成結果的物件。
6.3 Statement 中的方法:

6.4 釋放資源
1) 需要釋放的物件:ResultSet 結果集,Statement 語句,Connection 連線
2) 釋放原則:先開的後關,後開的先關。ResultSet  Statement  Connection
3) 放在哪個程式碼塊中:finally 塊
6.5 執行 DDL 操作
6.5.1 需求:使用 JDBC 在 MySQL 的資料庫中建立一張學生表

6.5.2 程式碼:

package com.itheima;
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:///day24", "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();
}
}
}
}
}

6.6 執行 DML 操作

需求:向學生表中新增 4 條記錄,主鍵是自動增長

步驟:
1) 建立連線物件
2) 建立 Statement 語句物件
3) 執行 SQL 語句:executeUpdate(sql)
4) 返回影響的行數
5) 釋放資源

程式碼:

package com.itheima;
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();
}
}

6.7 執行 DQL 操作
6.7.1 ResultSet 介面:
 作用:封裝資料庫查詢的結果集,對結果集進行遍歷,取出每一條記錄。

介面中的方法:

6.7.2 常用資料型別轉換表

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

6.7.3 需求:確保資料庫中有 3 條以上的記錄,查詢所有的學員資訊

步驟:

1) 得到連線物件
2) 得到語句物件
3) 執行 SQL 語句得到結果集 ResultSet 物件
4) 迴圈遍歷取出每一條記錄
5) 輸出的控制檯上
6) 釋放資源
結果:

package com.itheima;
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();
}
}

6.7.4 關於 ResultSet 介面中的注意事項:
1) 如果游標在第一行之前,使用 rs.getXX()獲取列值,報錯:Before start of result set
2) 如果游標在最後一行之後,使用 rs.getXX()獲取列值,報錯:After end of result set
3) 使用完畢以後要關閉結果集 ResultSet,再關閉 Statement,再關閉 Connection
第7節 資料庫工具類 JdbcUtils
 什麼時候自己建立工具類?
如果一個功能經常要用到,我們建議把這個功能做成一個工具類,可以在不同的地方重用。
7.1 需求:
上面寫的程式碼中出現了很多重複的程式碼,可以把這些公共程式碼抽取出來。
7.2 建立類 JdbcUtil 包含 3 個方法:
1) 可以把幾個字串定義成常量:使用者名稱,密碼,URL,驅動類
2) 得到資料庫的連線:getConnection()
3) 關閉所有開啟的資源:
close(Connection conn, Statement stmt),close(Connection conn, Statement stmt, ResultSet rs)

package com.itheima.utils;
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);
}
}

7.3 案例:使用者登陸

7.3.1 需求:
1) 有一張使用者表
2) 新增幾條使用者記錄

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';

3) 使用 Statement 字串拼接的方式實現使用者的登入, 使用者在控制檯上輸入使用者名稱和密碼。
7.3.2 步驟:
1) 得到使用者從控制檯上輸入的使用者名稱和密碼來查詢資料庫
2) 寫一個登入的方法
a) 通過工具類得到連線
b) 建立語句物件,使用拼接字串的方式生成 SQL 語句
c) 查詢資料庫,如果有記錄則表示登入成功,否則登入失敗
d) 釋放資源

package com.itheima;
import com.itheima.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);
}
}
}

7.3.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 語句進
行簡單的字串拼接。

第8節 PreparedStatement 介面
8.1 繼承結構與作用:

PreparedStatement 是 Statement 介面的子介面,繼承於父介面中所有的方法。它是一個預編譯的 SQL 語句
8.2 PreparedSatement 的執行原理

1) 因為有預先編譯的功能,提高 SQL 的執行效率。
2) 可以有效的防止 SQL 注入的問題,安全性更高。
8.3 Connection 建立 PreparedStatement 物件

8.4 PreparedStatement 介面中的方法:

8.5 PreparedSatement 的好處
1. prepareStatement()會先將 SQL 語句傳送給資料庫預編譯。PreparedStatement 會引用著預編譯後的結果。
可以多次傳入不同的引數給 PreparedStatement 物件並執行。減少 SQL 編譯次數,提高效率。
2. 安全性更高,沒有 SQL 注入的隱患。
3. 提高了程式的可讀性
8.6 使用 PreparedStatement 的步驟:
1) 編寫 SQL 語句,未知內容使用?佔位:"SELECT * FROM user WHERE name=? AND password=?";
2) 獲得 PreparedStatement 物件
3) 設定實際引數:setXxx(佔位符的位置, 真實的值)
4) 執行引數化 SQL 語句
5) 關閉資源

使用 PreparedStatement 改寫上面的登入程式,看有沒有 SQL 注入的情況

package com.itheima;
import com.itheima.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);
}
}

8.7 表與類的關係

8.7.1 案例:使用 PreparedStatement 查詢一條資料,封裝成一個學生 Student 物件

package com.itheima;
import com.itheima.entity.Student;
import com.itheima.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);
}
}

8.7.2 案例:將多條記錄封裝成集合 List<Student>,集合中每個元素是一個 JavaBean 實體類
 需求: 查詢所有的學生類,封裝成 List<Student>返回
 程式碼:

package com.itheima;
import com.itheima.entity.Student;
import com.itheima.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);
}
}
}

8.8 PreparedStatement 執行 DML 操作

package com.itheima;
import com.itheima.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);
}
}

第9節 JDBC 事務的處理
之前我們是使用 MySQL 的命令來操作事務。接下來我們使用 JDBC 來操作銀行轉賬的事務。
9.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);

9.2 API 介紹

9.3 開發步驟
1) 獲取連線
2) 開啟事務
3) 獲取到 PreparedStatement
4) 使用 PreparedStatement 執行兩次更新操作
5) 正常情況下提交事務
6) 出現異常回滾事務
7) 最後關閉資源
 案例程式碼

package com.itheima;
import com.itheima.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);
}
}
}