教程揭秘 | 動力節(jié)點內(nèi)部Java零基礎教學文檔第三篇:JDBC
接上期后續(xù)
本期分享第三章節(jié)-JDBC
已經(jīng)分享兩章了,大家都跟上了嗎?
跟上的我就不得不說真厲害~
沒跟上的要加油了!
但時間還久,后面要學還有很多~
大家需保持耐心慢慢來
爭取你們學習的速度!
跟上我更新的速度哦~?

今日新篇章
【JDBC】
【主要內(nèi)容】
1.?JDBC概述
2.?使用JDBC完成添加操作
3.?使用JDBC完成更新和刪除
4.?DBUtils的簡單封裝
5.?使用JDBC完成查詢
6.?使用JDBC完成分頁查詢
7.?常用接口詳解
8.?JDBC批處理
9.?SQL注入問題
10.?事務處理解決轉(zhuǎn)賬問題
11.?連接池
12.?使用反射對DBUtils再次的封裝
13.?BaseDAO的封裝
【學習目標】

1.?JDBC概述
1.1?什么是JDBC
JDBC(Java DataBase Connectivity)就是Java數(shù)據(jù)庫連接,說白了就是用Java語言來操作數(shù)據(jù)庫。原來我們操作數(shù)據(jù)庫是在控制臺使用SQL語句來操作數(shù)據(jù)庫,JDBC是用Java語言向數(shù)據(jù)庫發(fā)送SQL語句。

1.2?JDBC的原理
早期SUN公司的天才們想編寫一套可以連接天下所有數(shù)據(jù)庫的API,但是當他們剛剛開始時就發(fā)現(xiàn)這是不可完成的任務,因為各個廠商的數(shù)據(jù)庫服務器差異太大了。后來SUN開始與數(shù)據(jù)庫廠商們討論,最終得出的結(jié)論是,由SUN提供一套訪問數(shù)據(jù)庫的規(guī)范(就是一組接口),并提供連接數(shù)據(jù)庫的協(xié)議標準,然后各個數(shù)據(jù)庫廠商會遵循SUN的規(guī)范提供一套訪問自己公司的數(shù)據(jù)庫服務器的API出現(xiàn)。SUN提供的規(guī)范命名為JDBC,而各個廠商提供的,遵循了JDBC規(guī)范的,可以訪問自己數(shù)據(jù)庫的API被稱之為驅(qū)動!

JDBC是接口,而JDBC驅(qū)動才是接口的實現(xiàn),沒有驅(qū)動無法完成數(shù)據(jù)庫連接!每個數(shù)據(jù)庫廠商都有自己的驅(qū)動,用來連接自己公司的數(shù)據(jù)庫。
當然還有第三方公司專門為某一數(shù)據(jù)庫提供驅(qū)動,這樣的驅(qū)動往往不是開源免費的!
1.3?程序員,JDBC,JDBC驅(qū)動的關系及說明
1.3.1?JDBC API
提供者:Sun公司
內(nèi)容:供程序員調(diào)用的接口與類,集成在java.sql和javax.sql包中,如
DriverManager類 ??作用:管理各種不同的JDBC驅(qū)動
Connection接口 ?
Statement接口 ????
ResultSet接口
1.3.2?JDBC 驅(qū)動
提供者:數(shù)據(jù)庫廠商
作用:負責連接各種不同的數(shù)據(jù)庫
1.3.3?Java程序員
?????????JDBC對Java程序員而言是API,對實現(xiàn)與數(shù)據(jù)庫連接的服務提供商而言是接口模型。
1.3.4?三方關系
SUN公司是規(guī)范制定者,制定了規(guī)范JDBC(連接數(shù)據(jù)庫規(guī)范)
數(shù)據(jù)庫廠商微軟、甲骨文等分別提供實現(xiàn)JDBC接口的驅(qū)動jar包
程序員學習JDBC規(guī)范來應用這些jar包里的類。

1.4?總結(jié)
簡單地說,JDBC 可做三件事:與數(shù)據(jù)庫建立連接、發(fā)送 操作數(shù)據(jù)庫的語句并處理結(jié)果。

DriverManager :依據(jù)數(shù)據(jù)庫的不同,管理JDBC驅(qū)動
Connection :負責連接數(shù)據(jù)庫并擔任傳送數(shù)據(jù)的任務 ?
Statement :由 Connection 產(chǎn)生、負責發(fā)送執(zhí)行SQL語句
ResultSet:負責保存Statement執(zhí)行后所產(chǎn)生的查詢結(jié)果
2.?JDBC操作數(shù)據(jù)庫的步驟
2.1?總體步驟
1.?加載一個Driver驅(qū)動
2.?創(chuàng)建數(shù)據(jù)庫連接(Connection)
3.?創(chuàng)建SQL命令發(fā)送器Statement
4.?創(chuàng)建SQL
5.?通過Statement發(fā)送SQL命令并得到結(jié)果
6.?處理SQL結(jié)果(select語句)
7.?關閉數(shù)據(jù)庫資源
ResultSet
Statement
Connection
2.2?詳細步驟
2.2.1?加載驅(qū)動
加載JDBC驅(qū)動是通過調(diào)用方法java.lang.Class.forName(),下面列出常用的幾種數(shù)據(jù)庫驅(qū)動程序加載語句的形式 :
Class.forName(“oracle.JDBC.driver.OracleDriver”);//使用Oracle的JDBC驅(qū)動程序
Class.forName(“com.microsoft.JDBC.sqlserver.SQLServerDriver”);//使用SQL Server的JDBC驅(qū)動程序
Class.forName(“com.ibm.db2.JDBC.app.DB2Driver”);//使用DB2的JDBC驅(qū)動程序
Class.forName("com.mysql.jdbc.Driver");//使用MySql的JDBC驅(qū)動程序
2.2.2?創(chuàng)建數(shù)據(jù)庫連接
與數(shù)據(jù)庫建立連接的方法是調(diào)用DriverManager.getConnection(String url, String user, String password )方法
Connection conn=null;
String url="jdbc:mysql://localhost:3306/whpowernode?useUnicode=true&useSSL=false&charsetUnicode=UTF8";
String user=“root";
String password=“123456";
conn = DriverManager.getConnection(url, user, password);
2.2.3?創(chuàng)建Statement并發(fā)送命令
Statement對象用于將 SQL 語句發(fā)送到數(shù)據(jù)庫中,或者理解為執(zhí)行sql語句
有三種 Statement對象:
Statement:用于執(zhí)行不帶參數(shù)的簡單SQL語句;
PreparedStatement(從 Statement 繼承):用于執(zhí)行帶或不帶參數(shù)的預編譯SQL語句;
CallableStatement(從PreparedStatement 繼承):用于執(zhí)行數(shù)據(jù)庫存儲過程的調(diào)用。

2.2.4?處理ResultSet結(jié)果
ResultSet對象是executeQuery()方法的返回值,它被稱為結(jié)果集,它代表符合SQL語句條件的所有行,并且它通過一套getXXX方法(這些get方法可以訪問當前行中的不同列)提供了對這些行中數(shù)據(jù)的訪問。
ResultSet里的數(shù)據(jù)一行一行排列,每行有多個字段,且有一個記錄指針,指針所指的數(shù)據(jù)行叫做當前數(shù)據(jù)行,我們只能來操作當前的數(shù)據(jù)行。我們?nèi)绻胍〉媚骋粭l記錄,就要使用ResultSet的next()方法 ,如果我們想要得到ResultSet里的所有記錄,就應該使用while循環(huán)。
ResultSet對象自動維護指向當前數(shù)據(jù)行的游標。每調(diào)用一次next()方法,游標向下移動一行。
初始狀態(tài)下記錄指針指向第一條記錄的前面,通過next()方法指向第一條記錄。循環(huán)完畢后指向最后一條記錄的后面。

2.2.5?關閉數(shù)據(jù)庫資源
作為一種好的編程風格,應在不需要Statement對象和Connection對象時顯式地關閉它們。關閉Statement對象和Connection對象的語法形式為:
public void close() throws SQLException
用戶不必關閉ResultSet。當它的 Statement 關閉、重新執(zhí)行或用于從多結(jié)果序列中獲取下一個結(jié)果時,該ResultSet將被自動關閉。
注意:要按先ResultSet結(jié)果集,后Statement,最后Connection的順序關閉資源,因為Statement和ResultSet是需要連接是才可以使用的,所以在使用結(jié)束之后有可能其他的Statement還需要連接,所以不能先關閉Connection。
3.?準備工作
3.1?創(chuàng)建數(shù)據(jù)并創(chuàng)建student表



3.2?創(chuàng)建項目


3.3?創(chuàng)建lib目錄并引入MYSQL驅(qū)動包

3.4?把lib包引入項目環(huán)境


4.?使用JDBC完成添加操作
4.1?步驟
??加載MySQL的JDBC驅(qū)動
??建立數(shù)據(jù)的連接
??創(chuàng)建SQL命令的發(fā)送器
??編寫SQL
??使用SQL命令發(fā)送器發(fā)送SQL命令并得到結(jié)果
??處理結(jié)果
??關閉數(shù)據(jù)庫資源
4.2?代碼
package com.bjpowernode.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class Test01Add {
????// 驅(qū)動器路徑
????private static final String DRIVER = "com.mysql.jdbc.Driver";
????//連接數(shù)據(jù)庫地址
????private static final String URL = "jdbc:mysql://localhost:3306/whpowernode?useUnicode=true&useSSL=false&characterEncoding=UTF8";
????//數(shù)據(jù)庫用戶名
????private static final String USER_NAME = "root";
????//數(shù)據(jù)庫密碼
????private static final String USER_PASSWORD = "123456";
????public static void main(String[] args) throws ClassNotFoundException, SQLException {
????????// 加載JDBC訪問Oracle的驅(qū)動
????????Class.forName(DRIVER);
????????// 建立和數(shù)據(jù)庫的連接
????????Connection conn = DriverManager.getConnection(URL, USER_NAME, USER_PASSWORD);
????????// 創(chuàng)建SQL命令發(fā)送器
????????Statement stmt = conn.createStatement();
????????// 使用SQL命令發(fā)送器發(fā)送SQL命令并得到結(jié)果
????????String sql = "insert into student values(1,'小剛',32,'男','湖北省武漢市')";
????????int n = stmt.executeUpdate(sql);
????????// 處理結(jié)果
????????if (n > 0) {
????????????System.out.println("添加成功");
????????} else {
????????????System.out.println("添加失敗");
????????}
????????// 關閉數(shù)據(jù)庫資源
????????stmt.close();
????????conn.close();
????}
}
4.3?URL詳解
4.3.1?為什么要定義URL
Java和MySQL是廠商的,Java程序和MySQL數(shù)據(jù)庫此時不在同一個進程下,此時Java程序需要向MySQL發(fā)送請求。
4.3.2?如何發(fā)送請求呢?
jdbc:mysql://localhost:3306/whpowernode?useUnicode=true&useSSL=false&characterEncoding=UTF-8
使用URL的方式發(fā)送
jdbc 主協(xié)議
mysql 子協(xié)議
localhost MySQL服務器的地址,如果服務器就是我自己的主機,那么定義localhost就可以了
3306 MySQL服務器的端口號
whpowernode MySQL數(shù)據(jù)庫服務器的數(shù)據(jù)庫名稱
useUnicode=true ?Java和MySQL交互使用Unicode編碼
useSSL=false Java和MySQL交互不使用安全層協(xié)議
characterEncoding=UTF-8 Java和MySQL交互的編碼方式為UTF-8 ?【如果不設置會有亂碼的】
4.4?查看數(shù)據(jù)庫

4.4.1?一個URL由哪些部分組成
??協(xié)議://服務器主機:端口/服務器路徑?查詢參數(shù)
??協(xié)議 jdbc:mysql:
??服務器主機 localhost
??端口 3306
??服務器路徑 whpowernode
??參數(shù)useUnicode=true&useSSL=false&characterEncoding=UTF8
5.?使用JDBC完成更新和刪除
5.1?步驟
??加載MySQL的JDBC驅(qū)動
??建立數(shù)據(jù)的連接
??創(chuàng)建SQL命令的發(fā)送器
??編寫SQL
??使用SQL命令發(fā)送器發(fā)送SQL命令并得到結(jié)果
??處理結(jié)果
??關閉數(shù)據(jù)庫資源
5.2?修改代碼
package com.bjpowernode.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class Test02Update {
????// 驅(qū)動器路徑
????private static final String DRIVER = "com.mysql.jdbc.Driver";
????//連接數(shù)據(jù)庫地址
????private static final String URL = "jdbc:mysql://localhost:3306/whpowernode?useUnicode=true&useSSL=false&characterEncoding=UTF8";
????//數(shù)據(jù)庫用戶名
????private static final String USER_NAME = "root";
????//數(shù)據(jù)庫密碼
????private static final String USER_PASSWORD = "123456";
????public static void main(String[] args) throws ClassNotFoundException, SQLException {
????????// 加載Oracle的JDBC驅(qū)動
????????Class.forName(DRIVER);
????????// 建立數(shù)據(jù)的連接
????????Connection conn=DriverManager.getConnection(URL, USER_NAME, USER_PASSWORD);
????????// 創(chuàng)建SQL命令的發(fā)送器
????????Statement stat=conn.createStatement();
????????// 編寫SQL
????????String sql="update student set name='小明',age=23,sex='女',address='武漢' where id=1";
????????// 使用SQL命令發(fā)送器發(fā)送SQL命令并得到結(jié)果
????????int res=stat.executeUpdate(sql);
????????// 處理結(jié)果
????????if(res>0){
????????????System.out.println("修改成功");
????????}
????????else{
????????????System.out.println("處理失敗");
????????}
????????// 關閉數(shù)據(jù)庫資源
????????stat.close();
????????conn.close();
????}
}
5.3?刪除代碼
package com.bjpowernode.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class Test03Delete {
????// 驅(qū)動器路徑
????private static final String DRIVER = "com.mysql.jdbc.Driver";
????//連接數(shù)據(jù)庫地址
????private static final String URL = "jdbc:mysql://localhost:3306/whpowernode?useUnicode=true&useSSL=false&characterEncoding=UTF8";
????//數(shù)據(jù)庫用戶名
????private static final String USER_NAME = "root";
????//數(shù)據(jù)庫密碼
????private static final String USER_PASSWORD = "123456";
????public static void main(String[] args) throws ClassNotFoundException, SQLException {
????????// 加載Oracle的JDBC驅(qū)動
????????Class.forName(DRIVER);
????????// 建立數(shù)據(jù)的連接
????????Connection conn=DriverManager.getConnection(URL, USER_NAME, USER_PASSWORD);
????????// 創(chuàng)建SQL命令的發(fā)送器
????????Statement stat=conn.createStatement();
????????// 編寫SQL
????????String sql="delete from student where id=1";
????????// 使用SQL命令發(fā)送器發(fā)送SQL命令并得到結(jié)果
????????int res=stat.executeUpdate(sql);
????????// 處理結(jié)果
????????if(res>0){
????????????System.out.println("刪除成功");
????????}
????????else{
????????????System.out.println("刪除失敗");
????????}
????????// 關閉數(shù)據(jù)庫資源
????????stat.close();
????????conn.close();
????}
}
6.?DBUtils的簡單封裝
6.1?為什么要封裝
從我們上面的代碼大家可以看到,每一次寫我們創(chuàng)建一個連接,創(chuàng)建一個發(fā)送SQL的對象,最后還要關閉,那么我們可以考慮把這重復的代碼提取出來!
6.2?創(chuàng)建DBUtils封裝代碼
package com.bjpowernode.utils;
import java.io.Closeable;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DBUtils {
????// 驅(qū)動器路徑
????private static final String DRIVER = "com.mysql.jdbc.Driver";
????// 連接數(shù)據(jù)庫地址
????private static final String URL = "jdbc:mysql://localhost:3306/whpowernode?useUnicode=true&useSSL=false&characterEncoding=UTF8";
????// 數(shù)據(jù)庫用戶名
????private static final String USER_NAME = "root";
????// 數(shù)據(jù)庫密碼
????private static final String USER_PASSWORD = "123456";
????/**
?????* 靜態(tài)加載驅(qū)動程序
?????*/
????static {
????????try {
????????????Class.forName(DRIVER);
????????} catch (ClassNotFoundException e) {
????????????e.printStackTrace();
????????}
????}
????/**
?????* @return 連接對象
?????*/
????public static Connection getConn() {
????????try {
????????????return ?DriverManager.getConnection(URL, USER_NAME, USER_PASSWORD);
????????} catch (SQLException e) {
????????????e.printStackTrace();
????????????System.out.println("創(chuàng)建連接對象異常");
????????}
????????return null;
????}
????/**
?????* 關閉資源
?????*/
????public static void close(AutoCloseable closeable) {
????????try {
????????????if (closeable != null) {
????????????????closeable.close();
????????????}
????????} catch (Exception e) {
????????????e.printStackTrace();
????????}
????}
}
6.3?創(chuàng)建上面的工具類對象前面的代碼進行改造
package com.bjpowernode.jdbc;
import com.bjpowernode.utils.DBUtils;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class Test01Add {
????public static void main(String[] args) throws ClassNotFoundException, SQLException {
????????Connection conn = DBUtils.getConn();
????????// 創(chuàng)建SQL命令發(fā)送器
????????Statement stmt = conn.createStatement();
????????// 使用SQL命令發(fā)送器發(fā)送SQL命令并得到結(jié)果
????????String sql = "insert into student values(1,'小剛',32,'男','湖北省武漢市')";
????????int n = stmt.executeUpdate(sql);
????????// 處理結(jié)果
????????if (n > 0) {
????????????System.out.println("添加成功");
????????} else {
????????????System.out.println("添加失敗");
????????}
????????// 關閉數(shù)據(jù)庫資源
????????DBUtils.close(stmt);
????????DBUtils.close(conn);
????}
}
?
7.?使用JDBC完成查詢
7.1?循環(huán)向student表里面插入20條數(shù)
package com.bjpowernode.jdbc;
import com.bjpowernode.utils.DBUtils;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Random;
public class Test01Add20 {
????public static void main(String[] args) throws ClassNotFoundException, SQLException {
????????Connection conn = DBUtils.getConn();
????????// 創(chuàng)建SQL命令發(fā)送器
????????Statement stmt = conn.createStatement();
????????// 使用SQL命令發(fā)送器發(fā)送SQL命令并得到結(jié)果
????????Random random=new Random();
????????for (int i = 1; i <=20 ; i++) {
????????????Integer id=i;
????????????String name="小明"+i;
????????????int age=random.nextInt(100);
????????????String sex=random.nextBoolean()?"男":"女";
????????????String address="武漢"+i;
????????????String sql = "insert into student values("+i+",'"+name+"',"+age+",'"+sex+"','"+address+"')";
????????????int n = stmt.executeUpdate(sql);
????????????// 處理結(jié)果
????????????if (n > 0) {
????????????????System.out.println("添加成功");
????????????} else {
????????????????System.out.println("添加失敗");
????????????}
????????}
????????// 關閉數(shù)據(jù)庫資源
????????DBUtils.close(stmt);
????????DBUtils.close(conn);
????}
}
7.2?查詢
package com.bjpowernode.jdbc;
import com.bjpowernode.utils.DBUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class Test04Query {
????public static void main(String[] args) throws ClassNotFoundException, SQLException {
????????Connection conn = DBUtils.getConn();
????????// 創(chuàng)建SQL命令發(fā)送器
????????Statement stmt = conn.createStatement();
????????// 編寫SQL
????????String sql="select *?from student";
????????// 使用SQL命令發(fā)送器發(fā)送SQL命令并得到結(jié)果
????????ResultSet rs=stmt.executeQuery(sql);
????????// 處理結(jié)果
????????while(rs.next()){
????????????int id=rs.getInt(1);
????????????String name=rs.getString(2);
????????????int age=rs.getInt(3);
????????????String sex=rs.getString(4);
????????????String address=rs.getString(5);
????????????System.out.println(id+" ?"+name+" ?"+age+" ??"+sex+" ??"+address);
????????}
????????// 關閉數(shù)據(jù)庫資源
????????DBUtils.close(rs);
????????DBUtils.close(stmt);
????????DBUtils.close(conn);
????}
}
8.?使用JDBC完成分頁查詢
package com.bjpowernode.jdbc;
import com.bjpowernode.utils.DBUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class Test05QueryForPage {
????public static void main(String[] args) throws ClassNotFoundException, SQLException {
????????Connection conn = DBUtils.getConn();
????????// 創(chuàng)建SQL命令發(fā)送器
????????Statement stmt = conn.createStatement();
????????int pageNum=2; //頁碼
????????int pageSize=5;//每頁顯示的條數(shù)
????????// 編寫SQL
????????String sql="select *?from student limit?"+(pageNum-1)*pageSize+","+pageSize;
????????// 使用SQL命令發(fā)送器發(fā)送SQL命令并得到結(jié)果
????????ResultSet rs=stmt.executeQuery(sql);
????????// 處理結(jié)果
????????while(rs.next()){
????????????int id=rs.getInt(1);
????????????String name=rs.getString(2);
????????????int age=rs.getInt(3);
????????????String sex=rs.getString(4);
????????????String address=rs.getString(5);
????????????System.out.println(id+" ?"+name+" ?"+age+" ??"+sex+" ??"+address);
????????}
????????// 關閉數(shù)據(jù)庫資源
????????DBUtils.close(rs);
????????DBUtils.close(stmt);
????????DBUtils.close(conn);
????}
}
9.?常用接口詳解
9.1?DriverManager
用于管理JDBC驅(qū)動的服務類。程序中使用該類的的主要功能是獲取Connection對象,該類包含如下方法:
public static Connection getConnection(String url, String user, String password) throws SQLException
該方法獲得url對應數(shù)據(jù)庫的連接;
9.2?Connection
代表數(shù)據(jù)庫連接對象,每個Connection代表一個物理連接會話。要想訪問數(shù)據(jù)庫,必須先得到數(shù)據(jù)庫連接。該接口的常用方法如下:
Statement createStatement() throws SQLException; 該方法返回一個Statement對象;
PreparedStatement prepareStatement(String sql)throws SQLException;該方法返回預編譯的Statement對象,即將SQL語句提交到數(shù)據(jù)庫進行預編譯;
CallableStatement prepareCall(String sql) throws SQLException;
該方法返回CallableStatement對象,該對象用于調(diào)用存儲過程。
上面上個方法都返回用于執(zhí)行sql語句的Statement對象,PreparedStatement和CallableStatement是Statement的子類,只有獲得了Statement之后才可以執(zhí)行sql語句;
除此之外,Connection還有如下幾個用于控制事務的方法。
Savepoint setSavepoint() throws SQLException;創(chuàng)建一個保存點;
Savepoint setSavepoint(String name) throws SQLException;以指定名字來創(chuàng)建一個保存點;
void setTransactionIsolation(int level) throws SQLException;設置事務的隔離級別;
void rollback() throws SQLException;回滾事務;
void rollback(Savepoint savepoint) throws SQLException;將事務回滾到指定的保存點;
void setAutoCommit(boolean autoCommit) throws SQLException;關閉自動提交,打開事務;
void commit() throws SQLException;提交事務;
9.3?Statement
用于執(zhí)行sql語句的工具接口。該對象既可以執(zhí)行DDL,DCL語句,也可以用于執(zhí)行DML語句,還可以用于執(zhí)行sql查詢。當執(zhí)行sql查詢時,返回查詢到的結(jié)果集。它的常用方法如下:
ResultSet executeQuery(String sql) throws SQLException;該方法用于執(zhí)行查詢語句,并返回查詢結(jié)果對應ResultSet對象。該方法只能用于執(zhí)行查詢語句。
int executeUpdate(String sql) throws SQLException;該方法用于執(zhí)行DML語句,并返回受影響的行數(shù);該方法也可用于執(zhí)行DDL語句,執(zhí)行DDL語句將返回0;
boolean execute(String sql) throws SQLException;改方法可以執(zhí)行任何sql語句。如果執(zhí)行后第一個結(jié)果為ResultSet對象,則返回true;如果執(zhí)行后第一個結(jié)果為受影響的行數(shù)或沒有任何結(jié)果,則返回false;
9.4?PreparedStatement
預編譯的Statement對象,PreparedStatement是Statement的子接口,它允許數(shù)據(jù)庫預編譯sql語句(這些sql語句通常帶有參數(shù)),以后每次只改變sql命令的參數(shù),避免數(shù)據(jù)庫每次都需要編譯sql語句,無需再傳入sql語句,
只要為預編譯的sql語句傳入?yún)?shù)值即可。所以它比Statement多了如下方法:
void setXxx(int parameterIndex, Xxx value):該方法根據(jù)傳入?yún)?shù)值的類型不同,需要使用不同的方法。傳入的值根據(jù)索引傳給sql語句中指定位置的參數(shù)。
9.5?ResultSet
結(jié)果集對象。該對象包含訪問查詢結(jié)果的方法,ResultSet可以通過列索引或列名獲得列數(shù)據(jù)。它包含了如下常用方法來移動記錄指針。
void close() throws SQLException;釋放ResultSet對象;
boolean absolute( int row ) throws SQLException;將結(jié)果集的記錄指針移動到第row行,如果row是負數(shù),則移動到倒數(shù)第row行,如果移動后的記錄指針指向一條有效記錄,則該方法返回true;
boolean next() throws SQLException;將結(jié)果集的記錄指針定位到下一行,如果移動后的記錄指針指向一條有效的記錄,則該方法返回true;
boolean last() throws SQLException;將結(jié)果集的記錄指針定位到最后一行,如果移動后的記錄指針指向一條有效的記錄,則該方法返回true;
?
10.?SQL注入問題
10.1?問題引入
10.1.1?創(chuàng)建sys_user表并初始化數(shù)據(jù)


10.1.2?編寫代碼實現(xiàn)登陸
package com.bjpowernode.jdbc;
import com.bjpowernode.utils.DBUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;
public class Test06Login {
????public static void main(String[] args) throws ClassNotFoundException, SQLException {
????????Connection conn = DBUtils.getConn();
????????// 創(chuàng)建SQL命令發(fā)送器
????????Statement stmt = conn.createStatement();
????????//從鍵盤輸入
????????Scanner scanner=new Scanner(System.in);
????????System.out.println("請輸入用戶名:");
????????String username = scanner.nextLine();
????????System.out.println("請輸入密碼:");
????????String password=scanner.nextLine();
????????// 編寫SQL
????????String sql="select *?from sys_user where username='"+username+"' and password='"+password+"'";
????????// 使用SQL命令發(fā)送器發(fā)送SQL命令并得到結(jié)果
????????ResultSet rs=stmt.executeQuery(sql);
????????System.out.println(sql);
????????// 處理結(jié)果
????????if(rs.next()){
????????????System.out.println("登陸成功");
????????????int id=rs.getInt(1);
????????????String name=rs.getString(2);
????????????String pwd=rs.getString(3);
????????????System.out.println(id+" ?"+name+" ?"+pwd);
????????}
????????// 關閉數(shù)據(jù)庫資源
????????DBUtils.close(rs);
????????DBUtils.close(stmt);
????????DBUtils.close(conn);
????}
}
10.1.3?測試登陸

10.2?解決辦法【使用PreparedStatement】
10.2.1?技術(shù)原理
該 PreparedStatement接口繼承Statement,并與之在兩方面有所不同:
PreparedStatement 實例包含已編譯的 SQL 語句。這就是使語句“準備好”。包含于 PreparedStatement 對象中的 SQL 語句可具有一個或多個 IN 參數(shù)。IN參數(shù)的值在 SQL 語句創(chuàng)建時未被指定。相反的,該語句為每個 IN 參數(shù)保留一個問號(“?”)作為占位符。每個問號的值必須在該語句執(zhí)行之前,通過適當?shù)膕etXXX 方法來提供。
由于 PreparedStatement 對象已預編譯過,所以其執(zhí)行速度要快于 Statement 對象。因此,多次執(zhí)行的 SQL 語句經(jīng)常創(chuàng)建為 PreparedStatement 對象,以提高效率。
作為 Statement 的子類,PreparedStatement 繼承了 Statement 的所有功能。另外它還添加了一整套方法,用于設置發(fā)送給數(shù)據(jù)庫以取代 IN 參數(shù)占位符的值。同時,三種方法 execute、 executeQuery 和 executeUpdate 已被更改以使之不再需要參數(shù)。這些方法的 Statement 形式(接受 SQL 語句參數(shù)的形式)不應該用于 PreparedStatement 對象。
?
10.2.2?創(chuàng)建對象
以下的代碼段(其中 con 是 Connection 對象)創(chuàng)建包含帶兩個 IN 參數(shù)占位符的 SQL 語句的 PreparedStatement 對象:
PreparedStatement pstmt = con.prepareStatement("UPDATE table4 SET m = ? WHERE x = ?");
pstmt 對象包含語句 "UPDATE table4 SET m = ? WHERE x = ?",它已發(fā)送給DBMS,并為執(zhí)行作好了準備。
?
10.2.3?傳遞參數(shù)
在執(zhí)行 PreparedStatement 對象之前,必須設置每個 ? 參數(shù)的值。這可通過調(diào)用 setXXX 方法來完成,其中 XXX 是與該參數(shù)相應的類型。例如,如果參數(shù)具有Java 類型 long,則使用的方法就是 setLong。setXXX 方法的第一個參數(shù)是要設置的參數(shù)的序數(shù)位置,第二個參數(shù)是設置給該參數(shù)的值。例如,以下代碼將第一個參數(shù)設為 123456789,第二個參數(shù)設為 100000000:
pstmt.setLong(1, 123456789);
pstmt.setLong(2, 100000000);
一旦設置了給定語句的參數(shù)值,就可用它多次執(zhí)行該語句,直到調(diào)用clearParameters 方法清除它為止。在連接的缺省模式下(啟用自動提交),當語句完成時將自動提交或還原該語句。
如果基本數(shù)據(jù)庫和驅(qū)動程序在語句提交之后仍保持這些語句的打開狀態(tài),則同一個 PreparedStatement 可執(zhí)行多次。如果這一點不成立,那么試圖通過使用PreparedStatement 對象代替 Statement 對象來提高性能是沒有意義的。
?
10.2.4?修改代碼
package com.bjpowernode.jdbc;
import com.bjpowernode.utils.DBUtils;
import java.sql.*;
import java.util.Scanner;
public class Test07Login {
????public static void main(String[] args) throws ClassNotFoundException, SQLException {
????????//從鍵盤輸入
????????Scanner scanner=new Scanner(System.in);
????????System.out.println("請輸入用戶名:");
????????String username = scanner.nextLine();
????????System.out.println("請輸入密碼:");
????????String password=scanner.nextLine();
????????Connection conn = DBUtils.getConn();
????????// 編寫SQL
????????String sql="select *?from sys_user where username=? and password=? ";
????????System.out.println(sql);
????????// 創(chuàng)建SQL命令發(fā)送器
????????PreparedStatement pstmt = conn.prepareStatement(sql);
????????pstmt.setString(1,username);
????????pstmt.setString(2,password);
????????// 使用SQL命令發(fā)送器發(fā)送SQL命令并得到結(jié)果
????????ResultSet rs=pstmt.executeQuery();
????????// 處理結(jié)果
????????if(rs.next()){
????????????System.out.println("登陸成功");
????????????int id=rs.getInt(1);
????????????String name=rs.getString(2);
????????????String pwd=rs.getString(3);
????????????System.out.println(id+" ?"+name+" ?"+pwd);
????????}
????????// 關閉數(shù)據(jù)庫資源
????????DBUtils.close(rs);
????????DBUtils.close(pstmt);
????????DBUtils.close(conn);
????}
}
10.2.5?再次測試

11.?1事務處理解決轉(zhuǎn)賬問題
11.1?什么是事務
是數(shù)據(jù)庫操作的最小工作單元,是作為單個邏輯工作單元執(zhí)行的一系列操作;這些操作作為一個整體一起向系統(tǒng)提交,要么都執(zhí)行、要么都不執(zhí)行;事務是一組不可再分割的操作集合(工作邏輯單元)
11.2?事務的四大特性:
11.2.1?原子性
事務是數(shù)據(jù)庫的邏輯工作單位,事務中包含的各操作要么都做,要么都不做
11.2.2?一致性
事 務執(zhí)行的結(jié)果必須是使數(shù)據(jù)庫從一個一致性狀態(tài)變到另一個一致性狀態(tài)。因此當數(shù)據(jù)庫只包含成功事務提交的結(jié)果時,就說數(shù)據(jù)庫處于一致性狀態(tài)。如果數(shù)據(jù)庫系統(tǒng) 運行中發(fā)生故障,有些事務尚未完成就被迫中斷,這些未完成事務對數(shù)據(jù)庫所做的修改有一部分已寫入物理數(shù)據(jù)庫,這時數(shù)據(jù)庫就處于一種不正確的狀態(tài),或者說是 不一致的狀態(tài)。
11.2.3?隔離性
一個事務的執(zhí)行不能其它事務干擾。即一個事務內(nèi)部的操作及使用的數(shù)據(jù)對其它并發(fā)事務是隔離的,并發(fā)執(zhí)行的各個事務之間不能互相干擾。
11.2.4?持久性
也稱永久性,指一個事務一旦提交,它對數(shù)據(jù)庫中的數(shù)據(jù)的改變就應該是永久性的。接下來的其它操作或故障不應該對其執(zhí)行結(jié)果有任何影響。
11.3?需求描述
完成轉(zhuǎn)賬操作
要求,兩次數(shù)據(jù)庫操作,要么全成功。要么全失敗
11.4?準備工作
11.4.1?創(chuàng)建表
CREATE TABLE account
(
????aid INT PRIMARY KEY AUTO_INCREMENT,
????aname VARCHAR(30) NOT NULL,
????amount ?DECIMAL(10,2) NOT NULL
);
11.4.2?初始化數(shù)據(jù)

11.5?代碼實現(xiàn)
package com.bjpowernode.jdbc;
import com.bjpowernode.utils.DBUtils;
import java.sql.*;
import java.util.Scanner;
public class Test08Transaction {
????public static void main(String[] args) {
????????//聲明連接對象
????????Connection conn=null;
????????//聲明發(fā)送SQL的接口對象
????????Statement stmt=null;
????????try {
????????????//創(chuàng)建連接對象
????????????conn = DBUtils.getConn();
????????????// 關閉自動提交事務
????????????conn.setAutoCommit(false);
????????????// 編寫SQL
????????????String sql1 = "update account set amount = amount-1000 where aid=1";
????????????String sql2 = "update account set amount = amount+1000 where aid=2";
????????????// 創(chuàng)建SQL命令發(fā)送器
????????????stmt = conn.createStatement();
????????????stmt.executeUpdate(sql1);
????????????stmt.executeUpdate(sql2);
????????????????????}catch (Exception e){
????????????e.printStackTrace();
????????????try {
????????????????conn.rollback();
????????????} catch (SQLException e1) {
????????????????e1.printStackTrace();
????????????}
????????}finally {
conn.commit();
????????????// 關閉數(shù)據(jù)庫資源
????????????DBUtils.close(stmt);
????????????DBUtils.close(conn);
????????}
????}
}
?
11.6?測試
在兩個執(zhí)行的sql中間模擬異常,看事務是否會提交
?
?
12.?JDBC批處理
12.1?什么是批處理
批處理是建立一次連接(創(chuàng)建一個Connection對象)的情況下批量執(zhí)行多個DML語句,這些DML語句要么全部成功要么全部失敗。如何確保全部成功or全部失敗呢?在JDBC中開啟事務,使用事務管理DML語句。
12.2?需求描述及操作步驟
使用批處理根據(jù)id批量的刪除student表中的數(shù)據(jù)
1 定義SQL配置文件
2 創(chuàng)建Connection對象
3 創(chuàng)建PreparedStatement對象
4 將提交方式設置為手動提交,開啟事務
5 設置占位符
6 將占位符添加到批處理中(相當于收集若干個本子,放入包包中)
7 執(zhí)行批處理
8 提交事務
9 如果批處理失敗,在catch塊中回滾事務
10 關閉資源
12.3?案列代碼
package com.bjpowernode.jdbc;
import com.bjpowernode.utils.DBUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Arrays;
import java.util.List;
public class Test09Batch {
????public static void main(String[] args) {
????????//模擬要刪除的數(shù)據(jù)
????????List<Integer> ids= Arrays.asList(1,2,3,4,5);
????????//聲明連接對象
????????Connection conn=null;
????????//聲明發(fā)送SQL的接口對象
????????PreparedStatement pstmt=null;
????????try {
????????????//創(chuàng)建連接對象
????????????conn = DBUtils.getConn();
????????????// 關閉自動提交事務
????????????conn.setAutoCommit(false);
????????????// 編寫SQL
????????????String sql = "delete from student where id = ?;";
????????????// 創(chuàng)建SQL命令發(fā)送器
????????????pstmt = conn.prepareStatement(sql);
????????????for (Integer id : ids) {
????????????????pstmt.setInt(1,id);
????????????????pstmt.addBatch();
????????????}
????????????int[] rows = pstmt.executeBatch();
????????????System.out.println("受影響的行數(shù)為:"+Arrays.toString(rows));
????????????conn.commit();
????????}catch (Exception e){
????????????e.printStackTrace();
????????????try {
????????????????conn.rollback();
????????????} catch (SQLException e1) {
????????????????e1.printStackTrace();
????????????}
????????}finally {
????????????// 關閉數(shù)據(jù)庫資源
????????????DBUtils.close(pstmt);
????????????DBUtils.close(conn);
????????}
????}
}
13.?數(shù)據(jù)庫連接池
13.1?什么是連接池
連接池是創(chuàng)建和管理一個連接的緩沖池的技術(shù),這些連接準備好被任何需要它們的線程使用。
連接池是裝有連接的容器,使用連接的話,可以從連接池中進行獲取,使用完成之后將連接歸還給連接池。
13.2?為什么使用連接池
連接對象創(chuàng)建和銷毀是需要耗費時間的,在服務器初始化的時候就初始化一些連接。把這些連接放入到內(nèi)存中,使用的時候可以從內(nèi)存中獲取,使用完成之后將連接放入連接池中。從內(nèi)存中獲取和歸還的效率要遠遠高于創(chuàng)建和銷毀的效率。(提升性能)。
13.3?連接池的工作原理

13.4?市面上有哪些可用的連接池
??c3p0 ?老了
??druid 阿里的
??dbcp 老了
??Hikari ?小日本的,springboot官方推薦的
14.?BaseDAO的封裝【難點】
14.1?概述 husband------------------------->Husband(List<T> 、T)
對于JDBC市面上有一些封裝的非常好的ORM(對象關系映射)框架,如mybatis ?mybatis plus hibernate,但是我們現(xiàn)在還沒有學到框架,如何自己做模擬一個ORM的框架呢,接下來我們來講解BaseDAO的封裝和使用
?
14.2?代碼
package com.bjpowernode.dao.impl;
import com.bjpowernode.utils.DBUtils;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class BaseDao {
????/**
?????* @param sql ???sql 指令
?????* @param clss ??orm關聯(lián)類
?????* @param params sql中占位符 對應的參數(shù)
?????* @param <T>
?????* @return
?????* @throws Exception
?????*/
????public ?<T> List<T> selectList(String sql, Class<T> clss, Object... params) ?{
????????//創(chuàng)建一個空容器 ?規(guī)避空指針異常問題
????????List<T> data = new ArrayList<>(); // 需要改動
????????//2. 創(chuàng)建連接 使用驅(qū)動管理器 創(chuàng)建連接
????????Connection conn = DBUtils.getConnection();
????????//4. 獲取指令的指令對象
????????PreparedStatement prep = null;
????????ResultSet rs = null;
????????try {
????????????//4. 獲取指令的指令對象
????????????prep = conn.prepareStatement(sql);
????????????//設置預編譯參數(shù)
????????????for (int i = 0; i < params.length; i++) {
????????????????Object param = params[i]; //獲取參數(shù)
????????????????//設置參數(shù)
????????????????prep.setObject(i + 1, param);
????????????}
????????????//5. 執(zhí)行指令
????????????rs = prep.executeQuery();
????????????//獲取結(jié)果的元信息
????????????ResultSetMetaData metaData = rs.getMetaData();
????????????//獲取列數(shù)
????????????int columnCount = metaData.getColumnCount();
????????????//遍歷結(jié)果
????????????while (rs.next()) {
????????????????T t = clss.newInstance();
????????????????for (int i = 0; i < columnCount; i++) {
????????????????????//獲取別名 ?也是屬性名
????????????????????String columnLabel = metaData.getColumnLabel(i + 1);
????????????????????//根據(jù)別名獲取值
????????????????????Object columnValue = rs.getObject(columnLabel);
????????????????????//獲取屬性
????????????????????Field field = clss.getDeclaredField(columnLabel); //需要改動
????????????????????//設置屬性值
????????????????????field.setAccessible(true);
????????????????????field.set(t, columnValue);
????????????????}
????????????????data.add(t);//放入list
????????????}
????????}catch (Exception e){
????????????e.printStackTrace();
????????}finally {
????????????DBUtils.close(conn);
????????????DBUtils.close(prep);
????????????DBUtils.close(rs);
????????}
????????return data;
????}
????/**
?????* 查詢單個對象
?????*
?????* @param sql
?????* @param clss
?????* @param params
?????* @param <T>
?????* @return
?????* @throws Exception
?????*/
????public ?<T> T selectOne(String sql, Class<T> clss, Object... params) ?{
????????List<T> list = selectList(sql, clss, params);
????????if (!list.isEmpty() && list.size() == 1) {
????????????return list.get(0);
????????}
????????return null;
????}
????/**
?????* 通用的更新操作
?????* @param sql
?????* @param params
?????* @return
?????*/
????public ?boolean update(String sql, Object... params) {
????????//1. 獲取連接
????????Connection conn = DBUtils.getConnection();
????????PreparedStatement prep = null;
????????try {
????????????prep = conn.prepareStatement(sql);
????????????//設置預編譯參數(shù)
????????????for (int i = 0; i < params.length; i++) {
????????????????Object param = params[i]; //獲取參數(shù)
????????????????//設置參數(shù)
????????????????prep.setObject(i + 1, param);
????????????}
????????????//執(zhí)行sql指令
????????????int m = prep.executeUpdate();
????????????return m >= 1;
????????} catch (SQLException throwables) {
????????????throwables.printStackTrace();
????????}finally {
????????????DBUtils.close(conn);
????????????DBUtils.close(prep);
????????}
????????return ?false;
????}
}
14.3?使用
package com.bjpowernode.dao.impl;
public class UserDao ?extends BaseDao{
????/**
?????* 根據(jù)用戶名 和 密碼查詢用戶
?????* @param username
?????* @param password
?????* @return
?????*/
????public User selectUser(String username,String password){
????????String sql = "select ?`id`, `username`, `password`, `realname`, `role`, `deleted`, `img`, `create_time` as createTime, `modify_time` as modifyTime, `deleted_time` as deletedTime from user where username = ? and password=?";
????????User user = super.selectOne(sql, User.class, username, password);
????????return user;
????};
????public void updateState(String id, Integer delete) {
????????String sql = "update user set deleted = ??,deleted_time = now() where id = ?";
????????super.update(sql,delete,id);
????}
}
14.4?分頁封裝
14.4.1?創(chuàng)建分頁結(jié)果封裝類PageInfo<T>
package com.bjpowernode.common;
import java.util.List;
public class PageInfo<T> {
????private List<T> data;// 具體的數(shù)據(jù)
????private Long count; //符合條件的數(shù)據(jù)條數(shù)
????public List<T> getData() {
????????return data;
????}
????public void setData(List<T> data) {
????????this.data = data;
????}
????public Long getCount() {
????????return count;
????}
????public void setCount(Long count) {
????????this.count = count;
????}
}
14.4.2?修改BaseDao
/**
?* 分頁的封裝
?* @param sql
?* @param cls
?* @param page
?* @param limit
?* @param <T>
?* @return
?*/
protected <T> PageInfo<T> selectPage(String sql, Class<T> cls, String page, String limit) {
????//查詢符合條件 頁碼數(shù)據(jù)
????Integer pageNo = 0;
????if (Integer.parseInt(page) > 0){
????????pageNo = (Integer.parseInt(page) - 1) * Integer.parseInt(limit);
????}
????String listSql = sql + " limit ?"+pageNo+","+limit;
????//查詢列表數(shù)據(jù)
????List<T> users = selectList(listSql, cls);
????//查詢總數(shù)據(jù)條數(shù)
????Long count = getCount(sql);
????PageInfo<T> pageInfo = new PageInfo<>();
????pageInfo.setData(users);
????pageInfo.setCount(count);
????return pageInfo;
}
/**
?* 查詢總數(shù)據(jù)條數(shù)
?* @param sql
?* @return
?*/
private Long getCount(String sql){
????String countSql = "select count(1) from ("+sql+") rs";
????Connection conn = DBUtils.getConnection();
????PreparedStatement prep = null;
????ResultSet rs = null;
????try {
????????prep = conn.prepareStatement(countSql);
????????rs = prep.executeQuery();
????????rs.next();
????????Long count = rs.getLong(1);
????????return count;
????} catch (SQLException throwables) {
????????throwables.printStackTrace();
????}
????return ?0L;
};

更多干貨我們下期再說!
下期會分享
第四章節(jié)
HTML_CSS_JavaScript
相關知識~
下期見!?
