學習目標
- 理解
JDBC
原理 - 掌握
Connection
接口的使用 - 掌握
Statement
接口的使用 - 掌握
ResultSet
接口的使用 - 掌握
PreparedStatement
接口的使用 - 掌握
Properties
類與配置文件的使用
JDBC 概念
JDBC (Java DataBase Connectivity
)
Java數據庫連接技術的簡稱,提供連接各種常用數據庫的能力
說白了就是java語言連接數據庫
有一個程序員,他要寫一套程序,但是他不知道公司用什么數據庫所以,他就得學java連mysql連Oracle,連DB2,
市面上所有的關系型數據庫,他都得學習一遍,對吧!而我們期望使用統一的一套Java代碼可以操作所有的關系型數據庫
有一個程序員終于忍不住了,寫了個JDBC
JDBC:定義了操作所有關系型數據庫的規則(接口)這里只是寫了接口,但是沒有寫具體的實現類,那么這個實現類誰寫呢sun公司說了,每一個數據庫的廠商你們自己寫實現類
所以每個數據庫廠商都寫了不同的實現類,不同版本的實現類我們給這個實現類起了個名字,叫做數據庫驅動
JDBC
本質:其實就是官方(Sun
公司)定義的一套操作所有關系型數據庫的規則,及接口.
各個數據庫廠商去實現這套接口,提供數據庫驅動jar
包.我們可以使用這套接口(JDBC
)編程,真正執行的代碼是驅動jar
包中的實現類.
JDBC
快速入門
步驟:
-
導入驅動
jar
包
官網mysql
驅動jar
包下載地址:mysql/mysql-connector-java- 復制
mysql-connector-java-5.1.37.jar
- 右鍵–>
Add as library
- 復制
-
注冊驅動
-
獲取數據庫的連接對象
-
定義
sql
-
獲取執行
sql
語句的對象statement
-
執行
sql
,接收返回結果 -
處理結果
-
釋放資源
實例代碼
別管能不能看懂,直接我就上一堆代碼,然后再解釋
package cn.itcast.jdbc;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;/*** @author victor* @site https://victorfengming.github.io/* @company XDL* @project itcast* @package cn.itcast.jdbc* @created 2019-11-07 23:43* @function ""*/
public class JdbcDemo01 {public static void main(String[] args) throws Exception {// 1. 導入驅動jar包// 2.注冊驅動Class.forName("com.mysql.jdbc.Driver");// 3.獲取數據庫連接對象Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/plan","root","");//4. 定義sqlString sql = "use plan";//5. 獲取執行sql語句的對象 statementStatement stmt = conn.createStatement();//6. 執行sql,接收返回結果int count = stmt.executeUpdate(sql);//7. 處理結果System.out.println(count);//8. 釋放資源stmt.clearBatch();conn.close();}
}
詳解各個對象
DriverManager
:
驅動管理對象,用于管理JDBC
驅動, 功能如下:
1. 注冊驅動
- 注冊與給定的驅動程序
static void registerDriver(Driver driver)
- 寫代碼使用:
Class.forName("com.mysql.jdbc.Driver");
好像這個類中有一個靜態代碼塊,這里面有現成的代碼- 通過查看源碼發現:在
com.mysql.jdbc.Driver
類中存在靜態代碼塊
static {try {DriverManager.registerDriver(new Driver());} catch (SQLException var1) {throw new RuntimeException("Can't register driver!");}}
- 補充:
- 在
mysql-connector-java-5.1.37.jar
的5
版本之后 - 有一個文件在
mysql-connector-java-5.1.37.jar/META-INF/services/java.sql.Driver
里面 - 這個文件可以讓你省略注冊驅動的步驟,好吧!
- 在
2. 獲取數據庫連接:
方法:static Connection getConnection(String url,String user,String password)
參數:
- url
:指定連接的路徑
- 語法:jdbc:mysql://ip地址(域名):端口號/數據庫名稱
- 例子:jdbc:mysql://localhost:3306/plan
- 細節:如果連接的是本機的mysql
服務器,并且mysql服務器默認端口是3306,則url可以簡寫為:jdbc:mysql///數據庫名稱
- 例如:Connection conn = DriverManager.getConnection("jdbc:mysql:///plan","root","");
- user
:用戶名
- password
:密碼
Connection
:數據庫連接對象,用于連接數據庫并傳送數據
1. 獲取執行sql
的對象
Statement createStatement
PreparedStatement prepareStatement(String sql)
2. 管理事務:
- 開啟事務:
setAutoCommit(boolean autoCommit)
: 調用改方法設置參數為false
,就開啟事務啦,哈哈 - 提交事務:
commit()
- 回滾事務:
rollback()
Statement
:執行sql
的對象,負責執行SQL
語句
執行sql
boolean execute(String sql)
:可以執行任意的sql
,可能會返回多個結果!(了解即可)int executeUpdate(String sql)
:- 執行
DML(insert,update,delete)
語句,DDL(create,alter,drop)
語句 - 這個
DDL
不經常用,所有在這個地方我們都是用前者的 - 返回值: 執行語句后,所影響的行數,我們可以通過這個影響的行數判斷DML語句是否執行成功,要是大于0就成功了唄,反之,則失敗.
- 執行
ResultSet executeQuery(String sql)
: 執行DQL(select)
語句
練習:見附錄
ResultSet
:結果集對象,負責保存Statement執行后所產生的查詢結果
next()
:
- 游標向下移動一行
- 并且判斷當前行是否是最后一行的末尾
- 返回
boolean
,如果有數據返回true,反之亦然!
getXxx(參數)
:獲取數據
- Xxx:代表數據類型 如:
int getInt(), String getString()
- 參數:
int
: 代表列的編號, 從1開始 如getString(1)
String
: 代表列名稱. 如:getDouble("balance")
- 注意:
使用步驟:
- 游標向下移動一行
- 判斷是否有數據
- 獲取數據
- 練習:
定義一個方法,查詢emp表的數據將其封裝為對象,然后裝載集合,返回.
- 定義
Emp
類 用于封裝Emp
表數據的JavaBean
- 定義方法
public List<Emp> findAll(){}
- 實現方法
select * from emp;
PreparedStatement
: 執行sql
的對象,用于負責執行SQL
語句
1. SQL注入問題:在拼接sql時,有一些特殊關鍵字參與字符串的拼接.會造成安全性的問題.
- 輸入用戶名隨便,輸入密碼:
a' or 'a' = 'a
- sql:
select * from user where username = 'victor' and password = 'a' or 'a' = 'a'
2. 解決sql注入問題:使用PreparedStatement對象來解決,他是
3. 預編譯sql:參數使用?作為占位符
4. 使用步驟:
-
導入驅動包
-
注冊驅動
-
獲取數據庫對象連接
-
定義sql
- 注意:sql的參數使用?作為占位符.如:
select * from user where username = ? and password = ?;
-
獲取執行sql語句的對象PreparedStatement Connection.prepareStatement(String sql)
-
給? 賦值:
- 方法: setXxx(參數1,參數2)
- 參數1:?的位置編號 從1 開始
- 參數2:?的值
-
執行sql,接受返回結果,不需要傳遞sql語句
-
處理結果
-
釋放資源
5. 注意:后期都會使用PreparedStatement來完成增刪改查的所有操作
- 可以防止SQL注入
- 效率更高
抽取JDBC工具類 : JdbcUtils.java
- 目的 : 簡化書寫
- 分析 :
1. 注冊驅動
2. 抽取一個方法獲取連接對象
需求 : 不想傳遞參數(麻煩),還得保證工具類的通用性.
解決 : 配置文件 jdbc.properties
url=jdbc:mysql:///plan
user=root
password=
driver=com.mysql.jdbc.Driver
- 練習:
- 需求 :
- 通過鍵盤錄入用戶名和密碼
- 判斷用戶是否登錄成功
select * from user where username = "and password = ""
- 如果這個sql有結果,則查詢成功,反之失敗
- 步驟 :
創建數據庫表 user
create table user(id int primary key auto_increment,username varchar(32),password varchar(32)
);
插入數據
insert into user
values (null,"victor","123");insert into user
values (null,"ttkr","1907123");
JDBC控制事務:
1. 事務: 一個包含多個步驟的業務操作.如果這個業務操作被事務管理,則這多個步驟要么同事成功,要么同時失敗.
2. 操作:
- 開始事務
- 提交事務
- 回滾事務
3. 使用Connection對象來管理事務
- 開啟事務:setAutoCommit(boolean autoCommit) :調用改方法設置參數為false,即開啟事務
- 提交事務: commit()
- 回滾事務: rollback()
- catch中回滾事務
附錄(源代碼)
account
表 添加一條記錄
package cn.itcast.jdbc;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;/*** @author victor* @site https://victorfengming.github.io/* @company XDL* @project itcast* @package cn.itcast.jdbc* @created 2019-11-08 14:41* @function "用于插入數據"*/
public class JdbcDemo02 {public static void main(String[] args) {Statement stmt = null;try {// 1.注冊驅動// 這行不寫也行// Class.forName("com.mysql.jdbc.Driver");// 2. 定義sql// String sql = "insert into account values(null,'ttk',6299)";String sql = "insert into account values(null,'yupeng',5666)";// 獲取Connection對象Connection conn = DriverManager.getConnection("jdbc:mysql:///plan", "root", "");// 4獲取執行sql對象 Statementstmt = conn.createStatement();// 5 執行sqlint count = stmt.executeUpdate(sql);// count影響的行數// 6.處理結果System.out.println(count);if (count > 0) {System.out.println("提交成功");} else {System.out.println("添加失敗");}} catch (Exception e) {e.printStackTrace();} finally {// 釋放資源// 為了避免空指針異常需要判斷if (stmt != null) {try {stmt.close();} catch (SQLException e) {e.printStackTrace();}}}}
}
account
表 刪除一條記錄
package cn.itcast.jdbc;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;/*** @author victor* @site https://victorfengming.github.io/* @company XDL* @project itcast* @package cn.itcast.jdbc* @created 2019-11-08 15:35* @function "從表中刪除記錄"*/
public class JdbcDemo04 {public static void main(String[] args) {Connection conn = null;Statement stmt = null;try {// 1.注冊表// 2.獲取連接對象conn = DriverManager.getConnection("jdbc:mysql:///plan","root","");// 3. 定義sqlString sql = "delete from account where id = 4";// 4.獲取執行sql對象stmt = conn.createStatement();// 5.執行sqlint count = stmt.executeUpdate(sql);// 6.處理結果if (count > 0) {System.out.println("運行成功");} else {System.out.println("失敗了!");}} catch (Exception e) {e.printStackTrace();}finally {// 7. 釋放資源if (stmt != null) {try {stmt.close();} catch (SQLException e) {e.printStackTrace();}}}}}
account
表 修改一條記錄
package cn.itcast.jdbc;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;/*** @author victor* @site https://victorfengming.github.io/* @company XDL* @project itcast* @package cn.itcast.jdbc* @created 2019-11-08 15:16* @function "account表修改記錄"*/
public class JdbcDemo03 {public static void main(String[] args) {Connection conn = null;Statement stmt = null;try {// 1.注冊表// 2.獲取連接對象conn = DriverManager.getConnection("jdbc:mysql:///plan","root","");// 3. 定義sqlString sql = "update account set balance = 6060 where id = 2";// 4.獲取執行sql對象stmt = conn.createStatement();// 5.執行sqlint count = stmt.executeUpdate(sql);// 6.處理結果if (count > 0) {System.out.println("運行成功");} else {System.out.println("失敗了!");}} catch (Exception e) {e.printStackTrace();}finally {// 7. 釋放資源if (stmt != null) {try {stmt.close();} catch (SQLException e) {e.printStackTrace();}}}}
}
DDl
: 創建一個表
package cn.itcast.jdbc;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;/*** @author victor* @site https://victorfengming.github.io/* @company XDL* @project itcast* @package cn.itcast.jdbc* @created 2019-11-08 15:40* @function "用于創建一個新表"*/
public class JdbcDemo05 {public static void main(String[] args) {Connection conn = null;Statement stmt = null;try {// 1.注冊表// 2.獲取連接對象conn = DriverManager.getConnection("jdbc:mysql:///plan","root","");// 3. 定義sqlString sql = "create table student (id int ,name varchar(20))";// 4.獲取執行sql對象stmt = conn.createStatement();// 5.執行sqlint count = stmt.executeUpdate(sql);// 6.處理結果if (count > 0) {System.out.println("運行成功");} else {System.out.println("失敗了!");}} catch (Exception e) {e.printStackTrace();}finally {// 7. 釋放資源if (stmt != null) {try {stmt.close();} catch (SQLException e) {e.printStackTrace();}}}}
}
account
表 查詢一條記錄
package cn.itcast.jdbc;import java.sql.*;/*** @author victor* @site https://victorfengming.github.io/* @company XDL* @project itcast* @package cn.itcast.jdbc* @created 2019-11-08 16:01* @function "查詢是重中之重"*/
public class JdbcDemo06 {public static void main(String[] args) {Connection conn = null;Statement stmt = null;ResultSet rs = null;try {// 1.注冊表// 2.獲取連接對象conn = DriverManager.getConnection("jdbc:mysql:///plan", "root", "");// 3. 定義sqlString sql = "select * from account";// 4.獲取執行sql對象stmt = conn.createStatement();// 5.執行sqlrs = stmt.executeQuery(sql);// 6.處理結果// 6.1 讓游標向下移動一行// 循環判斷游標是否最后一行末尾while (rs.next()) {// 獲取數據int id = rs.getInt(1);String name = rs.getString("name");double balance = rs.getDouble(3);System.out.println(id + "---" + name + "---" + balance);}} catch (Exception e) {e.printStackTrace();} finally {// 7. 釋放資源if (stmt != null) {try {stmt.close();} catch (SQLException e) {e.printStackTrace();}}}}
}
工具類
package cn.itcast.util;import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.sql.*;
import java.util.Properties;/*** @author victor* @site https://victorfengming.github.io/* @company XDL* @project itcast* @package cn.itcast.util* @created 2019-11-09 10:40* @function "JDBC工具類"*/
public class JdbcUtils {//聲明三個成員變量// 因為只有靜態變量才能被靜態代碼塊所訪問,被靜態方法所訪問private static String url;private static String user;private static String password;private static String driver;/*** 文件的讀取,只需要讀取一次即可拿到這些值* 我們可以使用靜態代碼塊* 因為靜態代碼塊是隨著類加載進行加載的* 只讀取一次*/static {// 讀取資源文件,獲取值try {// 1. 創建 Properties集合類Properties pro = new Properties();// 2.加載文件
// pro.load(new FileReader("D:\\IdeaProjects\\itcast\\day04-JDBC\\src\\jdbc.properties"));// 這么寫指定不行// 獲取src路徑下的文件的方式-->ClassLoader 類加載器ClassLoader classLoader = JdbcUtils.class.getClassLoader();// 這里的相對路徑就是srcURL res = classLoader.getResource("jdbc.properties");// 這里URL表示統一資源對應符String path = res.getPath();
// System.out.println(path);pro.load(new FileReader(path));// 3.獲取屬性,賦值url = pro.getProperty("url");user = pro.getProperty("user");password = pro.getProperty("password");driver = pro.getProperty("driver");// 4. 注冊驅動Class.forName(driver);} catch (IOException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}}/*** 獲取連接* @return 連接對象*/
// public static Connection getConnection(String url,String user,String password,String driver) throws SQLException {
// return DriverManager.getConnection(url, user, password);
// }
// 這個地方,我既不想用傳遞參數的方式,還想要簡化書寫,這可咋整,我們這里使用配置文件的方式,所以要這樣寫public static Connection getConnection() throws SQLException {return DriverManager.getConnection(url, user, password);}/*** 釋放資源方法*/public static void close(Statement stmt, Connection conn) {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(ResultSet rs, Statement stmt, Connection conn) {if (rs != null) {try {rs.close();} catch (SQLException e) {e.printStackTrace();}}if (stmt != null) {try {stmt.close();} catch (SQLException e) {e.printStackTrace();}}if (conn != null) {try {conn.close();} catch (SQLException e) {e.printStackTrace();}}}
}
查詢emp表的數據將其封裝為對象
package cn.itcast.jdbc;import cn.itcast.domain.Emp;
import cn.itcast.util.JdbcUtils;import java.sql.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;/*** @author victor* @site https://victorfengming.github.io/* @company XDL* @project itcast* @package cn.itcast.jdbc* @created 2019-11-08 23:38* @function "定義一個方法,查詢emp表的數據將其封裝為對象,然后裝載集合,返回"*/
public class JdbcDemo08 {public static void main(String[] args) {List<Emp> list = new JdbcDemo08().findAll2();for (Emp emp : list) {System.out.println(emp);}}/*** 查詢所有emp對象** @return*/public List<Emp> findAll() {//提前聲明一些變量,以便于能夠在try和catch中來回穿梭Connection conn = null;Statement stmt = null;ResultSet rs = null;List<Emp> list = null;try {// 1.注冊,為了能夠兼容mysql5以下的版本,所以寫上Class.forName("com.mysql.jdbc.Driver");// 2.獲取連接conn = DriverManager.getConnection("jdbc:mysql:///plan", "root", "");// 3.定義sqlString sql = "select * from emp";// 4. 獲取執行sql的對象stmt = conn.createStatement();// 5.執行sqlrs = stmt.executeQuery(sql);// 先創建一個引用就okEmp emp = null;list = new ArrayList<Emp>();// 6.遍歷結果集while (rs.next()) {// 7.獲取數據int id = rs.getInt("id");String ename = rs.getString("ename");int job_id = rs.getInt("job_id");int mgr = rs.getInt("mgr");// 這個sqlDate是Date 的子類,所以是可以直接進行賦值的Date joindate = rs.getDate("joindate");double bonus = rs.getDouble("bonus");double salary = rs.getDouble("salary");int dept_id = rs.getInt("dept_id");// 創建emp對象emp = new Emp();emp.setId(id);emp.setEname(ename);emp.setJob_id(job_id);emp.setMgr(mgr);emp.setJoindate(joindate);emp.setBonus(bonus);emp.setSalary(salary);emp.setDept_id(dept_id);// 裝載集合list.add(emp);}} catch (Exception e) {e.printStackTrace();}finally {// 7. 釋放資源if (stmt != null) {try {stmt.close();} catch (SQLException e) {e.printStackTrace();}}if (rs != null) {try {rs.close();} catch (SQLException e) {e.printStackTrace();}}if (conn != null) {try {conn.close();} catch (SQLException e) {e.printStackTrace();}}}return list;} /*** 演示JDBC工具類** @return*/public List<Emp> findAll2() {//提前聲明一些變量,以便于能夠在try和catch中來回穿梭Connection conn = null;Statement stmt = null;ResultSet rs = null;List<Emp> list = null;try {// 1.注冊,為了能夠兼容mysql5以下的版本,所以寫上// 2.獲取連接conn = JdbcUtils.getConnection();// 3.定義sqlString sql = "select * from emp";// 4. 獲取執行sql的對象stmt = conn.createStatement();// 5.執行sqlrs = stmt.executeQuery(sql);// 先創建一個引用就okEmp emp = null;list = new ArrayList<Emp>();// 6.遍歷結果集while (rs.next()) {// 7.獲取數據int id = rs.getInt("id");String ename = rs.getString("ename");int job_id = rs.getInt("job_id");int mgr = rs.getInt("mgr");// 這個sqlDate是Date 的子類,所以是可以直接進行賦值的Date joindate = rs.getDate("joindate");double bonus = rs.getDouble("bonus");double salary = rs.getDouble("salary");int dept_id = rs.getInt("dept_id");// 創建emp對象emp = new Emp();emp.setId(id);emp.setEname(ename);emp.setJob_id(job_id);emp.setMgr(mgr);emp.setJoindate(joindate);emp.setBonus(bonus);emp.setSalary(salary);emp.setDept_id(dept_id);// 裝載集合list.add(emp);}} catch (Exception e) {e.printStackTrace();}finally {// 7. 釋放資源JdbcUtils.close(rs,stmt,conn);}return list;}
}
用戶登錄案例
package cn.itcast.jdbc;import cn.itcast.util.JdbcUtils;
import cn.itcast.util.python;import java.sql.*;/*** @author victor* @site https://victorfengming.github.io/* @company XDL* @project itcast* @package cn.itcast.jdbc* @created 2019-11-09 11:52* @function "- 需求 :* - 通過鍵盤錄入用戶名和密碼* - 判斷用戶是否登錄成功"*/
public class JdbcDemo09 {public static void main(String[] args) {// 1.鍵盤錄入,接收用戶名和密碼String username = python.input("請輸入用戶名:");String password = python.input("請輸入密碼:");// 2.調用用方法//創建對象boolean flag = new JdbcDemo09().login(username, password);// 3.判斷結果,輸出不同語句if (flag) {System.out.println("登錄成功");} else {System.out.println("用戶名或者密碼錯誤");}}/*** 登錄方法*/public boolean login(String username, String password) {Connection conn = null;PreparedStatement pstmt = null;ResultSet rs = null;try {if (username != null || password != null) {;}// 連接數據庫判斷是否登錄成功// 1.獲取連接conn = JdbcUtils.getConnection();// 2.定義sqlString sql = "select * from user where username = ? and password = ?";System.out.println(sql);// 3獲取執行sql的對象// stmt = conn.createStatement();// 升級版本的pstmt = conn.prepareStatement(sql);// 4執行查詢// rs = pstmt.executeQuery(sql);// 這里不需要傳遞參數了rs = pstmt.executeQuery();// 5判斷// 要是查到了, 唉, 就直接就返回true// if (rs.next()) {// // 如果有下一行則返回true// return true;// } else {// return false;// }// 上面的代碼就是垃圾代碼// 如果有下一行則返回truereturn rs.next();} catch (SQLException e) {e.printStackTrace();// 去改配置文件, 數據庫要db4// 這里就體現了JdbcUtils類的可擴展性很強了,不用改代碼都}finally {// 釋放資源JdbcUtils.close(rs,pstmt,conn);}return false;}
}
轉賬案例
package cn.itcast.jdbc;import cn.itcast.util.JdbcUtils;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;/*** @author victor* @site https://victorfengming.github.io/* @company XDL* @project itcast* @package cn.itcast.jdbc* @created 2019-11-09 14:07* @function "事務操作"*/
public class JdbcDemo10 {public static void main(String[] args) {transfer();}/*** 轉賬函數*/public static void transfer() {Connection conn = null;PreparedStatement pstmt1 = null;PreparedStatement pstmt2 = null;try {// 1.獲取來接conn = JdbcUtils.getConnection();// 2. 定義sql//2.1 victor+500String sql1 = "update account set balance = balance + ? where id = ?";// 2.2 ttkr -50String sql2 = "update account set balance = balance - ? where id = ?";// 3.獲取執行sql對象pstmt1 = conn.prepareStatement(sql1);pstmt2 = conn.prepareStatement(sql2);// 設置參數pstmt1.setDouble(1,500);pstmt1.setDouble(2,1);pstmt2.setDouble(1,500);pstmt2.setDouble(2,2);// 5.執行sqlpstmt1.executeUpdate();// 手動制造異常int i = 3/0;pstmt2.executeUpdate();} catch (Exception e) {// 事務回滾try {if (conn != null) {conn.rollback();}} catch (SQLException ex) {ex.printStackTrace();}e.printStackTrace();}finally {JdbcUtils.close(pstmt1,conn);JdbcUtils.close(pstmt2,null);}}
}