JDBC的全稱爲:java database connectivity (java和 數據庫的連接 )
JDBC六步
- 註冊驅動
- 建立數據庫的連接
- 通過con連接獲取到Statement實例
- statement執行sql語句,查詢到的結果集到ResultSet實例
- 從結果集中獲取數據
- 釋放資源
package JDBC;
import com.mysql.jdbc.Driver;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
public class JDBCTest {
public static void main(String[] args) throws SQLException, ClassNotFoundException {
Connection con = null;
Statement statement = null;
try {
//獲取連接
con = getConnection();
//創建statement
statement = con.createStatement();
// 執行添加語句
String sql = "insert into user values('116','zhaoliu',28)";
statement.executeUpdate(sql);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
//釋放資源
release(con, statement, null);
}
}
private static String driverClass = "";
private static String url = "";
private static String username = "";
private static String password = "";
//解析properties文件只需要執行一次,所以放在靜態代碼塊中
static{
//首先創建Properties對象
Properties properties = new Properties();
//加載文件
try {
properties.load(new FileInputStream("src/jdbc.properties"));
//獲取參數
driverClass = properties.getProperty("driverClass");
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
Class.forName(driverClass);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static Connection getConnection(){
Connection con = null;
try {
//2.獲取連接
con = DriverManager.getConnection(url, username, password);
} catch (SQLException e) {
e.printStackTrace();
}
return con;
}
//釋放資源
public static void release(Connection con,Statement st,ResultSet rs){
//6.釋放資源:先開後關,後開先關,最好放在finally代碼塊中
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(st!=null){
try {
st.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(con!=null){
try {
con.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
driverClass=com.mysql.jdbc.Driver
url = jdbc:mysql://localhost:3306/mybd
username = root
password = root
2.sql注入問題
sql注入:由於沒有對用戶輸入進行充分檢查,而SQL又是拼接而成,在用戶輸入參數時,在參數中添加一些SQL 關鍵字,達到改變SQL運行結果的目的,也可以完成惡意攻擊。
案例:模擬登陸
執行如下sql語句:創建一個day06數據庫,創建一張user表,表中插入一條數據。
create table user(
id varchar(10) primary key,
username varchar(10),
password varchar(10)
);
insert into user values('1','admin','123456');
package JDBC;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class Login {
public static void main(String[] args) {
String username = "admin' -- ";
String password = "123";
Connection con = null;
Statement statement = null;
ResultSet resultSet = null;
try {
//首先獲取連接
con = JDBCUtil.getConnection();
//創建statement對象
statement = con.createStatement();
//發送sql語句,查詢數據庫是否存在這個用戶
String sql = "select count(*) from user where username='" + username + "' and password = '" + password + "'";
resultSet = statement.executeQuery(sql);
int count = 0;
while (resultSet.next()) {
count = resultSet.getInt(1);
}
//如果不存在
if (count == 0) {
System.out.println("用戶登錄失敗");
} else {
//存在
System.out.println("用戶登錄成功");
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
JDBCUtil.release(con, statement, resultSet);
}
}
}
執行代碼,可以發現用戶名爲 admin’ – ,密碼爲 1234 這樣的用戶是不存在的,但是盡然可以登錄成功。那麼這是什麼原因造成的呢?
將字符串拼接後的sql語句放在可視化工具中查看發現如下情況:
可以發現,後面密碼的驗證被註釋掉了。這樣豈不是誰都可以登錄了?
問題根本原因:
語句和用戶輸入的內容進行拼接,發送給數據庫編譯的時候,數據庫將用戶輸入的內容當成sql語句編譯了。
從而從根本上改變了我們開發者所期望sql語句原有的含義。導致程序受到sql攻擊。那麼原因知道了,我們怎麼解決的?
問題的解決方案:
我們需要首先將sql語句所代表的含義確定下來,然後再向編譯好的sql語句中填充用戶輸入的內容。這樣用戶輸入的內容就不會再被編譯了。所以需要我們學習preparedStatement向數據庫發送預編譯的sql語句。
2.1 PreparedStatement解決sql注入 重要步驟:
PreparedStatement是Statement的子接口,它的實例對象可以通過調用Connection.preparedStatement(sql)方法獲得
注意:sql提前創建好的。sql語句中需要參數。使用?進行佔位。
- conn.prepareStatement(sql);
需要你事先傳遞sql。如果sql需要參數,使用?進行佔位。 - 設置參數(執行sql之前):prepStmt.setXXX(int index, 要放入的值)
根據不同類型的數據進行方法的選擇。第一個參數表示的是?出現的位置。從1開始計數,有幾個問號,就需要傳遞接個參數。方法的參數說明:
第一個參數:int index ;表示的是問號出現的位置。 問號是從1開始計數
第二個參數:要問號的位置傳入的值。 - 執行,不需要在傳遞sql了。
prepStmt.executeQuery();—執行select
prepStmt.executeUpdate();—執行insert,delete,update
package JDBC;
import java.sql.*;
public class Login {
public static void main(String[] args) {
String username = "admin";
String password = "123456";
Connection con = null;
PreparedStatement prepareStatement = null;
ResultSet resultSet = null;
try {
//首先獲取連接
con = JDBCUtil.getConnection();
String sql = "select COUNT(*) from user where username=? and password = ?";
//創建preparedStatement對象,對sql進行編譯
prepareStatement = con.prepareStatement(sql);
// 設置參數
prepareStatement.setString(1, username);
prepareStatement.setString(2, password);
resultSet = prepareStatement.executeQuery();
int count = 0;
while (resultSet.next()) {
count = resultSet.getInt(1);
}
//如果不存在
if (count == 0) {
System.out.println("用戶登錄失敗");
} else {
//存在
System.out.println("用戶登錄成功");
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
JDBCUtil.release(con, prepareStatement, resultSet);
}
}
}