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:原子性、一致性、隔離性、永續性
開啟事務: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 介面中的方法即
可,資料庫驅動由資料庫廠商提供。
使用 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); } } }