欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java JDBC API介紹與實(shí)現(xiàn)數(shù)據(jù)庫(kù)連接池流程

 更新時(shí)間:2022年12月08日 10:42:03   作者:-BoBooY-  
JDBC是指Java數(shù)據(jù)庫(kù)連接,是一種標(biāo)準(zhǔn)Java應(yīng)用編程接口( JAVA API),用來(lái)連接 Java 編程語(yǔ)言和廣泛的數(shù)據(jù)庫(kù)。從根本上來(lái)說(shuō),JDBC 是一種規(guī)范,它提供了一套完整的接口,允許便攜式訪問(wèn)到底層數(shù)據(jù)庫(kù),本篇文章我們來(lái)了解JDBC API及數(shù)據(jù)庫(kù)連接池

前言:上一節(jié)我?guī)Т蠹疑鲜至?a href="http://www.dbjr.com.cn/article/269474.htm" target="_blank">JDBC的基本代碼,這節(jié)我們仔細(xì)講一下JDBC的API和數(shù)據(jù)庫(kù)連接池。

JDBC API詳解

DriverManager

DriverManager(驅(qū)動(dòng)管理類(lèi))作用:注冊(cè)驅(qū)動(dòng)、獲取數(shù)據(jù)庫(kù)連接

注冊(cè)驅(qū)動(dòng)

registerDriver方法是用于注冊(cè)驅(qū)動(dòng)的,但是我們之前做的入門(mén)案例并不是這樣寫(xiě)的。而是如下實(shí)現(xiàn)

Class.forName("com.mysql.jdbc.Driver");

我們查詢(xún)MySQL提供的Driver類(lèi),看它是如何實(shí)現(xiàn)的,源碼如下:

static {
try {
DriverManager.registerDriver(new Driver());
}catch (SQLException var1) {
throw new RuntimeException( "can 't register driver! ");
}
}

在該類(lèi)中的靜態(tài)代碼塊中已經(jīng)執(zhí)行了 DriverManager 對(duì)象的registerDriver() 方法進(jìn)行驅(qū)動(dòng)的注冊(cè)了,那么我們只需要加載 Driver 類(lèi),該靜態(tài)代碼塊就會(huì)執(zhí)行。而Class.forName("com.mysql.jdbc.Driver"); 就可以加載Driver 類(lèi)。

提示:MySQL 5之后的驅(qū)動(dòng)包,可以省略注冊(cè)驅(qū)動(dòng)的步驟,自動(dòng)加載jar包中META-INF/services/java.sql.Driver文件中的驅(qū)動(dòng)類(lèi)

獲取數(shù)據(jù)庫(kù)連接

參數(shù)說(shuō)明:

  • user :用戶(hù)名
  • password:密碼
  • url : 連接路徑

語(yǔ)法:

jdbc:mysql://ip地址(域名):端口號(hào)/數(shù)據(jù)庫(kù)名稱(chēng)?參數(shù)鍵值對(duì)1&參數(shù)鍵值對(duì)2…

示例:
jdbc:mysql://localhost:3306/jdbc

補(bǔ)充:

  • 如果連接的是本機(jī)mysql服務(wù)器,并且mysql服務(wù)默認(rèn)端口是3306,則url可以簡(jiǎn)寫(xiě)為:jdbc:mysql:///數(shù)據(jù)庫(kù)名稱(chēng)?參數(shù)鍵值對(duì)
  • 配置 useSSL=false 參數(shù),禁用安全連接方式,解決警告提示

Connection

Connection(數(shù)據(jù)庫(kù)連接對(duì)象)作用:獲取執(zhí)行 SQL 的對(duì)象、管理事務(wù)

獲取執(zhí)行對(duì)象

普通執(zhí)行SQL對(duì)象

Statement createStatement()

入門(mén)案例中就是通過(guò)該方法獲取的執(zhí)行對(duì)象。

預(yù)編譯SQL的執(zhí)行SQL對(duì)象:防止SQL注入(重要?。。。?/p>

PreparedStatement prepareStatement(sql)

執(zhí)行存儲(chǔ)過(guò)程的對(duì)象

CallableStatement prepareCall(sql)

事務(wù)管理

回顧一下MySQL事務(wù)管理的操作:

開(kāi)啟事務(wù) :

BEGIN; 
或者 
START TRANSACTION;

提交事務(wù) :

COMMIT;

回滾事務(wù) :

ROLLBACK;

MySQL默認(rèn)是自動(dòng)提交事務(wù)

JDBC事務(wù)管理的方法

Connection幾口中定義了3個(gè)對(duì)應(yīng)的方法:

開(kāi)啟事務(wù)

參與autoCommit 表示是否自動(dòng)提交事務(wù),true表示自動(dòng)提交事務(wù),false表示手動(dòng)提交事務(wù)。而開(kāi)啟事務(wù)需要將該參數(shù)設(shè)為為false。

提交事務(wù)

回滾事務(wù)

案例測(cè)試事務(wù)管理

編寫(xiě)代碼

package com.bby;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
public class JdbcTransaction {
    public static void main(String[] args) throws Exception {
        String url = "jdbc:mysql:///jdbc?useSSL=false";
        String username = "root";
        String password = "1234";
        String sql = "update acount set money = 1002 where id = 1";
        String sql2 = "update acount set money = 1002 whe id = 1"; //注意這里SQL語(yǔ)句故意將where寫(xiě)錯(cuò)
        Class.forName("com.mysql.jdbc.Driver");
        Connection connection = DriverManager.getConnection(url, username, password);
        Statement statement = connection.createStatement();
        try {
            connection.setAutoCommit(false); // 開(kāi)啟手動(dòng)事務(wù)
            //執(zhí)行SQL語(yǔ)句并處理結(jié)果
            int count = statement.executeUpdate(sql);
            System.out.println(count);
            int count2 = statement.executeUpdate(sql2);
            System.out.println(count2);
            //提交事務(wù)
            connection.commit();
        } catch (Exception e) {
            //程序出現(xiàn)異常,回滾事務(wù)
            connection.rollback();
            e.printStackTrace();
        }        
    }
}

執(zhí)行程序,看控制臺(tái)結(jié)果

數(shù)據(jù)庫(kù)更新前

數(shù)據(jù)庫(kù)更新后

這里程序出現(xiàn)異常,進(jìn)行了事務(wù)的回滾,所以數(shù)據(jù)都沒(méi)有被更新

將代碼更改正確

String sql2 = "update acount set money = 1002 wher id = 2";

執(zhí)行程序測(cè)試

Statement

概述

Statement對(duì)象的作用就是用來(lái)執(zhí)行SQL語(yǔ)句。而針對(duì)不同類(lèi)型的SQL語(yǔ)句使用的方法也不一樣。

執(zhí)行DDL、DML語(yǔ)句

執(zhí)行DQL語(yǔ)句

代碼演示

此處只展示核心代碼,具體代碼可以參考上面的代碼案例

DML語(yǔ)句

// 定義sql
String sql = "update account set money = 3000 where id = 1";
// 獲取執(zhí)行sql的對(duì)象 Statement
Statement stmt = conn.createStatement();
// 執(zhí)行sql
int count = stmt.executeUpdate(sql);  //執(zhí)行完DML語(yǔ)句,受影響的行數(shù) 

執(zhí)行DDL語(yǔ)句

// 定義sql
String sql = "drop database db2";
// 獲取執(zhí)行sql的對(duì)象 Statement
Statement stmt = conn.createStatement();
// 執(zhí)行sql
int count = stmt.executeUpdate(sql); //執(zhí)行完DDL語(yǔ)句,可能是0

ResultSet

概述

ResultSet(結(jié)果集對(duì)象)作用:封裝了SQL查詢(xún)語(yǔ)句的結(jié)果

執(zhí)行了DQL語(yǔ)句后就會(huì)返回該對(duì)象,對(duì)應(yīng)執(zhí)行DQL語(yǔ)句的方法如下:

ResultSet executeQuery(sql):執(zhí)行DQL 語(yǔ)句,返回 ResultSet 對(duì)象

那么我們就需要從 ResultSet 對(duì)象中獲取我們想要的數(shù)據(jù)。ResultSet 對(duì)象提供了操作查詢(xún)結(jié)果數(shù)據(jù)的方法,如下:

如下圖為執(zhí)行SQL語(yǔ)句后的結(jié)果

一開(kāi)始光標(biāo)指定于第一行前,如圖所示紅色箭頭指向于表頭行。當(dāng)我們調(diào)用了 next() 方法后,光標(biāo)就下移到第一行數(shù)據(jù),并且方法返回true,此時(shí)就可以通過(guò) getInt(“id”) 獲取當(dāng)前行id字段的值,也可以通過(guò) getString(“name”) 獲取當(dāng)前行name字段的值。如果想獲取下一行的數(shù)據(jù),繼續(xù)調(diào)用 next() 方法,以此類(lèi)推。

代碼演示

編寫(xiě)JdbcResultSet

package com.bby;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class JdbcResultSet {
    public static void main(String[] args) throws Exception {
        Class.forName("com.mysql.jdbc.Driver");
        String url = "jdbc:mysql://localhost:3306/jdbc?useSSL=false";
        String username = "root";
        String password = "1234";
        Connection connection = DriverManager.getConnection(url, username, password);
        Statement statement = connection.createStatement();
        //定義查詢(xún)的SQL語(yǔ)句
        String sql = "select * from acount";
        //執(zhí)行查詢(xún)語(yǔ)句,獲取結(jié)果集
        ResultSet resultSet = statement.executeQuery(sql);
        //遍歷結(jié)果集
        while(resultSet.next()){
            int id = resultSet.getInt("id");
            String name = resultSet.getString("name");
            String money = resultSet.getString("money");
            System.out.println("id:" + id);
            System.out.println("name:" + name);
            System.out.println("money:" + money);
            System.out.println("-----------------");
        }
        resultSet.close();
        statement.close();
        connection.close();
    }
}

查看控制臺(tái)輸出

PreparedStatement

SQL注入

SQL注入是通過(guò)操作輸入來(lái)修改事先定義好的SQL語(yǔ)句,用以達(dá)到執(zhí)行代碼對(duì)服務(wù)器進(jìn)行攻擊的方法。

代碼模擬SQL注入問(wèn)題

package com.bby;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class JdbcSqlInjection {
    public static void main(String[] args) throws Exception {
        Class.forName("com.mysql.jdbc.Driver");
        String url = "jdbc:mysql://localhost:3306/jdbc?useSSL=false";
        String username = "root";
        String password = "1234";
        Connection connection = DriverManager.getConnection(url, username, password);
        // 接收用戶(hù)輸入 用戶(hù)名和密碼
        String name = "abcdefg";
        String pwd = "' or '1' = '1";
        String sql = "select * from users where name = '" + name + "' and password = '" + pwd + "'";
        Statement statement = connection.createStatement();
        ResultSet resultSet = statement.executeQuery(sql);
        // 判斷登錄是否成功
        if (resultSet.next()) {
            System.out.println("登錄成功~");
        } else {
            System.out.println("登錄失敗~");
        }
        resultSet.close();
        statement.close();
        connection.close();
    }
}

上面代碼是將用戶(hù)名和密碼拼接到sql語(yǔ)句中,拼接后的sql語(yǔ)句如下:

select * from users where name = 'abcdefg' and password = '' or '1' = '1'

從上面語(yǔ)句可以看出條件name = 'abcdefg' and password = ''不管是否滿(mǎn)足,而 or 后面的 '1' = '1' 是始終滿(mǎn)足的,最終條件是成立的,就可以正常的進(jìn)行登陸了。

所以不管登錄的密碼是否正確都可以登錄成功,控制臺(tái)輸出如下

PreparedStatement 概述

PreparedStatement作用:預(yù)編譯SQL語(yǔ)句并執(zhí)行,預(yù)防SQL注入問(wèn)題

獲取 PreparedStatement 對(duì)象

// SQL語(yǔ)句中的參數(shù)值,使用?占位符替代
String sql = "select * from users where name = ? and password = ?";
// 通過(guò)Connection對(duì)象獲取,并傳入對(duì)應(yīng)的sql語(yǔ)句
PreparedStatement ps = connection.prepareStatement(sql);

設(shè)置參數(shù)值

上面的sql語(yǔ)句中參數(shù)使用 ? 進(jìn)行占位,在之前之前肯定要設(shè)置這些 ? 的值。

PreparedStatement對(duì)象:

setXxx(參數(shù)1,參數(shù)2);//給 ? 賦值, 參數(shù)1是編號(hào)(從1開(kāi)始) 參數(shù)2是值

(1)Xxx:數(shù)據(jù)類(lèi)型 ; 如 setInt (參數(shù)1,參數(shù)2)

(2)參數(shù)1: ?的位置編號(hào),從1 開(kāi)始 參數(shù)2: ?的值

執(zhí)行SQL語(yǔ)句

executeUpdate(); // 執(zhí)行DDL語(yǔ)句和DML語(yǔ)句
executeQuery(); // 執(zhí)行DQL語(yǔ)句

注意:調(diào)用這兩個(gè)方法時(shí)不需要傳遞SQL語(yǔ)句,因?yàn)楂@取SQL語(yǔ)句執(zhí)行對(duì)象時(shí)已經(jīng)對(duì)SQL語(yǔ)句進(jìn)行預(yù)編譯了。

代碼演示

編寫(xiě)JdbcPreparedStatement

package com.bby;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class JdbcPreparedStatement {
    public static void main(String[] args) throws Exception {
        String url = "jdbc:mysql:///jdbc?useSSL=false";
        String username = "root";
        String password = "1234";
        String name = "abcdefg";
        String pwd = "' or '1' = '1";
        Class.forName("com.mysql.jdbc.Driver");
        Connection connection = DriverManager.getConnection(url, username, password);
        //定義SQL語(yǔ)句
        String sql = "select * from users where name = ? and password = ?";
        //預(yù)編譯SQL語(yǔ)句
        PreparedStatement ps = connection.prepareStatement(sql);
        //填充占位符
        ps.setString(1, name);
        ps.setString(2, pwd);
        //執(zhí)行SQL語(yǔ)句
        ResultSet resultSet = ps.executeQuery();
        //判斷登錄是否成功
        if(resultSet.next()) {
            System.out.println("登陸成功!");
        } else {
            System.out.println("登錄失敗");
        }
        resultSet.close();
        ps.close();
        connection.close();
    }
}

查看控制臺(tái)結(jié)果

執(zhí)行上面語(yǔ)句就可以發(fā)現(xiàn)不會(huì)出現(xiàn)SQL注入漏洞問(wèn)題了。那么PreparedStatement又是如何解決的呢?它是將特殊字符進(jìn)行了轉(zhuǎn)

義,轉(zhuǎn)義的SQL如下:

select * from `users` where name = 'abcdefg' and password = '\'or \'1\' = \'1'

PreparedStatement原理

PreparedStatement 好處:1.預(yù)編譯SQL,性能更高。2.防止SQL注入:將敏感字符進(jìn)行轉(zhuǎn)義

Java代碼操作數(shù)據(jù)庫(kù)流程如圖所示:

將sql語(yǔ)句發(fā)送到MySQL服務(wù)器端

MySQL服務(wù)端會(huì)對(duì)sql語(yǔ)句進(jìn)行如下操作

檢查SQL語(yǔ)句

檢查SQL語(yǔ)句的語(yǔ)法是否正確。

編譯SQL語(yǔ)句。將SQL語(yǔ)句編譯成可執(zhí)行的函數(shù)。

檢查SQL和編譯SQL花費(fèi)的時(shí)間比執(zhí)行SQL的時(shí)間還要長(zhǎng)。如果我們只是重新設(shè)置參數(shù),那么檢查SQL語(yǔ)句和編譯SQL語(yǔ)句將不需要重復(fù)執(zhí)行。這樣就提高了性能。

執(zhí)行SQL語(yǔ)句

MySQL服務(wù)端將結(jié)果返回

數(shù)據(jù)庫(kù)連接池

數(shù)據(jù)庫(kù)連接池簡(jiǎn)介

  • 數(shù)據(jù)庫(kù)連接池是個(gè)容器,負(fù)責(zé)分配、管理數(shù)據(jù)庫(kù)連接(Connection)
  • 它允許應(yīng)用程序重復(fù)使用一個(gè)現(xiàn)有的數(shù)據(jù)庫(kù)連接,而不是再重新建立一個(gè);
  • 釋放空閑時(shí)間超過(guò)最大空閑時(shí)間的數(shù)據(jù)庫(kù)連接來(lái)避免因?yàn)闆](méi)有釋放數(shù)據(jù)庫(kù)連接而引起的數(shù)據(jù)庫(kù)連接遺漏
  • 好處:資源重用、提升系統(tǒng)響應(yīng)速度、避免數(shù)據(jù)庫(kù)連接遺漏

之前我們代碼中使用連接使沒(méi)有使用都創(chuàng)建一個(gè)Connection對(duì)象,使用完畢就會(huì)將其銷(xiāo)毀。這樣重復(fù)創(chuàng)建銷(xiāo)毀的過(guò)程是特別耗費(fèi)計(jì)算機(jī)的性能的及消耗時(shí)間的。而數(shù)據(jù)庫(kù)使用了數(shù)據(jù)庫(kù)連接池后,就能達(dá)到Connection對(duì)象的復(fù)用,如下圖:

連接池是在一開(kāi)始就創(chuàng)建好了一些連接(Connection)對(duì)象存儲(chǔ)起來(lái)。用戶(hù)需要連接數(shù)據(jù)庫(kù)時(shí),不需要自己創(chuàng)建連接,而只需要從連

接池中獲取一個(gè)連接進(jìn)行使用,使用完畢后再將連接對(duì)象歸還給連接池;這樣就可以起到資源重用,也節(jié)省了頻繁創(chuàng)建連接銷(xiāo)毀連接

所花費(fèi)的時(shí)間,從而提升了系統(tǒng)響應(yīng)的速度。

數(shù)據(jù)庫(kù)連接池實(shí)現(xiàn)

標(biāo)準(zhǔn)接口:DataSource

官方(SUN) 提供的數(shù)據(jù)庫(kù)連接池標(biāo)準(zhǔn)接口,由第三方組織實(shí)現(xiàn)此接口。該接口提供了獲取連接的功能:

Connection getConnection()

那么以后就不需要通過(guò) DriverManager 對(duì)象獲取 Connection對(duì)象,而是通過(guò)連接池(DataSource)獲取 Connection 對(duì)象。

常見(jiàn)的數(shù)據(jù)庫(kù)連接池:DBCP 、C3P0 、Druid

我們現(xiàn)在使用更多的是Druid,它的性能比其他兩個(gè)會(huì)好一些

Druid(德魯伊)

Druid連接池是阿里巴巴開(kāi)源的數(shù)據(jù)庫(kù)連接池項(xiàng)目,功能強(qiáng)大,性能優(yōu)秀,是Java語(yǔ)言最好的數(shù)據(jù)庫(kù)連接池之一

Driud 的使用

導(dǎo)入jar包 druid-1.1.12.jar

將druid的jar包放到項(xiàng)目下的lib下并添加為庫(kù)文件

編寫(xiě)配置文件 druid.properties

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///jdbc
useSSL=false&useServerPrepStmts=true
username=root
password=1234
# 初始化連接數(shù)量
initialSize=5
# 最大連接數(shù)
maxActive=10
# 最大等待時(shí)間
maxWait=3000

編寫(xiě)Java代碼(JdbcDruid)

package com.bby;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.FileInputStream;
import java.sql.Connection;
import java.util.Properties;
public class JdbcDruid {
    public static void main(String[] args) throws Exception {
        // 加載配置文件
        Properties prop = new Properties();
        prop.load(new FileInputStream("jdbc-demo/src/druid.properties"));
        // 獲取連接池對(duì)象
        DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
        // 獲取數(shù)據(jù)庫(kù)連接 Connection
        Connection connection = dataSource.getConnection();
        System.out.println(connection); //獲取到了連接后就可以繼續(xù)做其他操作了
    }
}

運(yùn)行查看控制臺(tái)結(jié)果

到此這篇關(guān)于Java JDBC API介紹與實(shí)現(xiàn)數(shù)據(jù)庫(kù)連接池流程的文章就介紹到這了,更多相關(guān)Java JDBC API內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論