JDBC的概述
什麼是JDBC
什麼是數據庫驅動
驅動:兩個設備(應用)之間通信的橋樑。
爲什麼學習JDBC
沒有JDBC的時候,如果現在要開發一套系統,使用Java連接MySQL數據庫,那麼這時候Java程序員需要了解MySQL驅動API,如果使用Java連接Oracle數據庫,那麼這個時候Java程序員需要了解Oracle數據庫驅動API。
SUN公司提供一套統一的規範(接口)。然後各個數據庫生產商提供這套接口的實現。這套接口規範就是JDBC的規範。
JDBC的入門
JDBC的環境準備
創建數據庫和表
create database web_test;
use web_test;
create table user(
id int primary key auto_increment,
username varchar(20),
password varchar(20),
nickname varchar(20),
age int
);
insert into user values (null,'aaa','123','小麗',34);
insert into user values (null,'bbb','123','大王',32);
insert into user values (null,'ccc','123','小明',28);
insert into user values (null,'ddd','123','大黃',21);
創建項目,引入jar包
JDBC的代碼實現
JDBC的開發步驟
第一步:加載驅動
第二步:獲得連接
第三步:基本操作
第四步:釋放資源
JDBC的代碼實現
import org.junit.Test;
/**
* JDBC的入門程序
* @author jt
*
*/
public class JDBCDemo {
@Test
/**
* JDBC的入門
*/
public void demo1() throws Exception{
// 1.加載驅動
Class.forName("com.mysql.jdbc.Driver");
// 2.獲得連接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/web_test3", "root", "root");
// 3.基本操作:執行SQL
// 3.1獲得執行SQL語句的對象
Statement statement = conn.createStatement();
// 3.2編寫SQL語句:
String sql = "select * from user";
// 3.3執行SQL:
ResultSet rs = statement.executeQuery(sql);
// 3.4遍歷結果集:
while(rs.next()){
System.out.print(rs.getInt("id")+" ");
System.out.print(rs.getString("username")+" ");
System.out.print(rs.getString("password")+" ");
System.out.print(rs.getString("nickname")+" ");
System.out.print(rs.getInt("age"));
System.out.println();
}
// 4.釋放資源
rs.close();
statement.close();
conn.close();
}
}
JDBC的API詳解之DriverManager
DriverManager:驅動管理類
作用一:註冊驅動
這個方法可以完成驅動的註冊,但是實際開發中一般不會使用這個方法完成驅動的註冊!!!
原因:
如果需要註冊驅動,就會使用DriverManager.registerDriver(new Driver());,但是查看源代碼發現,在代碼中有一段靜態代碼塊,靜態代碼塊已經調用了註冊驅動的方法。
如果再手動調用該方法註冊驅動,就會導致驅動被註冊兩次。實際開發中一般會採用:
作用二:獲得連接
這個方法就是用來獲得與數據庫連接的方法:這個方法中有三個參數:
url :與數據庫連接的路徑
user :與數據庫連接的用戶名
password :與數據庫連接的密碼
主要關注的是url的寫法:
jdbc:mysql://localhost:3306/web_test3
jdbc :連接數據庫的協議
mysql :是jdbc的子協議
localhost :連接的MySQL數據庫服務器的主機地址。(連接是本機就可以寫成localhost),如果連接不是本機的,就需要寫上連接主機的IP地址。
3306 :MySQL數據庫服務器的端口號
web_test :數據庫名稱
url如果連接的是本機的路徑,可以簡化爲如下格式:
jdbc:mysql:///web_test3
JDBC的API詳解之Connection
Connection:與數據庫連接對象
作用一:創建執行SQL語句的對象
執行SQL語句對象:
l Statement :執行SQL
l CallableStatement :執行數據庫中存儲過程
PreparedStatement :執行SQL.對SQL進行預處理。解決SQL注入漏洞
作用二:管理事務
JDBC的API詳解之Statement
Statement:執行SQL
作用一:執行SQL
執行SQL的方法
boolean execute(String sql);
執行查詢,修改,添加,刪除的SQL語句。
ResultSet executeQuery(String sql);
執行查詢(執行select語句)。
int executeUpate(String sql);
執行修改,添加,刪除的SQL語句。
作用二:執行批處理
JDBC的API詳解之ResultSet
ResultSet:結果集。
通過select語句的查詢結果。
結果集的遍歷
l 結果集遍歷原理
l 代碼實現
結果集的獲取
結果集獲取可以使用結果集中的:
getXXX();方法通常都會有一個重載的方法。
getXXX(int columnIndex);
getXXX(String columnName);
JDBC的資源釋放
JDBC資源釋放
JDBC程序執行結束後,將與數據庫進行交互的對象釋放掉,通常是ResultSet,Statement,Connection。
這幾個對象中尤其是Connection對象是非常稀有的。這個對象一定要做到儘量晚創建,儘早釋放掉。
將資源釋放的代碼寫入到finally的代碼塊中。
資源釋放的代碼應該寫的標準:
if(rs !=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
rs = null;
}
if(statement !=null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
statement = null;
}
if(conn !=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
JDBC的CRUD操作之保存操作
保存操作代碼實現
@Test
/**
* 保存操作的代碼實現
*/
public void demo(){
Connection conn = null;
Statement stmt = null;
try{
// 註冊驅動:
Class.forName("com.mysql.jdbc.Driver");
// 獲得連接:
conn = DriverManager.getConnection("jdbc:mysql:///web_test", "root", "root");
// 執行操作:
// 創建執行SQL語句對象:
stmt = conn.createStatement();
// 編寫SQL語句:
String sql = "insert into user values (null,'eee','123','阿黃',21)";
// 執行SQL語句:
int num = stmt.executeUpdate(sql);
if(num > 0){
System.out.println("保存用戶成功!!!");
}
}catch(Exception e){
e.printStackTrace();
}finally{
// 資源釋放:
if(stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
}
JDBC的CURD操作之修改操作
修改操作代碼實現
@Test
/**
* 修改操作的代碼實現
*/
public void demo2(){
Connection conn = null;
Statement stmt = null;
try{
// 註冊驅動:
Class.forName("com.mysql.jdbc.Driver");
// 獲得連接
conn = DriverManager.getConnection("jdbc:mysql:///web_test", "root", "abc");
// 執行操作:
// 創建執行SQL語句的對象:
stmt = conn.createStatement();
// 編寫SQL語句:
String sql = "update user set password='abc',nickname='旺財' where id = 5";
// 執行SQL語句:
int num = stmt.executeUpdate(sql);
if(num > 0){
System.out.println("修改用戶成功!!!");
}
}catch(Exception e){
e.printStackTrace();
}finally{
// 資源釋放:
if(stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
}
JDBC的CRUD操作之刪除操作
刪除操作的代碼實現
@Test
/**
* 刪除操作的代碼實現
*/
public void demo3(){
Connection conn = null;
Statement stmt = null;
try{
// 註冊驅動:
Class.forName("com.mysql.jdbc.Driver");
// 獲得連接:
conn = DriverManager.getConnection("jdbc:mysql:///web_test3", "root", "abc");
// 創建執行SQL語句對象:
stmt = conn.createStatement();
// 編寫SQL:
String sql = "delete from user where id = 5";
// 執行SQL:
int num = stmt.executeUpdate(sql);
if(num > 0){
System.out.println("刪除用戶成功!!!");
}
}catch(Exception e){
e.printStackTrace();
}finally{
// 資源釋放:
if(stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
}
JDBC的CRUD操作之查詢操作
查詢多條記錄
@Test
/**
* 查詢多條記錄
*/
public void demo4(){
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try{
// 註冊驅動
Class.forName("com.mysql.jdbc.Driver");
// 獲得連接
conn = DriverManager.getConnection("jdbc:mysql:///web_test", "root", "root");
// 執行操作
// 創建執行SQL語句的對象:
stmt = conn.createStatement();
// 編寫SQL:
String sql = "select * from user";
// 執行SQL:
rs = stmt.executeQuery(sql);
// 遍歷結果集:
while(rs.next()){
System.out.println(rs.getInt("id")+" "+rs.getString("username")+" "+rs.getString("password"));
}
}catch(Exception e){
e.printStackTrace();
}finally{
// 資源釋放:
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
rs = null;
}
if(stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
}
查詢一條記錄
@Test
/**
* 查詢一條記錄
*/
public void demo5(){
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try{
// 註冊驅動
Class.forName("com.mysql.jdbc.Driver");
// 獲得連接
conn = DriverManager.getConnection("jdbc:mysql:///web_test", "root", "root");
// 執行SQL
// 創建執行SQL語句對象:
stmt = conn.createStatement();
// 編寫SQL:
String sql = "select * from user where id = 4";
rs = stmt.executeQuery(sql);
// 判斷就可以:
if(rs.next()){
System.out.println(rs.getInt("id")+" "+rs.getString("username")+" "+rs.getString("password"));
}
}catch(Exception e){
e.printStackTrace();
}finally{
// 資源釋放:
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
rs = null;
}
if(stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
}
JDBC的工具類的抽取
抽取一個JDBC的工具類
因爲傳統JDBC的開發,註冊驅動,獲得連接,釋放資源這些代碼都是重複編寫的。所以可以將重複的代碼提取到一個類中來完成。
/**
* JDBC的工具類
* @author jt
*
*/
public class JDBCUtils {
private static final String driverClassName;
private static final String url;
private static final String username;
private static final String password;
static{
driverClassName="com.mysql.jdbc.Driver";
url="jdbc:mysql:///web_test";
username="root";
password="root";
}
/**
* 註冊驅動的方法
*/
public static void loadDriver(){
try {
Class.forName(driverClassName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 獲得連接的方法
*/
public static Connection getConnection(){
Connection conn = null;
try{
// 將驅動一併註冊:
loadDriver();
// 獲得連接
conn = DriverManager.getConnection(url,username, password);
}catch(Exception e){
e.printStackTrace();
}
return conn;
}
/**
* 釋放資源的方法
*/
public static void release(Statement stmt,Connection conn){
if(stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
public static void release(ResultSet rs,Statement stmt,Connection conn){
// 資源釋放:
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
rs = null;
}
if(stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
}
1.1.2測試工具類
@Test
/**
* 查詢操作:使用工具類
*/
public void demo1(){
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try{
// 獲得連接:
conn = JDBCUtils.getConnection();
// 創建執行SQL語句的對象:
stmt = conn.createStatement();
// 編寫SQL:
String sql = "select * from user";
// 執行查詢:
rs = stmt.executeQuery(sql);
// 遍歷結果集:
while(rs.next()){
System.out.println(rs.getInt("id")+" "+rs.getString("username")+" "+rs.getString("password"));
}
}catch(Exception e){
e.printStackTrace();
}finally{
// 釋放資源:
JDBCUtils.release(rs, stmt, conn);
}
}
JDBC的配置信息提取到配置文件
配置文件
屬性文件
格式:擴展名是.properties
內容:key=value
XML文件
提取信息到配置文件
定義一個配置文件
在工具類中解析屬性文件
獲取到具體內容爲常量賦值
JDBC的SQL注入漏洞
什麼是SQL注入漏洞
在早期互聯網上SQL注入漏洞普遍存在。有一個網站,用戶需要進行註冊,用戶註冊以後根據用戶名和密碼完成登錄。假設現在用戶名已經被其他人知道了,但是其他人不知道你的密碼,也可以登錄到網站上進行相應的操作。
演示SQL注入漏洞
基本登錄功能實現
演示SQL注入漏洞
輸入用戶名
aaa’ or ‘1=1 密碼隨意
aaa’ -- 密碼隨意
JDBC的SQL注入漏洞分析和解決
SQL注入漏洞分析
SQL注入漏洞解決
需要採用PreparedStatement對象解決SQL注入漏洞。這個對象將SQL預先進行編譯,使用?作爲佔位符。?所代表內容是SQL所固定。再次傳入變量(包含SQL的關鍵字)。這個時候也不會識別這些關鍵字。
public class UserDao {
public boolean login(String username,String password){
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
// 定義一個變量:
boolean flag = false;
try{
// 獲得連接:
conn = JDBCUtils.getConnection();
// 編寫SQL語句:
String sql = "select * from user where username = ? and password = ?";
// 預編譯SQL
pstmt = conn.prepareStatement(sql);
// 設置參數:
pstmt.setString(1, username);
pstmt.setString(2, password);
// 執行SQL語句:
rs = pstmt.executeQuery();
if(rs.next()){
// 說明根據用戶名和密碼可以查詢到這條記錄
flag = true;
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(rs, pstmt, conn);
}
return flag;
}
JDBC的CRUD操作之PreparedStatement的保存操作
保存操作代碼實現
@Test
/**
* 保存操作
*/
public void demo1(){
Connection conn = null;
PreparedStatement pstmt = null;
try{
// 獲得連接:
conn = JDBCUtils.getConnection();
// 編寫SQL語句:
String sql = "insert into user values (null,?,?,?,?)";
// 預編譯SQL:
pstmt = conn.prepareStatement(sql);
// 設置參數:
pstmt.setString(1, "eee");
pstmt.setString(2, "abc");
pstmt.setString(3, "旺財");
pstmt.setInt(4, 32);
// 執行SQL
int num = pstmt.executeUpdate();
if(num > 0){
System.out.println("保存成功!");
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(pstmt, conn);
}
}
JDBC的CRUD操作之PreparedStatement的修改操作
修改操作代碼實現
@Test
/**
* 修改操作
*/
public void demo2(){
Connection conn = null;
PreparedStatement pstmt = null;
try{
// 獲得連接:
conn = JDBCUtils.getConnection();
// 編寫SQL語句:
String sql = "update user set username = ?,password =?,nickname=?,age = ? where id = ?";
// 預編譯SQL:
pstmt = conn.prepareStatement(sql);
// 設置參數:
pstmt.setString(1, "abc");
pstmt.setString(2, "1234");
pstmt.setString(3, "旺旺");
pstmt.setInt(4, 23);
pstmt.setInt(5, 6);
// 執行SQL:
int num = pstmt.executeUpdate();
if(num > 0){
System.out.println("修改成功!");
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(pstmt, conn);
}
}
JDBC的CRUD操作之PreparedStatement的刪除操作
刪除操作的代碼實現
@Test
/**
* 刪除操作
*/
public void demo3(){
Connection conn = null;
PreparedStatement pstmt = null;
try{
// 獲得連接:
conn = JDBCUtils.getConnection();
// 編寫SQL語句:
String sql = "delete from user where id = ?";
// 預編譯SQL
pstmt = conn.prepareStatement(sql);
// 設置參數:
pstmt.setInt(1, 4);
// 執行SQL:
int num = pstmt.executeUpdate();
if(num > 0){
System.out.println("刪除成功!");
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(pstmt, conn);
}
}
JDBC的CRUD操作之PreparedStatement的查詢操作
查詢操作代碼實現
@Test
/**
* 查詢操作
*/
public void demo4(){
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try{
// 獲得連接:
conn = JDBCUtils.getConnection();
// 編寫SQL:
String sql = "select * from user";
// 預編譯SQL:
pstmt = conn.prepareStatement(sql);
// 設置參數:
// 執行SQL:
rs = pstmt.executeQuery();
// 遍歷結果集:
while(rs.next()){
System.out.println(rs.getInt("id")+" "+rs.getString("username")+" "+rs.getString("password")+" "+rs.getString("nickname"));
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(rs, pstmt, conn);
}
}
JDBC的批處理操作
什麼是批處理
之前進行JDBC的操作的時候,都是一條SQL語句執行。現在如果使用批處理,可以將一批SQL一起執行。
批處理基本使用
@Test
/**
* 批處理基本操作
*/
public void demo1(){
Connection conn = null;
Statement stmt = null;
try{
// 獲得連接:
conn = JDBCUtils.getConnection();
// 創建執行批處理對象:
stmt = conn.createStatement();
// 編寫一批SQL語句:
String sql1 = "create database test1";
String sql2 = "use test1";
String sql3 = "create table user(id int primary key auto_increment,name varchar(20))";
String sql4 = "insert into user values (null,'aaa')";
String sql5 = "insert into user values (null,'bbb')";
String sql6 = "insert into user values (null,'ccc')";
String sql7 = "update user set name = 'mmm' where id = 2";
String sql8 = "delete from user where id = 1";
// 添加到批處理
stmt.addBatch(sql1);
stmt.addBatch(sql2);
stmt.addBatch(sql3);
stmt.addBatch(sql4);
stmt.addBatch(sql5);
stmt.addBatch(sql6);
stmt.addBatch(sql7);
stmt.addBatch(sql8);
// 執行批處理:
stmt.executeBatch();
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(stmt, conn);
}
}
批量插入(使用PreparedStatement)
@Test
/**
* 批量插入記錄:
* * 默認情況下MySQL批處理沒有開啓的,需要在url後面拼接一個參數即可。
*/
public void demo2(){
// 記錄開始時間:
long begin = System.currentTimeMillis();
Connection conn = null;
PreparedStatement pstmt = null;
try{
// 獲得連接:
conn = JDBCUtils.getConnection();
// 編寫SQL語句:
String sql = "insert into user values (null,?)";
// 預編譯SQL:
pstmt = conn.prepareStatement(sql);
for(int i=1;i<=10000;i++){
pstmt.setString(1, "name"+i);
// 添加到批處理
pstmt.addBatch();
// 注意問題:
// 執行批處理
if(i % 1000 == 0){
// 執行批處理:
pstmt.executeBatch();
// 清空批處理:
pstmt.clearBatch();
}
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(pstmt, conn);
}
long end = System.currentTimeMillis();
System.out.println((end-begin));
}