JDBC
文章目錄
一、DBUtils概述
1.1 數據的持久化
- 持久化(persistence):把數據保存到可掉電式存儲設備中以供之後使用。大多數情況下,特別是企業級應用,數據持久化意味着將內存中的數據保存到硬盤上加以”固化”,而持久化的實現過程大多通過各種關係數據庫來完成。
- 持久化的主要應用是將內存中的數據存儲在關係型數據庫中,當然也可以存儲在磁盤文件、XML數據文件中。
1.2數據持久化的好處
-
程序代碼重用性強,即使更換數據庫,只需要更改配置文件,不必重寫程序代碼。
-
業務邏輯代碼可讀性強,在代碼中不會有大量的SQL語言,提高程序的可讀性。
-
持久化技術可以自動優化,以減少對數據庫的訪問量,提高程序運行效率。
數據持久化對象的基本操作有:保存、更新、刪除、查詢等。
二、獲取數據庫連接
2.1 要素一:Driver接口實現類
2.1.1 Driver接口介紹
-
java.sql.Driver 接口是所有 DBUtils 驅動程序需要實現的接口。這個接口是提供給數據庫廠商使用的,不同數據庫廠商提供不同的實現。
-
在程序中不需要直接去訪問實現了 Driver 接口的類,而是由驅動程序管理器類(java.sql.DriverManager)去調用這些Driver實現。
- Oracle的驅動:oracle.jdbc.driver.OracleDriver - mySql 的驅動:com.mysql.jdbc.Driver
-
將上述jar包拷貝到Java工程的一個目錄中,習慣上新建一個lib文件夾,不同的idea有不同的操作。
2.1.2 加載與註冊DBUtils驅動
-
加載驅動:加載 DBUtils 驅動需調用 Class 類的靜態方法 forName(),向其傳遞要加載的 DBUtils 驅動的類名
- Class.forName(“com.mysql.jdbc.Driver”);
-
註冊驅動:DriverManager 類是驅動程序管理器類,負責管理驅動程序
-
使用DriverManager.registerDriver(com.mysql.jdbc.Driver)來註冊驅動
-
通常不用顯式調用 DriverManager 類的 registerDriver() 方法來註冊驅動程序類的實例,因爲 Driver 接口的驅動程序類都包含了靜態代碼塊,在這個靜態代碼塊中,會調用 DriverManager.registerDriver() 方法來註冊自身的一個實例。
-
2.2 要素二:URL
-
幾種常用數據庫的 DBUtils URL
MySQL的連接URL編寫方式:
- jdbc:mysql://主機名稱:mysql服務端口號/數據庫名稱?參數=值&參數=值
- jdbc:mysql://localhost:3306/shop_jdbc
- jdbc:mysql://localhost:3306/shop_jdbc?useUnicode=true&characterEncoding=utf8(如果程序與服務器端的字符集不一致,會導致亂碼,那麼可以通過參數指定服務器端的字符集)
- jdbc:mysql://localhost:3306/atguigu?user=root&password=root
2.3 要素三:用戶名和密碼
- user,password可以用“屬性名=屬性值”方式告訴數據庫
- 可以調用 DriverManager 類的 getConnection() 方法建立到數據庫的連接
2.4 數據庫連接方式舉例
//方式一
@Test
public void testConnection1() throws Exception{
//1.數據庫連接的4個基本要素:
String url = "jdbc:mysql://localhost:3306/shop_jdbc";
String user = "root";
String password = "root";
//8.0之後名字改了 com.mysql.cj.jdbc.Driver
String driverName = "com.mysql.jdbc.Driver";
//2.實例化Driver
Class clazz = Class.forName(driverName);
Driver driver = (Driver) clazz.newInstance();
//3.註冊驅動
DriverManager.registerDriver(driver);
//4.獲取連接
Connection conn = DriverManager.getConnection(url, user, password);
System.out.println(conn);
}
//方式二
//一般常用這種方式
@Test
public void testConnection2() throws Exception{
//1.數據庫連接的4個基本要素:
String url = "jdbc:mysql://localhost:3306/xinzhishop";
String user = "root";
String password = "root";
String driverName = "com.mysql.jdbc.Driver";
//2.加載驅動
Class.forName(driverName);
//3.獲取連接
Connection conn = DriverManager.getConnection(url, user, password);
System.out.println(conn);
}
//方式三
@Test
public void testConnection3() throws Exception{
//1.數據庫連接的4個基本要素:
String url = "jdbc:mysql://localhost:3306/xinzhishop";
String user = "root";
String password = "root";
String driverName = "com.mysql.jdbc.Driver";
//3.獲取連接
Connection conn = DriverManager.getConnection(url, user, password);
System.out.println(conn);
}
//方式四
/**
* 使用配置文件的方式 連接數據庫
* 當需要更改數據庫時 可以直接在文件中更改 方便 快捷
* @throws Exception
*/
@Test
public void testConnection4() throws Exception{
//1.數據庫連接的4個基本要素:
//jdbc.confile 是文件名稱
//文件內容
//> url=jdbc:mysql://localhost:3306/shop_jdbc
//> user=root
//> password=root
//> driverName=com.mysql.jdbc.Driver
InputStream in = TestUser.class.getClassLoader().getResourceAsStream("jdbc.config");
Properties properties = new Properties();
properties.load(in);
String url = properties.getProperty("url");
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String driverName = properties.getProperty("driverName");
//2.加載驅動
Class.forName(driverName);
//3.獲取連接
Connection conn = DriverManager.getConnection(url, user, password);
System.out.println(conn);
}
}
說明:使用配置文件的方式保存配置信息,在代碼中加載配置文件
使用配置文件的好處:
①實現了代碼和數據的分離,如果需要修改配置信息,直接在配置文件中修改,不需要深入代碼
②如果修改了配置信息,省去重新編譯的過程。
三、Statement 和 PreparedStatement
3.1 操作和訪問數據庫
-
數據庫連接被用於向數據庫服務器發送命令和 SQL 語句,並接受數據庫服務器返回的結果。其實一個數據庫連接就是一個Socket連接。
-
在 java.sql 包中有 3 個接口分別定義了對數據庫的調用的不同方式:
- Statement:用於執行靜態 SQL 語句並返回它所生成結果的對象。
- PrepatedStatement:SQL 語句被預編譯並存儲在此對象中,可以使用此對象多次高效地執行該語句。
3.2 使用Statement操作數據表的弊端
-
通過調用 Connection 對象的 createStatement() 方法創建該對象。該對象用於執行靜態的 SQL 語句,並且返回執行結果。
-
Statement 接口中定義了下列方法用於執行 SQL 語句:
int excuteUpdate(String sql):執行更新操作INSERT、UPDATE、DELETE ResultSet executeQuery(String sql):執行查詢操作SELECT
-
但是使用Statement操作數據表存在弊端:
- 問題一:存在拼串操作,繁瑣
- 問題二:存在SQL注入問題
-
SQL 注入是利用某些系統沒有對用戶輸入的數據進行充分的檢查,而在用戶輸入數據中注入非法的 SQL 語句段或命令(如:
String sql = "select id,username,password from user where username = 'zxcaf1' or 1 = 1";
) ,從而利用系統的 SQL 引擎完成惡意行爲的做法。 -
對於 Java 而言,要防範 SQL 注入,只要用
PreparedStatement
(從Statement擴展而來) 取代 Statement
3.2.1 增刪改 只需修改對應的 sql 語句就可
@Test
public void testConnection2() throws Exception{
//1.數據庫連接的4個基本要素:
String url = "jdbc:mysql://localhost:3306/shop_jdbc";
String user = "root";
String password = "root";
//8.0之後名字改了 com.mysql.cj.jdbc.Driver
String driverName = "com.mysql.jdbc.Driver";
//2、加載驅動(實例化Driver 註冊驅動)
Class.forName(driverName);
//3、獲取連接
Connection conn = DriverManager.getConnection(url,user,password);
//sql語句
Statement statement = conn.createStatement();
statement.execute("insert into user value (1,'ycw','123')");
System.out.println(conn);
}
3.2.2 查詢
@Test
public void testConnection2() throws Exception{
//1.數據庫連接的4個基本要素:
String url = "jdbc:mysql://localhost:3306/shop_jdbc";
String user = "root";
String password = "root";
//8.0之後名字改了 com.mysql.cj.jdbc.Driver
String driverName = "com.mysql.jdbc.Driver";
String sql = "select id,username,password from user";
//2、加載驅動(實例化Driver 註冊驅動)
Class.forName(driverName);
//3、獲取連接
Connection conn = DriverManager.getConnection(url,user,password);
//sql語句
Statement statement = conn.createStatement();
ResultSet resultSet = statement.executeQuery(sql);
resultSet.next();
int id = resultSet.getInt("id");
String username = resultSet.getString("username");
String pwd = resultSet.getString("password");
System.out.println(id+username+pwd);
resultSet.next();
id = resultSet.getInt("id");
username = resultSet.getString("username");
pwd = resultSet.getString("password");
System.out.println(id+username+pwd);
}
3.2.3 代碼優化
資源的釋放
- 釋放ResultSet, Statement,Connection。
- 數據庫連接(Connection)是非常稀有的資源,用完後必須馬上釋放,如果Connection不能及時正確的關閉將導致系統宕機。Connection的使用原則是儘量晚創建,儘量早的釋放。
- 可以在finally中關閉,保證及時其他代碼出現異常,資源也一定能被關閉。
1、手動處理異常
2、合理關係資源
3、遍歷處理數據
@Test
public void testConnection2(){
//1.數據庫連接的4個基本要素:
String url = "jdbc:mysql://localhost:3306/shop_jdbc";
String user = "root";
String password = "root";
//8.0之後名字改了 com.mysql.cj.jdbc.Driver
String driverName = "com.mysql.jdbc.Driver";
String sql = "select id,username,password from user";
//2、加載驅動(實例化Driver 註冊驅動)
Connection conn = null;
Statement statement = null;
ResultSet resultSet = null;
try {
Class.forName(driverName);
//3、獲取連接
conn = DriverManager.getConnection(url,user,password);
//sql語句
statement = conn.createStatement();
resultSet = statement.executeQuery(sql);
//使用while遍歷讀取
while (resultSet.next()){
int id = resultSet.getInt("id");
String username = resultSet.getString("username");
String pwd = resultSet.getString("password");
System.out.println(id+username+pwd);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
//避免浪費資源 用完即關閉資源
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement != null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (resultSet != null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
3.2.4 公共提取
1、不管哪裏要操作數據庫都要獲取連接,所以能不能提取
2、不管哪裏都要關閉資源能不能提取
public class DBUtil {
public static Connection getConnection(){
String url = "jdbc:mysql://localhost:3306/test";
String user = "root";
String password = "root";
String driverName = "com.mysql.jdbc.Driver";
Connection conn = null;
try {
Class clazz = Class.forName(driverName);
Driver driver = (Driver) clazz.newInstance();
//3.註冊驅動
DriverManager.registerDriver(driver);
//4.獲取連接
conn = DriverManager.getConnection(url, user, password);
}catch (Exception e){
e.printStackTrace();
}
return conn;
}
public static void closeAll(Connection connection, Statement statement, ResultSet rs){
if(connection != null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(statement != null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if( rs != null ){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
String sql = "select id,name,age from user";
Connection conn = null;
Statement statement = null;
ResultSet resultSet = null;
//try catch 獲取異常
try {
conn = DBUtil.getConnection();
statement = conn.createStatement();
resultSet = statement.executeQuery(sql);
//使用遍歷獲取數據
while (resultSet.next()){
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
System.out.println("id=" + id);
System.out.println("name=" + name);
System.out.println("age=" + age);
}
} catch (Exception exception) {
exception.printStackTrace();
}finally {
DBUtil.closeAll(conn,statement,resultSet);
}
}
3.2.5 sql注入問題
Sql注入是一種將sql代碼添加到輸入參數中,傳遞到Sql服務器解析並執行的一種攻擊手法
由於SQL語句是通過拼接完成的,那麼就有可能存在SQL注入攻擊
如何進行SQL注入攻擊?
數字注入
String sql = "select id,username,password from user where username = 'zxcaf1' or 1 = 1";
3.3 PreparedStatement的使用
使用preparestatement做查詢語句時可解決SQL注入的問題
@Test
public void testConnection4() throws Exception{
String url = "jdbc:mysql://localhost:3306/test";
String user = "root";
String password = "root";
String driverName = "com.mysql.jdbc.Driver";
//這裏的SQL語句沒有使用拼接,而是使用了?來代替字段
String sql = "select id,username,password from user where id = ? and username = ?";
//2、加載驅動
Class.forName(driverName);
//3、獲取連接
Connection conn = DriverManager.getConnection(url,user,password);
PreparedStatement preparedStatement = conn.prepareStatement(sql);
//爲 ? 設置參數
preparedStatement.setInt(1,1);
preparedStatement.setString(2,"ycw");
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()){
int id = resultSet.getInt("id");
String username = resultSet.getString("username");
String pwd = resultSet.getString("password");
System.out.println(id+username+pwd);
}
}
PreparedStatement可以有效防止sql注入,所以生產環境上一定要使用PreparedStatement,而不能使用Statement