資料庫:JDBC開發
目錄
6.登入驗證(SQL注入問題)
7.PreparedStatement介面(解決SQL注入問題)
1.什麼是JDBC?
java database connectivity(java連線資料庫技術),是sun公司為了簡化開發,設計的一套資料庫連線管理規範,主要由一些介面組成。
2.JDBC執行原理
JDBC執行原理如下圖所示:
3.JDBC開發步驟
步驟
- 註冊驅動(通過DriverManager管理驅動)
- 建立連線(Connection)
- 傳送sql命令
- 獲取響應處理結果
- 釋放資源
程式碼
import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import com.mysql.jdbc.Driver; public class Test { public static void main(String[] args) { Connection conn = null; Statement stmt = null; ResultSet rs = null; try { //1.註冊驅動 DriverManager.registerDriver(new Driver()); //2.建立連線 conn = DriverManager.getConnection( //?useSSL=true 加密傳輸,保證資料安全 "jdbc:mysql://localhost:3306/mysql?useSSL=true", "root", "root"); System.out.println(conn); //3.傳送sql命令 String sql = "select * from student"; stmt = conn.createStatement(); rs = stmt.executeQuery(sql); //4.獲取響應並處理 while(rs.next()){//獲取記錄 //逐列獲取資料 int sid = rs.getInt("sid"); String sname = rs.getString("sname"); int cid = rs.getInt("cid"); System.out.printf("sid:%s,sname:%s,cid:%s\n",sid,sname,cid); } } catch (SQLException e) { e.printStackTrace(); } finally { //5.釋放資源 if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (rs != null) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if (rs != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } } }
4.常見API詳解
常見類及介面:
- DriverManager:類,驅動管理器
- Connection:介面,代表與資料庫服務的連線
- Statement:介面,傳送sql指令
- PreparedStatement:Statement介面的介面,傳送sql指令(主要用這個)
- ResultSet:介面,結果集,底層封裝指向結果的遊標
- ResultSetMetaData:介面,封裝了結果集的元資料資訊
DriverManager:
作用:註冊和管理驅動程式;獲取連線物件。
示例:
//註冊驅動的兩種方式: //1.通過DriverManager進行註冊 DriverManager.registerDriver(new Driver()); //2.載入類的時候靜態改程式碼塊裡面呼叫下面的程式碼進行驅動註冊 Class.forName("com.mysql.jdbc.Driver");//(建議使用) //通過DriverManager獲取資料庫連線 conn = DriverManager.getConnection(url,user,password);
Connection:
作用:作為資料傳輸通道;用於建立傳送sql命令的物件Statement或PreparedStatement。
常用方法:
//設定自動提交
conn.setAutoCommit();
conn.commit();
conn.rollback();
//建立傳送sql命令的物件
conn.createStatement();
conn.prepareStatement(sql);//PreparedStatement介面是Statement 介面的子介面
conn.prepareCall(sql);//CallableStatement介面是Statement 介面的子介面 用於呼叫儲存過程
//元資料:解釋資料的資料
DatabaseMetaData md = conn.getMetaData();
String dpn = md.getDatabaseProductName();
String driverName = md.getDriverName();
String url = md.getURL();
String username = md.getUserName();
Statement:
作用:傳送sql指令
子介面:PreparedStatement(用於替換父介面) 和 CallableStatement(呼叫儲存過程)
常用方法:
- execute:傳送任意sql命令,返回值boolean
- executeQuery:傳送查詢sql命令,返回值是ResultSet物件
- executeUpdate:傳送增刪改和ddl語句,返回值是影響的行數
- executeBatch:傳送批處理命令,返回值int[ ]
ResultSet:
作用:底層封裝指向結果集的遊標,可以獲取結果集中的資料
常用方法:
- next():用於移動遊標
- getX():獲取自段值
- getMetaData():獲取結果集的元資料資訊
- ResultSetMetaData rsmd = rs.getMetaData();//獲取結果集的元資料資訊
- int columnCount = rsmd.getColumnCount();//獲取列數
- String columnName = rsmd.getColumnName(1);//獲取列名
5.實現增刪改查(見下面的DAO開發)
6.登入驗證
登入驗證中的問題?使用Statement物件傳送sql指令時,利用檢驗不合格、不充分繞過驗證,從而完成SQL注入。
何為SQL注入?
//定義sql字串
String sql = "select * from user where uname='"+uname+"' and password='"+password+"'";
//若uname和password的值如下:
String username="' or 1=1 or '";
String password="";
//將uname和password傳入上面的sql字串,將得到如下SQL語句
sql = "select * from user where uname='' or 1=1 or '' and password=''";
//這條sql語句的過濾條件是恆成立的,所以,一定會有查詢結果,從而無法達到驗證登入的效果
SQL注入產生的原因?利用sql的拼接。
如何解決SQL注入問題?使用PreparedStatement介面傳送sql指令,即可解決,具體見下文。
7.PreparedStatement介面
什麼是PreparedStatement?
- Statement的子介面
- 採用佔位符以及賦值方式解決sql注入問題
- 採用預編譯方式,效率高於Statement,常用PreparedStatement
示例程式碼:
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
String sql = "select * from userinfo where username=? and password=?";
//預編譯(語法、語義等檢查)
ps = conn.prepareStatement(sql);
//賦值
ps.setString(1, username);
ps.setString(2, password);
rs = ps.executeQuery();
if (rs.next()) {
System.out.println("登入成功");
} else {
System.out.println("登入失敗");
}
8.DAO開發
什麼是DAO?data access object,即資料訪問物件,被定義成介面,專門用於對資料庫表的進行增刪改查操作。
什麼是DAO開發?sun公司提出三層開發架構,其中資料持久層是實現與資料庫的互動,因此資料持久層開發稱為DAO開發。
如何進行DAO開發?
dao開發:由介面+實現類組成,每個介面只做一件事,對某一張表進行增刪改查操作(鬆耦合,儘可能避免依賴某一個具體類)
步驟:
- 建立entity包,並在該包下為資料庫中的每一張表建立一個類,資料庫表中的每一條記錄就對應該類的一個物件稱為物件-關係對映;
- 建立dao包,並在包下針對資料庫的每一張表定義Dao介面以及實現BaseDao類(通用查和通用改的類);
- 建立dao.impl包,並在包下建立Dao介面的實現類並繼承BaseDao類,然後實現該類;
- 測試Dao的增刪改查是否正確。
- 在開發過程中,會建立獲取連線資源和關閉資源的工具類JdbcUtils,並放在util包下。
包、介面與類:
完整程式碼:
package entity;
public class User {
private int uid;
private String uname;
private String password;
public User(){}
public int getUid() {
return uid;
}
public User setUid(int uid) {
this.uid = uid;
return this;
}
public String getUname() {
return uname;
}
public User setUname(String uname) {
this.uname = uname;
return this;
}
public String getPassword() {
return password;
}
public User setPassword(String password) {
this.password = password;
return this;
}
@Override
public String toString() {
return "User [uid=" + uid + ", uname=" + uname + ", password=" + password + "]";
}
}
package dao;
import java.util.List;
import entity.User;
public interface UserDao {
//增刪改查
int insert(User user);
int delete(User user);
int update(User user);
List<User> select(User user);
List<User> queryAll();
}
package dao;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.*;
import util.JdbcUtils;
public class BaseDao {
private Connection conn;
private PreparedStatement ps;
private ResultSet rs;
//通用增刪改方法
public int excuteUpdate(String sql, Object[] objects){
int res = 0;
try {
//獲取資料庫連線
conn = JdbcUtils.getConnection();
//獲取執行sql的物件
ps = conn.prepareStatement(sql);
for(int i=0; i<objects.length; i++){
ps.setObject(i+1, objects[i]);
}
res = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.closeRes(rs, ps, conn);
}
return res;
}
//通用查詢方法
public <T> List<T> excuteQuery(String sql, Object[] objects, Class<T> clz){
List<T> res = new ArrayList<>();
try {
//獲取資料庫連線
conn = JdbcUtils.getConnection();
//獲取執行sql的物件
ps = conn.prepareStatement(sql);
for(int i=0; i<objects.length; i++){
ps.setObject(i+1, objects[i]);
}
//執行sql,獲取結果集
rs = ps.executeQuery();
ResultSetMetaData rmd = rs.getMetaData();
int columnCount = rmd.getColumnCount();
while(rs.next()){
T t = clz.newInstance();
for(int i=0; i<columnCount; i++){
Object value = rs.getObject(i+1);
String columnName = rmd.getColumnName(i+1);
Field field = clz.getDeclaredField(columnName);
field.setAccessible(true);
field.set(t, value);
}
res.add(t);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtils.closeRes(rs, ps, conn);
}
return res;
}
}
package dao.impl;
import java.util.List;
import dao.*;
import entity.User;
public class UserDaoImpl extends BaseDao implements UserDao {
@Override
public int insert(User user) {
String sql = "insert into user(uname,password) values(?,?)";
Object[] objects = {user.getUname(),user.getPassword()};
return excuteUpdate(sql, objects);
}
@Override
public int delete(User user) {
String sql = "delete from user where uid=?";
Object[] objects = {user.getUid()};
return excuteUpdate(sql, objects);
}
@Override
public int update(User user) {
String sql = "update user set uname=?,password=? where uid=?";
Object[] objects = {user.getUname(),user.getPassword(),user.getUid()};
return excuteUpdate(sql, objects);
}
@Override
public List<User> select(User user) {
String sql = "select * from user where uname=? and password=?";
Object[] objects = {user.getUname(),user.getPassword()};
return excuteQuery(sql, objects, User.class);
}
@Override
public List<User> queryAll() {
String sql = "select * from user";
Object[] objects = {};
return excuteQuery(sql, objects, User.class);
}
}
package util;
import java.io.*;
import java.sql.*;
import java.util.Properties;
public class JdbcUtils {
private static String driver;
private static String url;
private static String user;
private static String password;
static{
try {
InputStream is =
JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
System.out.println(is);
Properties properties = new Properties();
properties.load(is);
is.close();
//1.註冊驅動
driver = properties.getProperty("driver");
System.out.println(driver);
Class.forName(driver);
url = properties.getProperty("url");
System.out.println(url);
user = properties.getProperty("user");
System.out.println(user);
password = properties.getProperty("password");
System.out.println(password);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
//獲取Connection連線
public static Connection getConnection() throws SQLException{
//2.獲取資料庫連線
return DriverManager.getConnection(url, user, password);
}
//關閉資源
public static void closeRes(ResultSet rs, PreparedStatement ps, Connection conn){
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
jdbc.properties檔案的內容,該檔案要放置在src下面才能被正常訪問。
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/bd1808?useSSL=true
user=root
password=root