JDBC基礎:JDBC快速入門,JDBC工具類,SQL注入攻擊,JDBC管理事務
一、JDBC快速入門
1.jdbc的概念
-
JDBC(Java DataBase Connectivity:java資料庫連接)是一種用於執行SQL語句的Java API,可以為多種關係型資料庫提供統一訪問,它是由一組用Java語言編寫的類和介面組成的。
-
JDBC的作用:可以通過java程式碼操作資料庫
2.jdbc的本質
-
其實就是java官方提供的一套規範(介面)。用於幫助開發人員快速實現不同關係型資料庫的連接!
-
圖示:
-
上圖解釋:
-
如果沒有jdbc,程式設計師想操作mysql,就需要學習mysql的一套操作規範
-
程式設計師想操作oracle,就需要學習oracle的一套操作規範,db2也是
-
這樣做太麻煩了,這時候java就出了一套規範jdbc,用來操作所有的關係型資料庫,然後各個資料庫廠商支援這個規範就可以了
-
那這樣的話,程式設計師就只需要學習jdbc這一套規範即可
-
3.jdbc的快速入門程式
-
導入jar包
資料中有一個壓縮包:mysql-connector-java-5.1.37.zip
解壓出來,找到mysql-connector-java-5.1.37-bin.jar -
註冊驅動
Class.forName("com.mysql.jdbc.Driver");
-
獲取連接
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/db2", "root", "root");
-
獲取執行者對象
Statement stat = con.createStatement();
-
執行sql語句,並接收返回結果
String sql = "SELECT * FROM user";
ResultSet rs = stat.executeQuery(sql); -
處理結果
while(rs.next()) {
System.out.println(rs.getInt("id") + "\t" + rs.getString("name"));
} -
釋放資源
rs.close();
stat.close();
con.close(); -
創建一個java項目:JDBC基礎
-
將jar包導入,並添加到引用類庫
-
新建com.itheima01.JDBCDemo01
package com.itheima01;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class JDBCDemo01 {
public static void main(String[] args) throws Exception{
//1.導入jar包
//2.註冊驅動
Class.forName("com.mysql.jdbc.Driver");
//3.獲取連接 (連接的資料庫名是db2,第二個第三個參數是連接資料庫的用戶名密碼)
Connection con = DriverManager.getConnection("jdbc:mysql://192.168.59.129:3306/db2","root","itheima");
//4.獲取執行者對象 (statement:表現,聲明,跟程式意思不匹配)
Statement stat = con.createStatement();
//5.執行sql語句,並且接收結果
String sql = "SELECT * FROM user";
ResultSet rs = stat.executeQuery(sql); //execute執行,query:查詢,resultset:結果集
//6.處理結果
while(rs.next()) {
System.out.println(rs.getInt("id") + "\t" + rs.getString("name"));
}
//7.釋放資源
rs.close();
stat.close();
con.close();
}
}
二、JDBC各個功能類詳解 ***
1.DriverManager
-
DriverManager:驅動管理對象
-
註冊驅動(告訴程式該使用哪一個資料庫驅動)
-
註冊給定的驅動程式:static void registerDriver(Driver driver) (DriverManager的方法)
-
我們在剛剛的入門案例中並沒有註冊驅動,也成功了,咋回事呢
-
-
這是因為我們使用了Class.forName:Class.forName(“com.mysql.jdbc.Driver”)
-
我們通過了給forName指定了是mysql的驅動
-
Class.forName()會載入,初始化指定的類
-
Driver類內部會幫助我們註冊驅動,如下:
-
-
在com.mysql.jdbc.Driver類中存在靜態程式碼塊(通過查看源碼發現)
//這是com.mysql.jdbc.Driver的靜態程式碼塊,只要使用這個類,就會執行這段程式碼
//而Class.forName("com.mysql.jdbc.Driver")就正好使用到了這個類
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}-
注意:我們不需要通過DriverManager調用靜態方法registerDriver(),因為只要Driver類被使用,則會執行其靜態程式碼塊完成註冊驅動
-
mysql5之後可以省略註冊驅動的步驟。在jar包中,存在一個java.sql.Driver配置文件,文件中指定了com.mysql.jdbc.Driver
-
所以後邊我們其實可以省略註冊驅動的步驟(可以注釋掉上個案例的註冊驅動的步驟,也可以查詢到數據)
-
-
-
獲取資料庫連接(獲取到資料庫的連接並返回連接對象)
-
static Connection getConnection(String url, String user, String password);
-
返回值:Connection資料庫連接對象
-
參數
-
url:指定連接的路徑。語法:jdbc:mysql://ip地址(域名):埠號/資料庫名稱
-
user:用戶名
-
password:密碼
-
-
-
2.Connection
-
Connection:資料庫連接對象
-
獲取執行者對象
-
獲取普通執行者對象:Statement createStatement();
-
獲取預編譯執行者對象:PreparedStatement prepareStatement(String sql);
-
-
管理事務
-
開啟事務:setAutoCommit(boolean autoCommit); 參數為false,則開啟事務。
-
提交事務:commit();
-
回滾事務:rollback();
-
-
釋放資源
-
立即將資料庫連接對象釋放:void close();
-
-
3.Statement
-
Statement:執行sql語句的對象
-
執行DML語句:int executeUpdate(String sql);
-
返回值int:返回影響的行數。
-
參數sql:可以執行insert、update、delete語句。
-
-
執行DQL語句:ResultSet executeQuery(String sql);
-
返回值ResultSet:封裝查詢的結果。
-
參數sql:可以執行select語句。
-
-
釋放資源
-
立即將執行者對象釋放:void close();
-
-
4.ResultSet
-
ResultSet:結果集對象
-
判斷結果集中是否還有數據:boolean next();
-
有數據返回true,並將索引向下移動一行
-
沒有數據返回false
-
-
獲取結果集中的數據:XXX getXxx(“列名”);
-
XXX代表數據類型(要獲取某列數據,這一列的數據類型)
-
例如:String getString(“name”); int getInt(“age”);
-
-
釋放資源
-
立即將結果集對象釋放:void close();
-
-
游標,相當於是箭頭
-
三、JDBC案例student學生表的CRUD ***
1. 數據準備
-
資料庫和數據表
-- 創建db14資料庫
CREATE DATABASE db14;
-- 使用db14資料庫
USE db14;
-- 創建student表
CREATE TABLE student(
sid INT PRIMARY KEY AUTO_INCREMENT, -- 學生id
NAME VARCHAR(20), -- 學生姓名
age INT, -- 學生年齡
birthday DATE -- 學生生日
);
-- 添加數據
INSERT INTO student VALUES (NULL,'張三',23,'1999-09-23'),(NULL,'李四',24,'1998-08-10'),(NULL,'王五',25,'1996-06-06'),(NULL,'趙六',26,'1994-10-20');
-
實體類
-
Student類,成員變數對應表中的列
-
注意:所有的基本數據類型需要使用包裝類,以防null值無法賦值
-
資料庫的查詢結果可能是 null,因為自動拆箱,用基本數據類型接收有 空指針 風險
-
-
新建com.itheima02.domain.Student (保存實體類的包名,可以用bean,也可以用domain)
-
package com.itheima02.domain;
import java.util.Date;
public class Student {
private Integer sid;
private String name;
private Integer age;
private Date birthday;
public Student() {
}
public Student(Integer sid, String name, Integer age, Date birthday) {
this.sid = sid;
this.name = name;
this.age = age;
this.birthday = birthday;
}
public Integer getSid() {
return sid;
}
public void setSid(Integer sid) {
this.sid = sid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
-
本案例會使用分層思想,所以需要新建幾個包
-
dao:數據持久層:操作資料庫
//定義dao介面:StudentDao
//本案例要處理五個功能,所以dao介面中定義五個抽象方法
package com.itheima02.dao;
import com.itheima02.domain.Student;
import java.util.ArrayList;
/*
Dao層介面
*/
public interface StudentDao {
//查詢所有學生資訊
public abstract ArrayList<Student> findAll();
//條件查詢,根據id獲取學生資訊
public abstract Student findById(Integer id);
//新增學生資訊
public abstract int insert(Student stu);
//修改學生資訊
public abstract int update(Student stu);
//刪除學生資訊
public abstract int delete(Integer id);
}
-
service:業務層:處理業務邏輯,調用dao
//定義service介面:StudentService
package com.itheima02.service;
import com.itheima02.domain.Student;
import java.util.ArrayList;
/*
Service層介面
*/
public interface StudentService {
//查詢所有學生資訊
public abstract ArrayList<Student> findAll();
//條件查詢,根據id獲取學生資訊
public abstract Student findById(Integer id);
//新增學生資訊
public abstract int insert(Student stu);
//修改學生資訊
public abstract int update(Student stu);
//刪除學生資訊
public abstract int delete(Integer id);
}
-
controller:控制層,調用業務層方法,將數據返回給前端介面(不過目前我們這個案例沒有與介面結合)
//定義控制層類:StudentController,提前準備好對應的測試方法
package com.itheima02.controller;
import org.junit.Test;
public class StudentController {
/*
查詢所有學生資訊
*/
2. 需求一:查詢全部(查詢所有學生資訊)
-
持久層:新建StudentDaoImpl實現StudentDao,並重寫所有方法,我們先來處理第一個邏輯:
/*
查詢所有學生資訊
*/
-
業務層:新建StudentServiceImpl,實現StudentService,重寫所有方法,然後先處理第一個邏輯:
private StudentDao dao = new StudentDaoImpl();
/*
查詢所有學生資訊
*/
-
控制層
private StudentService service = new StudentServiceImpl(); /* 查詢所有學生資訊 */ @Test public void findAll() { ArrayList<Student> list = service.findAll(); for(Student stu : list) { System.out.println(stu); } }
3. 需求二:條件查詢(根據id查詢學生資訊)
-
持久層
/* 條件查詢,根據id查詢學生資訊 */ @Override public Student findById(Integer id) { Student stu = new Student(); Connection con = null; Statement stat = null; ResultSet rs = null; try{ //1.註冊驅動 Class.forName("com.mysql.jdbc.Driver"); //2.獲取資料庫連接 con = DriverManager.getConnection("jdbc:mysql://192.168.59.129:3306/db14", "root", "itheima"); //3.獲取執行者對象 stat = con.createStatement(); //4.執行sql語句,並且接收返回的結果集 String sql = "SELECT * FROM student WHERE sid='"+id+"'"; // 引引加加 String sql = "SELECT * FROM student WHERE sid='"+id+"'"; // SELECT * FROM student WHERE sid='1' String sql = "SELECT * FROM student WHERE sid='"+id+"'"; String sql = "SELECT * FROM student WHERE sid=" +id; rs = stat.executeQuery(sql); //5.處理結果集 while(rs.next()) { Integer sid = rs.getInt("sid"); String name = rs.getString("name"); Integer age = rs.getInt("age"); Date birthday = rs.getDate("birthday"); //封裝Student對象 stu.setSid(sid); stu.setName(name); stu.setAge(age); stu.setBirthday(birthday); } } catch(Exception e) { e.printStackTrace(); } finally { //6.釋放資源 if(con != null) { try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } if(stat != null) { try { stat.close(); } catch (SQLException e) { e.printStackTrace(); } } if(rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } } //將對象返回 return stu; }
-
業務層
/* 條件查詢,根據id查詢學生資訊 */ @Override public Student findById(Integer id) { return dao.findById(id); }
-
控制層
/* 條件查詢,根據id查詢學生資訊 */ @Test public void findById() { Student stu = service.findById(3); System.out.println(stu); }
4. 需求三:新增數據 (添加學生)
-
持久層
/* 添加學生資訊 */ @Override public int insert(Student stu) { Connection con = null; Statement stat = null; int result = 0; try{ //1.註冊驅動 Class.forName("com.mysql.jdbc.Driver"); //2.獲取資料庫連接 con = DriverManager.getConnection("jdbc:mysql://192.168.59.129:3306/db14", "root", "itheima"); //3.獲取執行者對象 stat = con.createStatement(); //4.執行sql語句,並且接收返回的結果集 Date d = stu.getBirthday(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String birthday = sdf.format(d); // 字元串與多個變數進行拼接,將寫死的內容 替換為 "+變數+" String sql = "INSERT INTO student VALUES ('"+stu.getSid()+"','"+stu.getName()+"','"+stu.getAge()+"','"+birthday+"')"; result = stat.executeUpdate(sql); } catch(Exception e) { e.printStackTrace(); } finally { //6.釋放資源 if(con != null) { try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } if(stat != null) { try { stat.close(); } catch (SQLException e) { e.printStackTrace(); } } } //將結果返回 return result; }
-
業務層
/* 新增學生資訊 */ @Override public int insert(Student stu) { return dao.insert(stu); }
-
控制層
/* 新增學生資訊 */ @Test public void insert() { Student stu = new Student(5,"周七",27,new Date()); int result = service.insert(stu); if(result != 0) { System.out.println("新增成功"); }else { System.out.println("新增失敗"); } }
5. 需求四:修改數據(修改學生資訊)
-
持久層
/* 修改學生資訊 */ @Override public int update(Student stu) { Connection con = null; Statement stat = null; int result = 0; try{ //1.註冊驅動 Class.forName("com.mysql.jdbc.Driver"); //2.獲取資料庫連接 con = DriverManager.getConnection("jdbc:mysql://192.168.59.129:3306/db14", "root", "itheima"); //3.獲取執行者對象 stat = con.createStatement(); //4.執行sql語句,並且接收返回的結果集 Date d = stu.getBirthday(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String birthday = sdf.format(d); String sql = "UPDATE student SET sid='"+stu.getSid()+"',name='"+stu.getName()+"',age='"+stu.getAge()+"',birthday='"+birthday+"' WHERE sid='"+stu.getSid()+"'"; result = stat.executeUpdate(sql); } catch(Exception e) { e.printStackTrace(); } finally { //6.釋放資源 if(con != null) { try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } if(stat != null) { try { stat.close(); } catch (SQLException e) { e.printStackTrace(); } } } //將結果返回 return result; }
-
業務層
/* 修改學生資訊 */ @Override public int update(Student stu) { return dao.update(stu); }
-
控制層
/* 修改學生資訊 */ @Test public void update() { Student stu = service.findById(5); stu.setName("周七七"); int result = service.update(stu); if(result != 0) { System.out.println("修改成功"); }else { System.out.println("修改失敗"); } }
6. 需求五:刪除數據(刪除學生)
-
持久層
/* 刪除學生資訊 */ @Override public int delete(Integer id) { Connection con = null; Statement stat = null; int result = 0; try{ //1.註冊驅動 Class.forName("com.mysql.jdbc.Driver"); //2.獲取資料庫連接 con = DriverManager.getConnection("jdbc:mysql://192.168.59.129:3306/db14", "root", "itheima"); //3.獲取執行者對象 stat = con.createStatement(); //4.執行sql語句,並且接收返回的結果集 String sql = "DELETE FROM student WHERE sid='"+id+"'"; result = stat.executeUpdate(sql); } catch(Exception e) { e.printStackTrace(); } finally { //6.釋放資源 if(con != null) { try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } if(stat != null) { try { stat.close(); } catch (SQLException e) { e.printStackTrace(); } } } //將結果返回 return result; }
-
業務層
/* 刪除學生資訊 */ @Override public int delete(Integer id) { return dao.delete(id); }
-
控制層
/* 刪除學生資訊 */ @Test public void delete() { int result = service.delete(5); if(result != 0) { System.out.println("刪除成功"); }else { System.out.println("刪除失敗"); } }
四、JDBC工具類
-
為啥需要工具類呢?
-
因為在上個案例中的dao層的程式碼,很多都是重複的
-
程式設計師一但碰到重複程式碼,就要想辦法解決
1.工具類的抽取 ***
-
配置文件(在src下創建config.properties)
driverClass=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/db14 username=root password=itheima
-
工具類:com.itheima02.utils.JDBCUtils
package com.itheima02.utils; import java.io.InputStream; import java.sql.*; import java.util.Properties; /* JDBC工具類 */ public class JDBCUtils { //1.私有構造方法 private JDBCUtils(){}; //2.聲明配置資訊變數 private static String driverClass; private static String url; private static String username; private static String password; private static Connection con; //3.靜態程式碼塊中實現載入配置文件和註冊驅動 static{ try{ //通過類載入器返回配置文件的位元組流 InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("config.properties"); //創建Properties集合,載入流對象的資訊 Properties prop = new Properties(); prop.load(is); //獲取資訊為變數賦值 driverClass = prop.getProperty("driverClass"); url = prop.getProperty("url"); username = prop.getProperty("username"); password = prop.getProperty("password"); //註冊驅動 Class.forName(driverClass); } catch (Exception e) { e.printStackTrace(); } } //4.獲取資料庫連接的方法 public static Connection getConnection() { try { con = DriverManager.getConnection(url,username,password); } catch (SQLException e) { e.printStackTrace(); } return con; } //5.釋放資源的方法 public static void close(Connection con, Statement stat, ResultSet rs) { if(con != null) { try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } if(stat != null) { try { stat.close(); } catch (SQLException e) { e.printStackTrace(); } } if(rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } } public static void close(Connection con, Statement stat) { close(con,stat,null); } }
2.使用工具類優化student表的CRUD
-
查詢全部:修改StudentDaoImpl 中的方法
/* 查詢所有學生資訊 */ @Override public ArrayList<Student> findAll() { ArrayList<Student> list = new ArrayList<>(); Connection con = null; Statement stat = null; ResultSet rs = null; try{ con = JDBCUtils.getConnection(); //3.獲取執行者對象 stat = con.createStatement(); //4.執行sql語句,並且接收返回的結果集 String sql = "SELECT * FROM student"; rs = stat.executeQuery(sql); //5.處理結果集 while(rs.next()) { Integer sid = rs.getInt("sid"); String name = rs.getString("name"); Integer age = rs.getInt("age"); Date birthday = rs.getDate("birthday"); //封裝Student對象 Student stu = new Student(sid,name,age,birthday); //將student對象保存到集合中 list.add(stu); } } catch(Exception e) { e.printStackTrace(); } finally { //6.釋放資源 JDBCUtils.close(con,stat,rs); } //將集合對象返回 return list; }
-
條件查詢
/* 條件查詢,根據id查詢學生資訊 */ @Override public Student findById(Integer id) { Student stu = new Student(); Connection con = null; Statement stat = null; ResultSet rs = null; try{ con = JDBCUtils.getConnection(); //3.獲取執行者對象 stat = con.createStatement(); //4.執行sql語句,並且接收返回的結果集 String sql = "SELECT * FROM student WHERE sid='"+id+"'"; rs = stat.executeQuery(sql); //5.處理結果集 while(rs.next()) { Integer sid = rs.getInt("sid"); String name = rs.getString("name"); Integer age = rs.getInt("age"); Date birthday = rs.getDate("birthday"); //封裝Student對象 stu.setSid(sid); stu.setName(name); stu.setAge(age); stu.setBirthday(birthday); } } catch(Exception e) { e.printStackTrace(); } finally { //6.釋放資源 JDBCUtils.close(con,stat,rs); } //將對象返回 return stu; }
-
新增數據
/* 添加學生資訊 */ @Override public int insert(Student stu) { Connection con = null; Statement stat = null; int result = 0; try{ con = JDBCUtils.getConnection(); //3.獲取執行者對象 stat = con.createStatement(); //4.執行sql語句,並且接收返回的結果集 Date d = stu.getBirthday(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String birthday = sdf.format(d); String sql = "INSERT INTO student VALUES ('"+stu.getSid()+"','"+stu.getName()+"','"+stu.getAge()+"','"+birthday+"')"; result = stat.executeUpdate(sql); } catch(Exception e) { e.printStackTrace(); } finally { //6.釋放資源 JDBCUtils.close(con,stat); } //將結果返回 return result; }
-
修改數據
/* 修改學生資訊 */ @Override public int update(Student stu) { Connection con = null; Statement stat = null; int result = 0; try{ con = JDBCUtils.getConnection(); //3.獲取執行者對象 stat = con.createStatement(); //4.執行sql語句,並且接收返回的結果集 Date d = stu.getBirthday(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String birthday = sdf.format(d); String sql = "UPDATE student SET sid='"+stu.getSid()+"',name='"+stu.getName()+"',age='"+stu.getAge()+"',birthday='"+birthday+"' WHERE sid='"+stu.getSid()+"'"; result = stat.executeUpdate(sql); } catch(Exception e) { e.printStackTrace(); } finally { //6.釋放資源 JDBCUtils.close(con,stat); } //將結果返回 return result; }
-
刪除數據
/* 刪除學生資訊 */ @Override public int delete(Integer id) { Connection con = null; Statement stat = null; int result = 0; try{ con = JDBCUtils.getConnection(); //3.獲取執行者對象 stat = con.createStatement(); //4.執行sql語句,並且接收返回的結果集 String sql = "DELETE FROM student WHERE sid='"+id+"'"; result = stat.executeUpdate(sql); } catch(Exception e) { e.printStackTrace(); } finally { //6.釋放資源 JDBCUtils.close(con,stat); } //將結果返回 return result; }
3.student表的CRUD整合頁面
-
接下來我們將程式與介面結合起來
-
用戶表的數據準備
-- 創建用戶表 CREATE TABLE USER( uid VARCHAR(50) PRIMARY KEY, -- 用戶id ucode VARCHAR(50), -- 用戶標識 loginname VARCHAR(100), -- 登錄用戶名 PASSWORD VARCHAR(100), -- 登錄密碼 username VARCHAR(100), -- 用戶名 gender VARCHAR(10), -- 用戶性別 birthday DATE, -- 出生日期 dutydate DATE -- 入職日期 ); -- 添加一條測試數據 INSERT INTO `user` VALUES ('11111111', 'zhangsan001', 'zhangsan', '1234', '張三', '男', '2008-10-28', '2018-10-28');
-
今天資料中有一個準備好的項目:JDBC基礎網頁版
-
將上個案例的StudentDaoImpl複製到這個案例中,需要修改insert方法中的id為null,因為id是自增的
-
或者直接複製如下程式碼:
public class StudentDaoImpl implements StudentDao { /* 查詢所有學生資訊 */ @Override public ArrayList<Student> findAll() { Connection con = null; Statement stat = null; ResultSet rs = null; ArrayList<Student> list = new ArrayList<>(); try { //1.獲取連接 con = JDBCUtils.getConnection(); //2.獲取執行者對象 stat = con.createStatement(); //3.執行sql語句,並接收結果 String sql = "SELECT * FROM student"; rs = stat.executeQuery(sql); //4.處理結果,將每條記錄封裝成一個Student對象。將多個Student對象保存到集合中 while(rs.next()) { Integer sid = rs.getInt("sid"); String name = rs.getString("name"); Integer age = rs.getInt("age"); Date birthday = rs.getDate("birthday"); Student stu = new Student(sid,name,age,birthday); list.add(stu); } } catch (SQLException e) { e.printStackTrace(); } finally { //5.釋放資源 JDBCUtils.close(con,stat,rs); } return list; } /* 條件查詢,根據id查詢學生資訊 */ @Override public Student findById(Integer id) { Connection con = null; Statement stat = null; ResultSet rs = null; Student stu = new Student(); try { //1.獲取連接 con = JDBCUtils.getConnection(); //2.獲取執行者對象 stat = con.createStatement(); //3.執行sql語句,並接收結果 String sql = "SELECT * FROM student WHERE sid='"+id+"'"; rs = stat.executeQuery(sql); //4.處理結果,將記錄封裝成一個Student對象。 if(rs.next()) { Integer sid = rs.getInt("sid"); String name = rs.getString("name"); Integer age = rs.getInt("age"); Date birthday = rs.getDate("birthday"); stu.setSid(sid); stu.setName(name); stu.setAge(age); stu.setBirthday(birthday); } } catch (SQLException e) { e.printStackTrace(); } finally { //5.釋放資源 JDBCUtils.close(con,stat,rs); } return stu; } /* 新增學生資訊 */ @Override public int insert(Student stu) { Connection con = null; Statement stat = null; int result = 0; try{ //1.獲取連接 con = JDBCUtils.getConnection(); //2.獲取執行者對象 stat = con.createStatement(); //3.執行sql語句,並接收結果 Date date = stu.getBirthday(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String birthday = sdf.format(date); String sql = "INSERT INTO student VALUES (null,'"+stu.getName()+"','"+stu.getAge()+"','"+birthday+"')"; result = stat.executeUpdate(sql); } catch (SQLException e) { e.printStackTrace(); } finally { //4.釋放資源 JDBCUtils.close(con,stat); } return result; } /* 修改學生資訊 */ @Override public int update(Student stu) { Connection con = null; Statement stat = null; int result = 0; try{ //1.獲取連接 con = JDBCUtils.getConnection(); //2.獲取執行者對象 stat = con.createStatement(); //3.執行sql語句,並接收結果 Date date = stu.getBirthday(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String birthday = sdf.format(date); String sql = "UPDATE student SET sid='"+stu.getSid()+"',name='"+stu.getName()+"',age='"+stu.getAge()+"',birthday='"+birthday+"' WHERE sid='"+stu.getSid()+"'"; result = stat.executeUpdate(sql); } catch (SQLException e) { e.printStackTrace(); } finally { //4.釋放資源 JDBCUtils.close(con,stat); } return result; } /* 刪除學生資訊 */ @Override public int delete(Integer id) { Connection con = null; Statement stat = null; int result = 0; try{ //1.獲取連接 con = JDBCUtils.getConnection(); //2.獲取執行者對象 stat = con.createStatement(); //3.執行sql語句,並接收結果 String sql = "DELETE FROM student WHERE sid='"+id+"'"; result = stat.executeUpdate(sql); } catch (SQLException e) { e.printStackTrace(); } finally { //4.釋放資源 JDBCUtils.close(con,stat); } return result; } }
-
測試:用戶名zhangsan,密碼1234
-
找到學生管理
-
然後可以添加,修改,刪除
-
那至於,介面和相關Servlet程式碼,咱們先不用管,後邊會有類似的案例
五、SQL注入攻擊
1.sql注入攻擊的演示 ***
-
sql注入攻擊:就是利用sql語句的漏洞來對系統進行攻擊
-
演示:
-
在登錄介面,輸入一個錯誤的用戶名或密碼,也可以登錄成功
-
-
分析:為啥能夠登陸成功?
-
登陸的時候,會調用UserDaoImpl .findByLoginNameAndPassword 方法
-
此方法中的sql語句如下
//2.定義SQL語句 String sql = "SELECT * FROM user WHERE loginname='"+loginName+"' AND password='"+password+"'"; System.out.println(sql); //3.獲取操作對象,執行sql語句,獲取結果集 st = conn.createStatement(); rs = st.executeQuery(sql); //4.獲取結果集 if (rs.next()) { //5.封裝 user = new User(); user.setUid(rs.getString("uid")); .... } //6.返回 return user;
-
輸入aaa和aaa’ or ‘1’=’1 (注意引號)最終sql拼接完變為:
SELECT * FROM user WHERE loginname='aaa' AND password='aaa' or '1'='1'
-
這樣的sql語句,會將user表中所有數據都查詢出來(當然我們目前user中只有zhangsan一條數據)
-
所以最終查詢到用戶了,就登陸進去了
-
2.sql注入攻擊的原理
-
按照正常道理來說,我們在密碼處輸入的所有內容,都應該認為是密碼的組成
-
但是現在Statement對象在執行sql語句時,將一部分內容當做查詢條件來執行了
3.PreparedStatement的介紹
-
預編譯sql語句的執行者對象。在執行sql語句之前,將sql語句進行提前編譯
-
明確sql語句的格式後,就不會改變了。剩餘的內容都會認為是參數
-
參數使用?作為佔位符
-
為參數賦值的方法:setXxx(參數1,參數2);
-
參數1:?的位置編號(編號從1開始)
-
參數2:?的實際參數
-
-
執行sql語句的方法
-
執行insert、update、delete語句:int executeUpdate();
-
執行select語句:ResultSet executeQuery();
-
4.PreparedStatement的使用 ***
/* 使用PreparedStatement的登錄方法,解決注入攻擊 */ @Override public User findByLoginNameAndPassword(String loginName, String password) { //定義必要資訊 Connection conn = null; PreparedStatement pstm = null; ResultSet rs = null; User user = null; try { //1.獲取連接 conn = JDBCUtils.getConnection(); //2.創建操作SQL對象 String sql = "SELECT * FROM user WHERE loginname=? AND password=?"; SELECT * FROM user WHERE loginname='aaa' AND password= "aaa' or '1' = '1" pstm = conn.prepareStatement(sql); //3.設置參數 pstm.setString(1,loginName);//設置第一個?參數 pstm.setString(2,password);//設置第二個?參數 System.out.println(sql); //4.執行sql語句,獲取結果集 rs = pstm.executeQuery(); //5.獲取結果集 if (rs.next()) { //6.封裝 user = new User(); user.setUid(rs.getString("uid")); user.setUcode(rs.getString("ucode")); user.setUsername(rs.getString("username")); user.setPassword(rs.getString("password")); user.setGender(rs.getString("gender")); user.setDutydate(rs.getDate("dutydate")); user.setBirthday(rs.getDate("birthday")); user.setLoginname(rs.getString("loginname")); } //7.返回 return user; }catch (Exception e){ throw new RuntimeException(e); }finally { JDBCUtils.close(conn,pstm,rs); } }
5.使用PreparedStatement優化student表的CRUD(作業)
public class StudentDaoImpl implements StudentDao { @Override public ArrayList<Student> findAll() { //定義必要資訊 Connection conn = null; PreparedStatement pstm = null; ResultSet rs = null; ArrayList<Student> students = null; try { //1.獲取連接 conn = JDBCUtils.getConnection(); //2.獲取操作對象 pstm = conn.prepareStatement("select * from student"); //3.執行sql語句,獲取結果集 rs = pstm.executeQuery(); //4.遍歷結果集 students = new ArrayList<Student>(); while (rs.next()) { //5.封裝 Student student = new Student(); student.setSid(rs.getInt("sid")); student.setName(rs.getString("name")); student.setAge(rs.getInt("age")); student.setBirthday(rs.getDate("birthday")); //加入到集合中 students.add(student); } //6.返回 return students; }catch (Exception e){ throw new RuntimeException(e); }finally { JDBCUtils.close(conn,pstm,rs); } } @Override public Student findById(Integer sid) { //定義必要資訊 Connection conn = null; PreparedStatement pstm = null; ResultSet rs = null; Student student = null; try { //1.獲取連接 conn = JDBCUtils.getConnection(); //2.獲取操作對象 pstm = conn.prepareStatement("select * from student where sid = ? "); pstm.setInt(1,sid); //3.執行sql語句,獲取結果集 rs = pstm.executeQuery(); //4.遍歷結果集 if (rs.next()) { //5.封裝 student = new Student(); student.setSid(rs.getInt("sid")); student.setName(rs.getString("name")); student.setAge(rs.getInt("age")); student.setBirthday(rs.getDate("birthday")); } //6.返回 return student; }catch (Exception e){ throw new RuntimeException(e); }finally { JDBCUtils.close(conn,pstm,rs); } } @Override public int insert(Student student) { //定義必要資訊 Connection conn = null; PreparedStatement pstm = null; int result = 0; try { //1.獲取連接 conn = JDBCUtils.getConnection(); //2.獲取操作對象 pstm = conn.prepareStatement("insert into student(sid,name,age,birthday)values(null,?,?,?)"); //3.設置參數 //pstm.setInt(1,null); pstm.setString(1,student.getName()); pstm.setInt(2,student.getAge()); pstm.setDate(3,new Date(student.getBirthday().getTime())); //4.執行sql語句 result = pstm.executeUpdate(); }catch (Exception e){ throw new RuntimeException(e); }finally { JDBCUtils.close(conn,pstm); } return result; } @Override public int update(Student student) { //定義必要資訊 Connection conn = null; PreparedStatement pstm = null; int result = 0; try { //1.獲取連接 conn = JDBCUtils.getConnection(); //2.獲取操作對象 pstm = conn.prepareStatement("update student set name=?,age=?,birthday=? where sid=? "); //3.設置參數 pstm.setString(1,student.getName()); pstm.setInt(2,student.getAge()); pstm.setDate(3,new Date(student.getBirthday().getTime())); pstm.setInt(4,student.getSid()); //4.執行sql語句 result = pstm.executeUpdate(); }catch (Exception e){ throw new RuntimeException(e); }finally { JDBCUtils.close(conn,pstm); } return result; } @Override public int delete(Integer sid) { //定義必要資訊 Connection conn = null; PreparedStatement pstm = null; int result = 0; try { //1.獲取連接 conn = JDBCUtils.getConnection(); //2.獲取操作對象 pstm = conn.prepareStatement("delete from student where sid=? "); //3.設置參數 pstm.setInt(1,sid); //4.執行sql語句 result = pstm.executeUpdate(); }catch (Exception e){ throw new RuntimeException(e); }finally { JDBCUtils.close(conn,pstm); } return result; } }
六、JDBC管理事務 ***
1. 介紹
-
JDBC如何管理事務
-
管理事務的功能類:Connection
-
開啟事務:setAutoCommit(boolean autoCommit); 參數為false,則開啟手動提交事務。
-
自動提交
-
true:自動提交
-
false:不是自動提交(手動提交,程式設計師可以控制事務提交或回滾)
-
-
提交事務:commit();
-
回滾事務:rollback();
2. 演示
-
演示批量添加數據並在業務層管理事務
-
注意:事務的管理需要在業務層實現,因為dao層的功能要給很多模組提供功能的支撐,而有些模組是不需要事務的。
-
批量添加用戶
-
我們在這裡可以批量添加三個用戶
3. 具體操作
-
UserService 介面
/* 批量添加 : batch:一批,成批處理 */ void batchAdd(List<User> users);
-
UserServiceImpl 實現類:事務需要在service添加
/* 事務要控制在此處 */ @Override public void batchAdd(List<User> users) { //獲取資料庫連接 Connection connection = JDBCUtils.getConnection(); try { //開啟事務 connection.setAutoCommit(false); for (User user : users) { //1.創建ID,並把UUID中的-替換 String uid = UUID.randomUUID().toString().replace("-", "").toUpperCase(); //2.給user的uid賦值 user.setUid(uid); //3.生成員工編號 user.setUcode(uid); //模擬異常 //int n = 1 / 0; //4.保存 userDao.save(connection,user); } //提交事務 connection.commit(); }catch (Exception e){ try { //回滾事務 connection.rollback(); }catch (Exception ex){ ex.printStackTrace(); } e.printStackTrace(); }finally { JDBCUtils.close(connection,null,null); } }
-
UserDao 介面
/** 支援事務的添加 */ void save(Connection connection,User user);
-
UserDaoImpl 實現類
/* 支援事務的添加 */ @Override public void save(Connection connection, User user) { //定義必要資訊 PreparedStatement pstm = null; try { //1.獲取連接 connection = JDBCUtils.getConnection(); //2.獲取操作對象 pstm = connection.prepareStatement("insert into user(uid,ucode,loginname,password,username,gender,birthday,dutydate)values(?,?,?,?,?,?,?,?)"); //3.設置參數 pstm.setString(1,user.getUid()); pstm.setString(2,user.getUcode()); pstm.setString(3,user.getLoginname()); pstm.setString(4,user.getPassword()); pstm.setString(5,user.getUsername()); pstm.setString(6,user.getGender()); pstm.setDate(7,new Date(user.getBirthday().getTime())); pstm.setDate(8,new Date(user.getDutydate().getTime())); //4.執行sql語句,獲取結果集 pstm.executeUpdate(); }catch (Exception e){ throw new RuntimeException(e); }finally { JDBCUtils.close(null,pstm,null); } }