JDBC概論
1、JDBC是什么?
?? ?Java DataBase Connectivity(Java語言連接數據庫)
2、JDBC的本質是什么?
?? ?JDBC是SUN公司制定的一套接口(interface)
?? ??? ?java.sql.*; (這個軟件包下有很多接口。)
?? ?接口都有調用者和實現者。
?? ?面向接口調用、面向接口寫實現類,這都屬于面向接口編程。
?? ?為什么要面向接口編程?
?? ??? ?解耦合:降低程序的耦合度,提高程序的擴展力。
?? ??? ?多態機制就是非常典型的:面向抽象編程。(不要面向具體編程)
?? ??? ??? ?建議:
?? ??? ??? ??? ?Animal a = new Cat();
?? ??? ??? ??? ?Animal a = new Dog();
?? ??? ??? ??? ?// 喂養的方法
?? ??? ??? ??? ?public void feed(Animal a){ // 面向父類型編程。
?? ??? ??? ??? ?
?? ??? ??? ??? ?}
?? ??? ??? ?不建議:
?? ??? ??? ??? ?Dog d = new Dog();
?? ??? ??? ??? ?Cat c = new Cat();
?? ?
?? ?思考:為什么SUN制定一套JDBC接口呢?
?? ??? ?因為每一個數據庫的底層實現原理都不一樣。
?? ??? ?Oracle數據庫有自己的原理。0
?? ??? ?MySQL數據庫也有自己的原理。
?? ??? ?MS SqlServer數據庫也有自己的原理。
?? ??? ?....
?? ??? ?每一個數據庫產品都有自己獨特的實現原理。
?? ?
?? ?JDBC的本質到底是什么?
?? ??? ?一套接口。
3、JDBC開發前的準備工作,先從官網下載對應的驅動jar包,然后將其配置到環境變量classpath當中。
?? ?classpath=.;D:\course\06-JDBC\resources\MySql Connector Java 5.1.23\mysql-connector-java-5.1.23-bin.jar
?? ?以上的配置是針對于文本編輯器的方式開發,使用IDEA工具的時候,不需要配置以上的環境變量。
?? ?IDEA有自己的配置方式。
?? ??
4、JDBC編程六步(需要背會)
?? ?
?? ?第一步:注冊驅動(作用:告訴Java程序,即將要連接的是哪個品牌的數據庫)
?? ?第二步:獲取連接(表示JVM的進程和數據庫進程之間的通道打開了,這屬于進程之間的通信,重量級的,使用完之后一定要關閉通道。)
?? ?第三步:獲取數據庫操作對象(專門執行sql語句的對象)
?? ?第四步:執行SQL語句(DQL DML....)
?? ?第五步:處理查詢結果集(只有當第四步執行的是select語句的時候,才有這第五步處理查詢結果集。)
?? ?第六步:釋放資源(使用完資源之后一定要關閉資源。Java和數據庫屬于進程間的通信,開啟之后一定要關閉。)
程序運行
DML語句
?
/*JDBC編程基礎六步
*/
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Connection;
import java.sql.Statement;public class JDBCTest01{public static void main(String[] args){Connection conn = null;Statement stmt = null;try{//1、注冊驅動Driver driver = new com.mysql.jdbc.Driver(); // 多態,父類型引用指向子類型對象。// Driver driver = new oracle.jdbc.driver.OracleDriver(); // oracle的驅動。DriverManager.registerDriver(driver);//2、獲取連接/*url:統一資源定位符(網絡中某個資源的絕對路徑)https://www.baidu.com/ 這就是URL。URL包括哪幾部分?協議IPPORT資源名http://182.61.200.7:80/index.htmlhttp:// 通信協議182.61.200.7 服務器IP地址80 服務器上軟件的端口index.html 是服務器上某個資源名jdbc:mysql://127.0.0.1:3306/bjpowernodejdbc:mysql:// 協議127.0.0.1 IP地址3306 mysql數據庫端口號bjpowernode 具體的數據庫實例名。說明:localhost和127.0.0.1都是本機IP地址。jdbc:mysql://192.168.151.27:3306/bjpowernode什么是通信協議,有什么用?通信協議是通信之前就提前定好的數據傳送格式。數據包具體怎么傳數據,格式提前定好的。oracle的URL:jdbc:oracle:thin:@localhost:1521:orcl*/String url = "jdbc:mysql://localhost:3306/bjpowernode";String user = "root";String password = "123456";conn = DriverManager.getConnection(url,user,password);// com.mysql.jdbc.JDBC4Connection@41cf53f9System.out.println("數據庫連接對象 = " + conn); //3、獲取數據庫操作對象(Statement專門執行sql語句的)stmt = conn.createStatement();//4、執行sqlString sql = "insert into dept(deptno,dname,loc) values(80,'人事部','廣州')";// 專門執行DML語句的(insert delete update)// 返回值是“影響數據庫中的記錄條數”int count = stmt.executeUpdate(sql); System.out.println(count == 1 ? "保存成功" : "保存失敗");//5、處理查詢結果集}catch(SQLException e){e.printStackTrace();}finally{//6、釋放資源// 為了保證資源一定釋放,在finally語句塊中關閉資源// 并且要遵循從小到大依次關閉// 分別對其try..catchtry{if(stmt != null){stmt.close();}}catch(SQLException e){e.printStackTrace();}try{if(conn != null){conn.close();}}catch(SQLException e){e.printStackTrace();}}}
}?
注冊驅動的另一種方式
/*注冊驅動的另一種方式(這種方式常用)
*/
import java.sql.*;public class JDBCTest03{public static void main(String[] args){try{//1、注冊驅動// 這是注冊驅動的第一種寫法。// DriverManager.registerDriver(new com.mysql.jdbc.Driver());// 注冊驅動的第二種方式:常用的。// 為什么這種方式常用?因為參數是一個字符串,字符串可以寫到xxx.properties文件中。// 以下方法不需要接收返回值,因為我們只想用它的類加載動作。Class.forName("com.mysql.jdbc.Driver");//2、獲取連接Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");// com.mysql.jdbc.JDBC4Connection@41cf53f9System.out.println(conn);}catch(SQLException e){e.printStackTrace();}catch(ClassNotFoundException e){e.printStackTrace();}}
}
ResultSet
DQL語句需要Resultset,在JDBC中,ResultSet
對象用于存儲和操作SQL查詢返回的結果集。當你執行一個SELECT查詢時,數據庫會返回一組符合查詢條件的記錄,這些記錄被存儲在一個ResultSet
對象中。通過這個對象,你可以遍歷、檢索和修改結果集中的數據。getInt
和getString
方法是ResultSet
接口提供的兩種常用方法,用于從當前行的指定列中檢索數據。這些方法允許你以不同的數據類型獲取查詢結果,參數為列的索引或列名。next
方法用于將光標從當前位置移動到結果集的下一行。如果下一行存在,next
方法會返回true
;如果已經到達結果集的末尾,則返回false
。這個方法在遍歷結果集的過程中非常重要,因為它控制著循環的進行,允許你逐行處理查詢結果。
DQL語句
/*處理查詢結果集(遍歷結果集。)
*/
import java.sql.*;public class JDBCTest05{public static void main(String[] args){Connection conn = null;Statement stmt = null;ResultSet rs = null;try{//1、注冊驅動Class.forName("com.mysql.jdbc.Driver");//2、獲取連接conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");//3、獲取數據庫操作對象stmt = conn.createStatement();//4、執行sqlString sql = "select empno as a,ename,sal from emp";// int executeUpdate(insert/delete/update)// ResultSet executeQuery(select)rs = stmt.executeQuery(sql); // 專門執行DQL語句的方法。//5、處理查詢結果集/*boolean flag1 = rs.next();//System.out.println(flag1); // trueif(flag1){// 光標指向的行有數據// 取數據// getString()方法的特點是:不管數據庫中的數據類型是什么,都以String的形式取出。String empno = rs.getString(1); // JDBC中所有下標從1開始。不是從0開始。String ename = rs.getString(2);String sal = rs.getString(3);System.out.println(empno + "," + ename + "," + sal);}flag1 = rs.next();if(flag1){// 以下程序的1 2 3 說的第幾列。String empno = rs.getString(1);String ename = rs.getString(2);String sal = rs.getString(3);System.out.println(empno + "," + ename + "," + sal);}*/while(rs.next()){/*String empno = rs.getString(1);String ename = rs.getString(2);String sal = rs.getString(3);System.out.println(empno + "," + ename + "," + sal);*//*// 這個不是以列的下標獲取,以列的名字獲取//String empno = rs.getString("empno");String empno = rs.getString("a"); // 重點注意:列名稱不是表中的列名稱,是查詢結果集的列名稱。String ename = rs.getString("ename");String sal = rs.getString("sal");System.out.println(empno + "," + ename + "," + sal);*/// 除了可以以String類型取出之外,還可以以特定的類型取出。/*int empno = rs.getInt(1);String ename = rs.getString(2);double sal = rs.getDouble(3);System.out.println(empno + "," + ename + "," + (sal + 100));*/int empno = rs.getInt("a");String ename = rs.getString("ename");double sal = rs.getDouble("sal");System.out.println(empno + "," + ename + "," + (sal + 200));}}catch(Exception e){e.printStackTrace();}finally{//6、釋放資源if(rs != null){try{rs.close();}catch(Exception e){e.printStackTrace();}}if(stmt != null){try{stmt.close();}catch(Exception e){e.printStackTrace();}}if(conn != null){try{conn.close();}catch(Exception e){e.printStackTrace();}}}}
}
SQL注入
什么是SQL注入?
SQL注入的核心是:用戶輸入的參數改變了SQL的語法結構。
如何解決SQL注入?
只要用戶提供的信息不參與SQL語句的編譯過程,問題就解決了。即使用戶提供的信息中含有SQL語句的關鍵字,但是沒有參與編譯,仍然不起作用。要想用戶信息不參與SQL語句的編譯,那么必須使用java.sql.PreparedStatement,PreparedStatement接口繼承了java.sql.Statement, PreparedStatement是屬于預編譯的數據庫操作對象。,原理是:預先對SQL語句的框架進行編譯,然后再給SQL語句傳“值”。Statement編譯一次,執行一次,PreparedStatement編譯一次,執行N次。
- 明確sql語句的格式后,就不會改變了。剩余的內容都會認為是參數
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;/**什么情況下必須使用Statement呢?* 業務方面要求必須支持SQL注入的時候。* Statement支持SQL注入,凡是業務方面要求是需要進行sql語句拼接的,必須使用Statement。*/
public class JDBCTest07 {public static void main(String[] args) {// 初始化一個界面Map<String,String> userLoginInfo = initUI();// 驗證用戶名和密碼boolean loginSuccess = login(userLoginInfo);// 最后輸出結果System.out.println(loginSuccess ? "true" : "false");}/*** 用戶登錄* @param userLoginInfo 用戶登錄信息* @return false表示失敗,true表示成功*/private static boolean login(Map<String, String> userLoginInfo) {// 打標記的意識boolean loginSuccess = false;// 單獨定義變量String loginName = userLoginInfo.get("loginName");String loginPwd = userLoginInfo.get("loginPwd");// JDBC代碼Connection conn = null;PreparedStatement ps = null; // 這里使用PreparedStatement(預編譯的數據庫操作對象)ResultSet rs = null;try {// 1、注冊驅動Class.forName("com.mysql.jdbc.Driver");// 2、獲取連接conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root", "333");// 3、獲取預編譯的數據庫操作對象// SQL語句的框子。其中一個?,表示一個占位符,一個?將來接收一個“值”,注意:占位符不能使用單引號括起來。String sql = "select * from student where loginName = ? and loginPwd = ?";// 程序執行到此處,會發送sql語句框子給DBMS,然后DBMS進行sql語句的預先編譯。ps = conn.prepareStatement(sql);// 給占位符?傳值(第1個問號下標是1,第2個問號下標是2,JDBC中所有下標從1開始。)ps.setString(1, loginName);ps.setString(2, loginPwd);// 4、執行sqlrs = ps.executeQuery();// 5、處理結果集if(rs.next()){// 登錄成功loginSuccess = true;}} catch (Exception e) {e.printStackTrace();} finally {// 6、釋放資源if (rs != null) {try {rs.close();} catch (SQLException e) {e.printStackTrace();}}if (ps != null) {try {ps.close();} catch (SQLException e) {e.printStackTrace();}}if (conn != null) {try {conn.close();} catch (SQLException e) {e.printStackTrace();}}}return loginSuccess;}/*** 初始化用戶界面* @return 用戶輸入的用戶名和密碼等登錄信息*/private static Map<String, String> initUI() {Scanner s = new Scanner(System.in);System.out.print("用戶名:");String loginName = s.nextLine();System.out.print("密碼:");String loginPwd = s.nextLine();Map<String,String> userLoginInfo = new HashMap<>();userLoginInfo.put("loginName", loginName);userLoginInfo.put("loginPwd", loginPwd);return userLoginInfo;}
}
悲觀鎖(行級鎖)和樂觀鎖
悲觀鎖:事務必須排隊執行。數據鎖住了,不允許并發。(行級鎖:select后面添加forupdate)
樂觀鎖:支持并發,事務也不需要排隊,只不過需要一個版本號。
素材來源:動力節點杜聚賓老師。