JDBC原生方式連接數據庫示例-PreparedStatement方式

 

1、連接數據庫時的配置文件

##數據庫驅動
driver=com.mysql.jdbc.Driver
##MySQL連接信息
url=jdbc:mysql://127.0.0.1:3306/RUNOOB?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=GMT
##用戶名
username=root
##密碼
password=admin0001112

2、JDBC原生方式連接數據庫工具類

package com.wind.ssm.utils;

import org.springframework.stereotype.Component;

import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

/**
 * @Date: 2020/7/3 17:10
 * @Description: 原生JDBC連接數據庫
 */

/**
 * 事務:一組操作,要麼都成功,要麼都失敗
 * ACID原則:
 * (1)原子性:一組操作,要麼全都執行完成,要麼都不完成。
 * (2)一致性:這裏是說最終一致性,總數保持不變。
 * (3)隔離性:多個事務之間相互隔離,互不干擾(多個進程互不干擾)。
 * (4)持久性:事務一旦提交之後,就持久化到數據庫了,不能夠再去修改。
 * 隔離性的級別會帶來一些問題:
 * (1)髒讀:一個事務讀取到了另一個事務還沒有提交的數據
 * (2)不可重複度:在同一個事務內,重複讀取表中的某個數據,前後兩次讀取到的數據不一致(數據被更新了,是update操作)。
 * (3)幻讀:在一個事務內,讀取到了另一個事務插入的數據,導致前後兩次讀出來的結果不一致(記錄數增加了或者減少了,是insert或delete操作)。
 */

@Component
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 inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
            Properties properties = new Properties();
            properties.load(inputStream);
            driver = properties.getProperty("driver");
            url = properties.getProperty("url");
            username = properties.getProperty("username");
            password = properties.getProperty("password");

            //1.加載數據庫驅動(只需要加載一次即可)
            Class.forName(driver);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //2.獲取數據庫連接對象
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url, username, password);
    }

    //3.釋放數據庫連接資源
    public static void release(ResultSet resultSet, Statement statement, Connection connection) {
        try {
            if (resultSet != null && !resultSet.isClosed()) {
                resultSet.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

        try {
            if (statement != null && !statement.isClosed()) {
                statement.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

        try {
            if (connection != null && !connection.isClosed()) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

}

3、測試PreparedStatement對象

package com.wind.ssm.java.test;

import com.wind.ssm.entity.StudentEntity;
import com.wind.ssm.utils.JdbcUtils;
import org.springframework.stereotype.Component;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;


/**
 * @Date: 2020/7/3 18:40
 * @Description:
 */
@Component
public class JdbcUtilsTest2 {

    public static void main(String[] args) {
        Connection connection = null;
        PreparedStatement preparedStatement = null; //是Statement的子類
        ResultSet resultSet = null;

        try {
            //1.獲取數據庫連接
            connection = JdbcUtils.getConnection();

            //2.【新增SQL】參數使用佔位符而不是直接使用實際值hardcode
            String insertSql = "insert into RUN_Student(Name,ClassId,Status,Addtime,UpdateTime) values(?,?,?,?,?)";

            //3.預編譯SQL,此時並不執行SQL
            preparedStatement = connection.prepareStatement(insertSql);

            //4.給SQL中的佔位符設置具體值
            preparedStatement.setString(1, "南京市");
            preparedStatement.setInt(2, 2);
            preparedStatement.setInt(3, 1);
            preparedStatement.setDate(4, new java.sql.Date(new Date().getTime()));
            preparedStatement.setDate(5, new java.sql.Date(new Date().getTime()));

            //4.執行SQL,並且獲取結果。"查"executeQuery(),"增刪改"executeUpdate()。
            int insertRes = preparedStatement.executeUpdate();

            //5.處理結果
            if (insertRes > 0) {
                System.out.println("插入DB記錄成功");
            }


            //【更新SQL】
            String updateSql = "update RUN_Student set name = ? where id = ?";
            preparedStatement = connection.prepareStatement(updateSql);
            preparedStatement.setString(1, "南京市玄武區");
            preparedStatement.setInt(2, 19);
            int updateRes = preparedStatement.executeUpdate();
            if (updateRes > 0) {
                System.out.println("更新DB記錄成功");
            }

            //【刪除SQL】
            String deleteSql = "delete from RUN_Student where id = ?";
            preparedStatement = connection.prepareStatement(deleteSql);
            preparedStatement.setInt(1, 19);
            int deleteRes = preparedStatement.executeUpdate();
            if (deleteRes > 0) {
                System.out.println("刪除DB記錄成功");
            }

            //【查詢SQL】
            String selectSql = "select * from RUN_Student where id in (?,?)";
            preparedStatement = connection.prepareStatement(selectSql);
            preparedStatement.setInt(1, 1);
            preparedStatement.setInt(2, 2);
            resultSet = preparedStatement.executeQuery();
            while (resultSet.next()) {
                StudentEntity entity = new StudentEntity();
                entity.setId(resultSet.getInt("Id"));
                entity.setName(resultSet.getString("Name"));
                entity.setClassId(resultSet.getInt("ClassId"));
                entity.setStatus(resultSet.getInt("Status"));
                entity.setAddTime(resultSet.getDate("AddTime"));
                entity.setUpdateTime(resultSet.getDate("UpdateTime"));
                System.out.println("查詢DB記錄成功=entity=" + entity.toString());
            }

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JdbcUtils.release(resultSet, preparedStatement, connection);
        }


    }

}

 

4、PreparedStatement對象與Statement對象的區別

本人的幾點淺見,各位大大不喜勿噴(前面兩篇博客已經做了對應的測試)

先說下這倆到底是幹啥的吧。其實這倆乾的活兒都一樣,就是創建了一個執行對象,然後通過對象調用executeQuery方法來執行sql語句。說是CreateStatement和PrepareStatement的區別,但其實說的就是Statement和PrepareStatement的區別,相信大家在網上已經看到過不少這方面的資料和博客,我在此處提幾點,大家看到過的,就當是重新記憶,沒看到就當補充~下面開始談談它們的區別。

(1)最明顯的區別,就是執行的sql語句格式不同(是否存在佔位符)。我們放兩段代碼來看看他們的區別:
代碼背景:我們有一個數據庫,裏面有一個user表,有username, userpwd兩列。我們要查出這兩列的數據。

這是使用CreateStatement方法創建了stmt 對象,再通過他查詢的一部分語句片段。

String sql = "select * from users where  username= '"+username+"' and userpwd='"+userpwd+"'";  
stmt = conn.createStatement();  
rs = stmt.executeQuery(sql);  

而下面則是使用了PrepareStatement方法創建了pstmt 對象,再通過這個對象查詢的一部分語句片段。

String sql = "select * from users where  username=? and userpwd=?";  
pstmt = conn.prepareStatement(sql);  
pstmt.setString(1, username);  
pstmt.setString(2, userpwd);  
rs = pstmt.executeQuery();  

相信寫到這,大家很多人就能看出來了,原來PrepareStatement跟Statement的主要區別:就是把上面sql語句中的變量抽出來了。這就是我要說的第一大優點,PrepareStatement可以提高代碼的可讀性。什麼?你沒覺得這有什麼可以提高可讀性的?那好,咱來看看下面這兩段代碼,看完你再說話。

代碼背景:我們有一個數據庫,裏面有一個book表,有bookid, bookname, bookauthor, booksort, bookprice五列。我們要向這個表中添加一部分數據。

【Statement版】

String sql = "insert into book (bookid,bookname,bookauthor,booksort,bookprice) values ('"+var1+"',  
                                '"+var2+"',"+var3+",'"+var4+","+var5+"')";  
stmt = conn.createStatement();  
rs = stmt.executeUpdate(sql);  

【ParperStatement版】

String sql = "insert into book (bookid,bookname,bookauthor,booksort,bookprice) values (?,?,?,?,?)";  
pstmt = conn.prepareStatement(sql);  
pstmt.setString(1,var1);  
pstmt.setString(2,var2);  
pstmt.setString(3,var3);  
pstmt.setString(4,var4);  
pstmt.setString(5,var5);  
pstmt.executeUpdate();  

怎麼樣,反正我打這行代碼的時候,整個引號逗號就給我刺激懵了。
(2)下面說說第二點優點,ParperStatement提高了代碼的靈活性和執行效率。
PrepareStatement接口是Statement接口的子接口,它繼承了Statement接口的所有功能。它主要是拿來解決我們使用Statement對象多次執行同一個SQL語句的效率問題的。ParperStatement接口的機制是在數據庫支持預編譯的情況下 預先將SQL語句編譯,當多次執行這條SQL語句時,可以直接執行編譯好的SQL語句,這樣就大大提高了程序的靈活性和執行效率。

(3)最後但也是最重要的一個大大的比Statement好的優點,那就是安全,可以防止SQL注入!
你說啥?這還關安全啥事兒,那我給你一行代碼,你來給我說說這是幹嘛的。

String sql = "select * from user where username= '"+varname+"' and userpwd='"+varpasswd+"'";  
stmt = conn.createStatement();  
rs = stmt.executeUpdate(sql);  

這是驗證用戶名密碼的,對吧。但要是我們把 'or '1' = 1' 當作密碼傳進去,你猜猜會發生啥。

select * from user where username = 'user' and userpwd = '' or '1' = '1';  

發現了吧!這是個永真式,因爲1永遠等於1。所以不管怎樣都能獲取到權限。哇。這就壞咯!這還不是最壞的,你再看!

String sql = "select * from user where username= '"+varname+"' and userpwd='"+varpasswd+"'";  
stmt = conn.createStatement();  
rs = stmt.executeUpdate(sql);  


依舊是這行代碼。這次我們把 'or '1' = 1';drop table book;  當成密碼傳進去。哇!又壞了!這次直接把表給刪了。但是,你如果用PrepareStatement的話就不會出現這種問題,因爲它是把入參直接當做字符串來處理的,你傳入的這些數據根本不會跟原來的數據有任何的交集,也不會發生這些問題。文章寫到這就結束了。

轉自:https://blog.csdn.net/u011161786/article/details/48394751

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章