JDBC
SQL注入
- 概述:是指web應用程序對用戶輸入數據的合法性沒有判斷或過濾不嚴,攻擊者可以在web應用程序中事先定義好的查詢語句的結尾上添加額外的SQL語句,在管理員不知情的情況下實現非法操作,以此來實現欺騙數據庫服務器執行非授權的任意查詢,從而進一步得到相應的數據信息,導致數據泄露。
- SQL語句通過
or
被拼接:
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class SQL注入 {
// 模擬登錄業務
public static void login(String username,String password){
Connection conn =null;
Statement st = null;
ResultSet rs = null;
try {
conn = jdbcUtils.getConnection();
st = conn.createStatement();
String sql = "select * from users where `NAME`='"+username+"' AND `password` ='"+password+"'";
rs = st.executeQuery(sql); //查詢完畢會返回一個結果集
while (rs.next()){
System.out.println(rs.getString("NAME"));
System.out.println(rs.getString("password"));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
jdbcUtils.release(conn,st,rs);
}
}
}
這兩個sql語句:
-- 正常sql語句
SELECT * FROM users WHERE `Name` = 'zhangsan' AND `password` = '123456';
-- 將name和password設置爲空或者1=1
SELECT * FROM users WHERE `Name` = '' or '1=1' AND `password` = '' or '1=1';
正常登錄:
public static void main(String[] args) {
login("zhangsan","123456");
}
運行結果:
or
拼接登錄:
public static void main(String[] args) {
login(" 'or '1=1"," 'or'1=1");
}
運行結果:將所有用戶信息查詢出來
PreparedStatement對象
可以防止SQL注入,效率更好!
- 防止SQL注入:
原理:把傳遞進來的參數當做字符,假設其中存在轉義字符,比如說 ’ 會被直接轉義。
import java.sql.*;
public class 防止SQL注入 {
public static void main(String[] args) {
login(" 'or '1=1"," 'or'1=1");
}
// 登錄業務
public static void login(String username,String password){
Connection conn =null;
PreparedStatement st = null;
ResultSet rs = null;
try {
conn = jdbcUtils.getConnection();
String sql = "select * from users where `NAME`=? and `PASSWORD`=?"; // Mybatis
st = conn.prepareStatement(sql);
st.setString(1,username);
st.setString(2,password);
rs = st.executeQuery(); //查詢完畢會返回一個結果集
while (rs.next()){
System.out.println(rs.getString("NAME"));
System.out.println(rs.getString("password"));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
jdbcUtils.release(conn,st,rs);
}
}
}
運行結果:查詢不到結果。
- 使用PreparedStatement對象的插入數據操作:
import java.sql.Connection;
import java.util.Date;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class TestInsert {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
try {
conn = jdbcUtils.getConnection();
// 使用? 佔位符代替參數
String sql = "insert into users(id,`NAME`,`PASSWORD`,`email`,`birthday`) values(?,?,?,?,?)";
st = conn.prepareStatement(sql);
// 手動給參數賦值
st.setInt(1,4); //id
st.setString(2,"qinjiang");
st.setString(3,"1232112");
st.setString(4,"[email protected]");
st.setDate(5,new java.sql.Date(new Date().getTime()));
//執行
int i = st.executeUpdate();
if (i>0){
System.out.println("插入成功!");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
jdbcUtils.release(conn,st,null);
}
}
}
- 和Statement對象的區別:
使用PreparedStatement對象編寫是要經過預編譯SQL,即先寫sql,然後不執行,再手動給參數賦值;
使用Statement對象編寫則是將編寫好的SQL語句直接進行執行操作;故使用PreparedStatement對象可以防止SQL注入,效率更好!
IDEA連接數據庫
打開IDEA:
選擇好MySQL後會出現下面的操作,填寫User和Password後,點擊Test Connection,顯示Successful表示連接成功:
最後點擊Apply再點擊OK,完成連接;
點擊設置標誌,導入需要的數據庫:
查看錶內容:雙擊表名即可查看錶信息,下面爲sql語句;
修改表內容後必須點擊下面的綠色箭頭,完成保存:
在IDEA中編寫SQL代碼:
Java代碼實現事務
事務描述見我的前面的博文:事務
1、關閉自動提交,開啓事務 conn.setAutoCommit(false);
2、一組業務執行完畢,提交事務;
3、可以在catch 語句中顯示的定義 回滾語句,但默認失敗就會回滾。
模擬轉賬用戶A給用戶B轉賬100:我們加入int x = 1/0;
這個錯誤,報錯後,默認回滾
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class Test {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try {
conn = jdbcUtils.getConnection();
conn.setAutoCommit(false);
String sql1 = "update account set money = money-100 where name = 'A'";
st = conn.prepareStatement(sql1);
st.executeUpdate();
int x = 1/0; // 報錯
String sql2 = "update account set money = money+100 where name = 'B'";
st = conn.prepareStatement(sql2);
st.executeUpdate();
//業務完畢,提交事務
conn.commit();
System.out.println("成功!");
} catch (SQLException e) {
// 如果失敗,則默認回滾
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
} finally {
jdbcUtils.release(conn,st,rs);
}
}
}
數據庫連接池
- 概述:數據庫連接池負責分配、管理和釋放數據庫連接,它允許應用程序重複使用一個現有的數據庫連接,而不是再重新建立一個;釋放空閒時間超過最大空閒時間的數據庫連接來避免因爲沒有釋放數據庫連接而引起的數據庫連接遺漏。這項技術能明顯提高對數據庫操作的性能。
- 影響因素:
1)最小連接數
是連接池一直保持的數據庫連接,所以如果應用程序對數據庫連接的使用量不大,將會有大量的數據庫連接資源被浪費。
2)最大連接數
是連接池能申請的最大連接數,如果數據庫連接請求超過此數,後面的數據庫連接請求將被加入到等待隊列中,這會影響之後的數據庫操作。
3)最小連接數與最大連接數差距
最小連接數與最大連接數相差太大,那麼最先的連接請求將會獲利,之後超過最小連接數量的連接請求等價於建立一個新的數據庫連接。不過,這些大於最小連接數的數據庫連接在使用完不會馬上被釋放,它將被放到連接池中等待重複使用或是空閒超時後被釋放。 - 開源數據源實現:
- DBCP
需要的jar包:commons-dbcp-1.4 、 commons-pool-1.6 - C3P0
需要的jar包:c3p0-0.9.5.5、mchange-commons-jiava-0.2.19
需要的jar包:百度網盤鏈接,提取碼:s09s
- DBCP數據庫連接池的測試:
工具類提取:
import org.apache.commons.dbcp.BasicDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class JdbcUtils_DBCP {
private static DataSource dataSource = null;
static {
try{
InputStream in = JdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
Properties properties = new Properties();
properties.load(in);
//創建數據源 工廠模式 --> 創建
dataSource = BasicDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
//獲取連接
public static Connection getConnection() throws SQLException {
return dataSource.getConnection(); //從數據源中獲取連接
}
//釋放連接資源
public static void release(Connection conn, Statement st, ResultSet rs){
if (rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (st!=null){
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
測試代碼:
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Date;
public class TestDBCP {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
try {
conn = JdbcUtils_DBCP.getConnection();
String sql = "insert into users(id,`NAME`,`PASSWORD`,`email`,`birthday`) values(?,?,?,?,?)";
st = conn.prepareStatement(sql);
st.setInt(1,4); //id
st.setString(2,"qinjiang");
st.setString(3,"1232112");
st.setString(4,"[email protected]");
st.setDate(5,new java.sql.Date(new Date().getTime()));
//執行
int i = st.executeUpdate();
if (i>0){
System.out.println("插入成功!");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils_DBCP.release(conn,st,null);
}
}
}
//下篇再見…謝謝