JDBC(重點)
JDBC(重點)
1、資料庫驅動
驅動:音效卡,顯示卡,資料庫
graph LR 應用程式-->MySQL驅動 應用程式-->Oracle驅動 MySQL驅動-->資料庫 Oracle驅動-->資料庫2、JDBC
SUN 公司為了簡化 開發人員的 (對資料庫的統一)操作,提供了一個(Java操作資料庫)規範,俗稱 JDBC
沒有什麼是加一層解決不了的
graph LR 應用程式-->JDBC JDBC-->MySQL驅動 JDBC-->Oracle驅動 MySQL驅動-->資料庫 Oracle驅動-->資料庫3、第一個 JDBC 程式
maven倉庫
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
建立測試資料庫
# 建立表 create table users( id int primary key , name varchar(40), password varchar(40), email varchar(60), birthday date ); # 插入資料 insert into users values (1,'zhangsan','123546','[email protected]','1980-12-04'), (2,'lisi','123546','[email protected]','1981-12-04'), (3,'wangwu','123546','[email protected]','1979-12-04');
Java程式碼
package com.raykeyzzz.lession01; import java.sql.*; // 我的第一個JDBC程式 public class JdbcFirstDemo { public static void main(String[] args) throws ClassNotFoundException, SQLException { // 1.載入驅動 Class.forName("com.mysql.jdbc.Driver"); // 2.使用者資訊和 url String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8"; String username = "root"; String password = "123456"; // 3.連線成功,資料庫物件 Connection connection = DriverManager.getConnection(url, username, password); // 4.執行 SQL 的物件 Statement statement = connection.createStatement(); // 5.執行 SQL 的物件 去 執行SQL,返回結果 ResultSet resultSet = statement.executeQuery("select * from jdbcstudy.users"); while (resultSet.next()){ System.out.println("id" + resultSet.getObject("id")); System.out.println("name" + resultSet.getObject("name")); System.out.println("password" + resultSet.getObject("password")); } // 6.釋放連線 resultSet.close(); statement.close(); connection.close(); } }
步驟總結:載入驅動 --> 連線資料庫 DiverManager --> 獲得執行sql物件 Statement --> 返回結果集 -->釋放連線
簡化:寫工具類
工具類程式碼
package com.raykeyzzz.lession02;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class jdbcUtils {
private static String driver = null;
private static String url = null;
private static String username = null;
private static String password = null;
// 載入驅動
static {
try {
InputStream resourceAsStream = jdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
Properties properties = new Properties();
properties.load(resourceAsStream);
driver = properties.getProperty("driver");
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
Class.forName(driver);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
// 獲取資料庫連線
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url,username,password);
}
// 釋放資源
public static void release(Connection connection, Statement statement, ResultSet resultSet){
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
操作資料庫程式碼
package com.raykeyzzz.lession02;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestInsert {
public static void main(String[] args) {
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
connection = jdbcUtils.getConnection();
statement = connection.createStatement();
resultSet = statement.executeQuery("select * from jdbcstudy.users");
while (resultSet.next()) {
System.out.println(resultSet.getObject("id"));
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
jdbcUtils.release(connection,statement,resultSet);
}
}
}
SQL注入問題
SQL 存在漏洞,會被攻擊導致資料洩漏,SQL會被拼接 or
package com.raykeyzzz.lession02;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestInsert {
public static void main(String[] args) {
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
connection = jdbcUtils.getConnection();
statement = connection.createStatement();
String sql = query("'' or 1 = 1"); // 將所有使用者資訊都查了出來,資訊洩露
System.out.println(sql);
resultSet = statement.executeQuery(sql);
while (resultSet.next()) {
System.out.println(resultSet.getObject("id"));
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
jdbcUtils.release(connection,statement,resultSet);
}
}
// 查詢 某個使用者資訊
public static String query(String name){
return "select * from jdbcstudy.users " + "where name = " + name;
}
}
4、第二個 JDBC 程式
PreparedStatement 可以防止SQL注入。效果更好!
工具類
package com.raykeyzzz.lession03;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class jdbcUtils {
private static String driver = null;
private static String url = null;
private static String username = null;
private static String password = null;
// 載入驅動
static {
try {
InputStream resourceAsStream = jdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
Properties properties = new Properties();
properties.load(resourceAsStream);
driver = properties.getProperty("driver");
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
Class.forName(driver);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
// 獲取資料庫連線
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url,username,password);
}
// 釋放資源
public static void release(Connection connection, PreparedStatement statement, ResultSet resultSet){
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
操作資料庫程式碼
package com.raykeyzzz.lession03;
import java.sql.*;
public class TestInsert {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
connection = jdbcUtils.getConnection();
String sql = insert();
// 預編譯
statement = connection.prepareStatement(sql);
statement.setInt(1,4);
statement.setString(2,"qinjiang");
statement.setString(3,"123132456");
statement.setString(4,"[email protected]");
statement.setDate(5,new java.sql.Date(new java.util.Date().getTime()));
int i = statement.executeUpdate();
if (i > 0) {
System.out.println("插入成功");
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
jdbcUtils.release(connection,statement,resultSet);
}
}
// 查詢 某個使用者資訊
public static String insert(){
return "insert into users(id,name,password,email,birthday) values(?,?,?,?,?)";
}
}
PreparedStatement 防注入原理
使用 ?
作為佔位符,一個 ?
代表一個引數,將一些非法字元轉義 再 拼接
如 " or 1 = 1"
使用 Statement
select * from jdbcstudy.users where name = '' or 1 = 1
使用 PreparedStatement
程式碼
package com.raykeyzzz.lession03;
import java.sql.*;
public class TestInsert {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet1 = null;
try {
connection = jdbcUtils.getConnection();
String sql = query();
// 預編譯
statement = connection.prepareStatement(sql);
statement.setString(1,"'' or 1 = 1");
resultSet1 = statement.executeQuery();
System.out.println(statement);
while (resultSet1.next()) {
System.out.println(resultSet1.getObject("name"));
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
jdbcUtils.release(connection,statement,resultSet1);
}
}
public static String query() {
return "select * from jdbcstudy.users where name = ?";
}
}
結果
com.mysql.jdbc.JDBC4PreparedStatement@5010be6: select * from jdbcstudy.users where name = '\'\' or 1 = 1'
5、事務
ACID
原子性(Atomic)
一致性(Consistency)
隔離性(Isolation)
永續性(Durability)
目的:一致性(提交前後按照預想的結果走,要不都成功,要麼都失敗)
實現保證:原子性、永續性、隔離性
原子性:事務是最小元素不可分割,技術支援:事務回滾
隔離性:事務之間相互不干擾,技術支援:鎖和MVCC機制
永續性:保證儲存到磁碟上,防止斷電即失。技術支援:
redo log
日誌記錄事務操作記錄, 當事務提交的時候,會將redo log
日誌進行刷盤(redo log
一部分在記憶體中,一部分在磁碟上) 。當資料庫宕機重啟的時候,會將redo log
中的內容恢復到資料庫中,再根據undo log
和binlog
內容決定回滾資料還是提交資料。
6、資料庫連線池
graph LR 資料庫連線 --> 執行完畢 執行完畢 --> 釋放連線--釋放 十分耗費資源
池化技術:不用頻繁的 建立--釋放 資料庫
最小連線數
最大連線數
等待超時
編寫連線池
實現一個介面 DataSource
開源資料來源實現
本質:實現 DateSource 介面
該工廠用於提供到此
DataSource
物件所表示的物理資料來源的連線。作為DriverManager
工具的替代項,DataSource
物件是獲取連線的首選方法。實現DataSource
介面的物件通常在基於 JavaTM Naming and Directory Interface (JNDI) API 的命名服務中註冊。
DataSource
介面由驅動程式供應商實現。共有三種類型的實現:
- 基本實現 - 生成標準的
Connection
物件- 連線池實現 - 生成自動參與連線池的
Connection
物件。此實現與中間層連線池管理器一起使用。- 分散式事務實現 - 生成一個
Connection
物件,該物件可用於分散式事務,大多數情況下總是參與連線池。此實現與中間層事務管理器一起使用,大多數情況下總是與連線池管理器一起使用。
-
DBCP
-
C3P0
-
Druid:阿里巴巴
DBCP 實現程式碼:
maven倉庫
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
配置檔案
########DBCP配置檔案##########
#驅動名
driverClassName=com.mysql.jdbc.Driver
#url
url=jdbc:mysql://localhost:3306/jdbcstudy
#使用者名稱
username=root
#密碼
password=123456
#初試連線數
initialSize=30
#最大活躍數
maxTotal=30
#最大idle數
maxIdle=10
#最小idle數
minIdle=5
#最長等待時間(毫秒)
maxWaitMillis=1000
#程式中的連線不使用後是否被連線池回收(該版本要使用removeAbandonedOnMaintenance和removeAbandonedOnBorrow)
#removeAbandoned=true
removeAbandonedOnMaintenance=true
removeAbandonedOnBorrow=true
#連線在所指定的秒數內未使用才會被刪除(秒)(為配合測試程式才配置為1秒)
removeAbandonedTimeout=1
工具類
package com.raykeyzzz.lession04;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class JdbcUtils {
private static String driver = null;
private static String url = null;
private static String username = null;
private static String password = null;
// 載入驅動
static {
try {
InputStream resourceAsStream = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
Properties properties = new Properties();
properties.load(resourceAsStream);
driver = properties.getProperty("driver");
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
Class.forName(driver);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
// 獲取資料庫連線
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url,username,password);
}
// 釋放資源
public static void release(Connection connection, PreparedStatement statement, ResultSet resultSet){
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
操作資料庫程式碼
package com.raykeyzzz.lession04;
import com.raykeyzzz.lession02.jdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestInsert {
public static void main(String[] args) {
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
connection = JdbcUtils.getConnection();
statement = connection.createStatement();
String sql = query("'' or 1 = 1"); // 將所有使用者資訊都查了出來,資訊洩露
System.out.println(sql);
resultSet = statement.executeQuery(sql);
while (resultSet.next()) {
System.out.println(resultSet.getObject("id"));
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
jdbcUtils.release(connection,statement,resultSet);
}
}
// 查詢 某個使用者資訊
public static String query(String name){
return "select * from jdbcstudy.users " + "where name = " + name;
}
}