1. 程式人生 > >第二天:JDBC和DBUtils

第二天:JDBC和DBUtils

jdbc dbutils

一、eclipse的使用

1、修改註釋內容

選擇window---->>preferences

技術分享

選擇Java---->>codestyle---->>code template---->>comments,然後雙擊types,修改裏面的內容即可!

技術分享

2、修改Eclipse裏面的快捷鍵

舉例:修改復制行內容快捷鍵。

1.點擊window菜單->preferences子菜單->general->keys,進入快捷鍵管理界面

技術分享

2.在這裏可以查找所有功能的快捷鍵,需要修改或新增時,點擊需要修改或新增的命令,在binding裏設置快捷鍵

技術分享

3.設置完快捷鍵後,還需要設置在什麽時候可以使用該快捷鍵,eclipse提供各種場景供選擇,一般選擇In Windows(即在eclipse窗口激活狀態)即可

技術分享

完成以上操作,點擊OK即完成設置

技術分享

3、設置工作空間的編碼

技術分享

二、JUnit單元測試

1、介紹:JUnit是一個java語言的單元測試框架。屬於第三方工具,一般情況下需要導入jar包,不過多數java開發環境已經集成了JUnit作為單元測試工具。

創建“day07”java項目,並創建“cn.itcast.a_junit”包

技術分享

2、編寫測試類,簡單理解可以用於取代java的main方法

3、在測試類方法上添加註解@Test

4、註解修飾方法要求:public void 方法名(){...},方法名自定義建議test開頭

技術分享

5、添加STS中集成的Junit庫,鼠標點擊“@Test”,使用快捷鍵“ctrl+1”,點擊“Add Junit...”

技術分享

結果

技術分享

6、使用:選中方法右鍵,執行當前方法;選中類名右鍵,執行類中所有方法(方法必須標註@Test)

技術分享

7、常用註解

@Test,用於修飾需要執行的方法

@Before,測試方法前執行的方法

@After,測試方法後執行的方法

技術分享

8、常見使用錯誤,如果沒有添加“@Test”,使用“Junit Test”進行運行,將拋異常

技術分享

三、JDBC

1、什麽是JDBC

JDBC(Java DataBase Connectivity)就是java數據庫連接,簡單地說就是用java語言來操作數據庫。原來我們操作數據庫是在控制臺使用SQL語句來操作數據庫,JDBC是用Java語言向數據庫發送SQL語句

<1>JDBC是一種用於執行SQL語句的java API

<2>JDBC可以為多種關系數據庫提供統一訪問入口

<3>JDBC由一組java工具類和接口組成

2、JDBC原理

早期SUN公司的天才們想編寫一套可以連接天下所有數據庫的API,但是當他們剛剛開始時就發現這是不可完成的任務,因為各個廠商的數據庫服務器差異太大了。後來SUN開始與數據庫廠商們討論,最終得出的結論是,由SUN提供一套訪問數據庫的規範(就是一組接口),並提供連接數據庫的協議標準,然後各個數據庫廠商會遵循SUN的規範提供一套訪問自己公司的數據庫服務器的API出現。SUN提供的規範命名為JDBC。而各個廠商提供的,遵循了JDBC規範的,可以訪問自數據庫的API被稱之為驅動。

技術分享

JDBC是接口,而JDBC驅動才是接口的實現,沒有驅動無法完成數據庫連接!每個數據庫廠商都有自己的驅動,用來連接自己公司的數據庫。

當然還有第三方公司專門為某一數據庫提供驅動,這樣的驅動往往不是開源免費的!

3、JDBC核心類(接口)介紹

JDBC中的核心類有:DriverManager、Connection、Statement和ResultSet

DriverManager(驅動管理器)的作用有兩個

<1>註冊驅動:這可以讓JDBC知道要使用的是哪個驅動;

<2>獲取Connection:如果可以獲取到Connection,那麽說明已經與數據庫連接上了。

Connection對象表示連接,與數據庫的通訊都是通過這個對象展開的;

<3>Connection最為重要的一個方法就是用來獲取Statement對象;

<4>Statement是用來向數據庫發送SQL語句的,這樣數據庫會執行發送過來的的SQL語句

<5>void executeUpdate(String sql):執行更新操作(insert、update、delete等);

<6>ResultSet executeQuery(String sql):執行查詢操作,數據庫在執行查詢後會返回查詢結果,查詢 結果就是ResultSer;

ResultSet對象表示查詢結果集,只有在執行查詢操作後才會有結果集的產生。結果集是一個人二維 的表格,有行有列。操作結果集要學習移動ResultSet內部的“行光標”,以及獲取當前行上的數據

<7>boolean next():使“行光標”移動到下一行,並返回移動後的行是否存在;

<8>xxx getxxx(int col):獲取當前行指定列上的值,參數就是列數,列數從1開始,而不是0

4、JDBC開發步驟

4.1 導入mysql數據庫的驅動jar包:

<1>創建lib目錄,用於存放當前項目需要的所有jar包

<2>選擇jar包,右鍵執行build path/Add to Build Path

技術分享


4.2 註冊驅動

註冊驅動就只有一句話:Class.forName("com.mysql.jdbc.Driver"),

下面的內容都是對這句代碼的解釋。今後我們的代碼中,與註冊驅動相關的代碼只有這一句。

<1>分析步驟1:JDBC規範定義驅動接口:java.sql.Driver,

MySql驅動包提供了實現類:com.mysql.jdbc.Driver
<2>分析步驟2:DriverManager工具類,提供註冊驅動的方法registerDriver(),

方法的參數是java.sql.Driver,所以我們可以通過如下語句進行註冊

DriverManager.registerDriver(new com.mysql.jdbc.Driver());

以上代碼不推薦使用,存在兩方面不足:

1.硬編碼,後期不易於程序擴展和維護

2.驅動被註冊兩次

<3>分析步驟3:通常開發我們使用Class.forName()加載一個使用字符串描述的驅動類。

如果使用Class.forName()將加載到內存,該類的靜態代碼將自動執行

通過查詢com.mysql.jdbc.Driver源碼,我們發現Driver類“主動”將自己進行註冊

DriverManager類的registerDriver()方法的參數是java.sql.Driver,但java.sql.Driver是一個接口,實現類由mysql驅動來提供,mysql驅動中的java.sql.Driver接口的實現類為com.mysql.Driver那麽註冊驅動的代碼如下:

DriverManager.registerDriver(new com.mysql.jdbc.Driver());

上面的代碼雖然可以註冊驅動,但是出現硬編碼(代碼依賴mysql驅動jar包),如果將來想連接Oracle數據庫,那麽必須要修改代碼。並且其實這種註冊驅動的方式是註冊了兩次驅動!

JDBC中規定,驅動類在被加載時,需要自己“主動”把自己註冊到DriverManager中,下面來看看com.mysql.jdbc.Driver類的源代碼:

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
        } catch(SQLException E) {
            throw newRuntimeException("Can‘t register driver!");
        }
    }
……
}

com.mysql.jdbc.Driver類中的static塊會創建本類對象,並註冊到DriverManager中,這說明只要去加載com.mysql.jdbc.Driver類,那麽就會執行這個static塊,從而也就會把com.mysql.jdbc.Driver註冊到DriverManager中,所以可以把註冊驅動類的代碼修改為加載驅動類。

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


4.3 獲取連接

代碼:Connection con=DriverManager.getConnection("jdbc:mysql://localhost:3306/mydbl","root","root")

獲取連接需要兩步,一是使用DriverManager來註冊驅動,二是使用DriverManager來獲取Connection對象。

獲取連接的也只有一句話代碼:

DriverManager.getConnection(url,username,password);

其中username和password是登錄數據庫的用戶名和密碼

url相對復雜一點,它是用來找到要連接數據庫的“網址”,就好比你要在瀏覽器中查找百度時,也需要提供一個url。下面是mysql的url:

jdbc:mysql://localhost:3306/mydbl

JDBC規定url的格式由三部分組成,每個部分中間使用冒號分割。

<1>第一部分是jdbc,這是固定的;

<2>第二部分是數據庫名稱,那麽連接mysql數據庫,第二部分當然是mysql了;

<3>第三部分是由數據庫廠商規定的,我們需要了解每個數據庫廠商的要求,mysql的第三部分分別 由數據庫服務器的IP地址(localhost)、端口號(3306),以及database名稱(mybdl)組成

下面是獲取連接的語句:

Connection con=DriverManager.getConnection("jdbc:mysql://localhost:3306/web08","root","root");

還可以在url中提供參數:

jdbc:mysql://localhost:3306/web08?useUnicode=true&characterEncoding=UTF8

useUnicode參數指定這個連接數據庫的過程中,使用的字節集是Unicode字節集;

characterEncoding參數指定java程序連接數據庫的過程中,使用的字符集編碼為UTF-8編碼。註意,mysql中指定UTF-8編碼給出的是UTF8,而不是UTF-8


4.4 獲得語句執行

String sql="insert into category(cid,cname) values(‘c007‘,‘分類‘)";

Statement stmt=con.createStatement();

在得到Connection之後,說明已經與數據庫連接上了,下面是通過Connection獲取Statement對象的代碼:

Statement stmt=con.createStatement();

Statement是用來向數據庫發送要執行的SQL語句的

常用方法:

執行SQL語句:

<1>int executeUpdate(String sql);---執行insert update delete語句.(DML語句)

<2>ResultSet executeQuery(String sql);---執行select語句(DQL語句)

<3>boolean execute(String sql);---執行select返回true,執行其他的語句返回false

如果返回true,需要使用getResultSet()獲得查詢結果

如果返回false,需要使用getUpdateCount()獲得影響行數

執行批處理:(可選)

addBatch(String sql);

clearBatch();

executeBatch();


4.5 預處理對象

使用處理對象時,建議每條sql語句所有的實際參數,都使用逗號分隔

String sql="insert into category(cid,cname) values(?,?)";

PreparedStatement psmt=con.prepareStatement(sql);

常用方法:

執行SQL語句:

<1>int executeUpdate(String sql);---執行insert update delete語句.(DML語句)

<2>ResultSet executeQuery(String sql);---執行select語句(DQL語句)

<3>boolean execute(String sql);---執行select返回true,執行其他的語句返回false

設置實際參數:

setxxx(int,T)通過setter方法將?占位符替換成實際參數

例如:setString()實際參數類型為hi字符串

執行批處理:(可選)

addBatch; 添加批處理的實際參數,需要調方法前執行對應setter方法

clearBatch();

executeBatch();


4.6 處理結果集

ResultSet就是一張二維的表格,它內部有一個“行光標”,光標默認位置在“第一行上方”,我們可以調用rs對象的next()方法把“行光標”向下移動一行,當第一次調用next()方法時,“行光標”就到了第一行記錄的位置,這時就可以使用ResultSet提供的getxxx(int col)方法來獲取指定列的數據了:

rs.next();//光標移動到第一行

rs.getInt(1);//光標移動到第一列的數據

技術分享

當你使用rs.getInt(1)方法時,你必須可以肯定第1列的數據類型就是int類型,如果你不能肯定,那麽最好使用rs.getObject(1)。在ResultSet類中提供了一系列的getxxx()方法,比較常用的方法有:

Object getObject(int col)

String getString(int col)

int getInt(int col)

double getDouble(int col)


4.7 釋放資源

與IO流一樣,使用後的東西都需要關閉,關閉的順序是先得到的後關閉,後得到的先關閉

rs.close();

stmt.close();

con.close;


4.8 完成查詢操作代碼

    public static Connection getConnection() throws Exception {
        Class.forName("com.mysql.jdbc.Driver");
        String url ="jdbc:mysql://localhost:3306/web08";
        return DriverManager.getConnection(url, "root", "root");
    }
    @Test
    public void query() throws Exception{
        Connectioncon = getConnection();
        Statementstmt = con.createStatement();
        String sql ="select * from user";
        ResultSet rs= stmt.executeQuery(sql);
        while(rs.next()){
            String username = rs.getString(1);
            String password = rs.getString(2);
            System.out.println(username+ ", " + password);
        }
    }


4.9 規範化代碼

所謂規範化代碼就是無論是否出現異常,都要關閉ResultSet、Statement,以及Connection

    @Test
    public void query() {
        Connection con = null;
        Statement stmt = null;
        ResultSet rs= null;
        try {
            con = getConnection();
            stmt =con.createStatement();
            String sql = "select * from user";
            rs =stmt.executeQuery(sql);
            while(rs.next()){
                String username = rs.getString(1);
                String password = rs.getString(2);
                System.out.println(username+ ", " + password);
            }
        } catch(Exceptione) {
            throw newRuntimeException(e);
        } finally {
            try {
                if(rs != null)rs.close();
                if(stmt != null) stmt.close();
                if(con != null)con.close();
            } catch(SQLExceptione) {}
        }
    }

4.10 案例:查詢所有


技術分享

技術分享


5、JDBC對象介紹

5.1JDBC中的主要類(接口)

<1>DriverManager

<2>Connection

<3>Statement

<4>ResultSet


5.2 DriverManager

我們今後只需要會用DriverManager的getConnection()方法即可:

<1>Class.forName("com.mysql.jdbc.Driver");

<2>String url="jdbc:mysql://localhost:3306/web08";

<3>String username="root"

<4>String password="root"

<5>Connection con=DriverManager.getConnection(url,username,password);


註意:上面代碼可能出現的兩種異常:

<1>ClassNotFoundException:這個異常是在第1句上出現的,出現這個異常有兩個可能:

(1)你沒有給出mysql的jar包;

(2)你把類名打錯了,查看類名是不是com.mysql.jdbc.Driver。

<2>SQLException:這個異常出現在第5句,出現這個異常就是三個參數的問題,往往username和password一般不會出錯,所以需要認真查看url是否打錯

對於DriverManager.registerDriver()方法了解即可,因為我們今後註冊驅動只會用 Class.forName(),而不會使用這個方法

5.3 Connection

Connection最為重要的方法就是獲取Statement:

(1)Statement stmt=con.createStatement();

後面在學習ResultSet方法時,還要學習一下下面的方法

(2)Statement stmt=con.createStatement(int,int);

5.4 Statement

Statement最為重要的方法是:

<1>int executeUpdate(String sql):執行更新操作,即執行insert、update、delete語句,其實

這個方法也可以執行create table、alter table,以及drop table等語句,但我們很少會使JDBC

來執行這些語句;

<2>ResultSet executeQuery(String sql):執行查詢語句,執行查詢操作會返回ResultSet,即結果集;

<3>boolean execute():Statement還有一個boolean execute()方法,這個方法可以用來執行 增、刪、改、查所有的SQL語句,該方法返回的是boolean類型,表示SQL語句是否執行成功。

如果使用execute()方法執行的是更新語句,那麽還要調用int getUpdateCount()來獲取insert、 update、delete語句所影響的行數

如果使用execute()方法執行的是查詢語句,那麽還要調用ResultSet、getResultSet()來獲取 select語句的查詢結果

5.5 ResultSet之滾動結果集(了解)

ResultSet表示結果集,它是一個二維的表格!ResultSet內部維護一個行光標(遊標),ResultSet提供了一系列的方法來移動遊標:

<1>void beforeFirst():把光標放到第一行的前面,這也是光標默認的位置;

<2>void afterLast():把光標放到最後一行的後面;

<3>boolean first():把光標放到第一行的位置上,返回值表示調控光標是否成功;

<4>boolean last():把光標放到最後一行的位置上;返回值表示調控光標是否成功

<5>boolean isBeforeFirst():當前光標位置是否在第一行前面;

<6>boolean isAfterLast():當前光標位置是否在最後一行的後面;

<7>boolean isFirst():當前光標位置是否在第一行上;

<8>boolean isLast:當前光標位置是否在最後一行上;

<9>boolean next():把光標向下挪一行;

<10>boolean relative(int row):相對位移,當row為正數時,表示向下移動row行,為負數時 表示向上移動row行;

<11>boolean absolute(int row):絕對位移,把光標移動到指定行上;

<12>int getRow():返回當前光標所有行


上面方法分為兩類,一類用來判斷遊標位置的,另一類是用來移動遊標的。如果結果集是不可滾動的,那麽只能使用next()方法來移動遊標。而beforeFirst()、afterLast()、first()、last()、previous()、relative()方法都不能使用!

結果集是否支持滾動,要從Connection類的createStatement()方法說起,也就是說創建的Statement決定了使用Statement創建的ResultSet是否支持滾動

Statement createStatement(int resultSetType,int resultSetConcurrency)

resultSetType的可選值:

(1)ResultSet.TYPE_FORWARD_ONLY:不滾動結果集

(2)ResultSet.TYPE_SCROLL_INSENSITIVE:滾動結果集,但結果集數據不會再跟隨數據庫而變 化;

(3)ResultSet.TYPE_SCROLL_SENSITIVE:滾動結果集,但結果集數據不會在跟隨數據庫而變化


可以看出,如果想使用滾動的結果集,我們應該選擇TYPE_SCROLL_INSENSITIVE!其實很少有數據庫驅動會支持TYPE_SCROLL_SENSITIVE的特性!通常我們也不需要查詢到的結果集再受到數據庫變化的影響

resultSetConcurrency的可選值:

(1)CONCUR_READ_ONLY:結果集是只讀的,不能通過修改結果集而反向影響數據庫;

(2)CONCUR_UPDATABLE:結果集是可更新的,對結果集的更新可以反向影響數據庫;


通常可更新結果集這一“高級特性”我們也是不需要的!


獲取滾動結果集的代碼如下:

Connection con=Statement stmt=con.createStatement(ResultSet.TYPE.SCROLL.INSENSITIVE,CONCUR_READ_ONLY);

String sql=....//查詢語句

ResultSet rs=stmt.executeQuery(sql);//這個結果集是可滾動的

5.6 ResultSet之獲取列數據

可以通過next()方法使ResultSet的遊標向下移動,當遊標移動到你需要的行時,就需要來獲取該行的數據了,ResultSet提供了一系列的獲取列數據的方法:

(1)String getString(int columnIndex):獲取指定列的String類型數據;

(2)int getInt(int columnIndex):獲取指定列的int類型數據

(3)double getDouble(int columnIndex):獲取指定列的double類型數據;

(4)boolean getBoolean(int columnIndex):獲取指定列的boolean類型數據

(5)Object getObject(int columnIndex):獲取指定列的Object類型的數據


上面的方法中,參數columnIndex表示列的索引,列索引從1開始,而不是0,這第一點與數組不同,如果你清楚當前列的數據類型,那麽可以使用getInt()之類的方法來獲取,如果你不清楚列的類型,那麽你應該使用getObject()方法類獲取

ResultSet還提供了一套通過列名稱來獲取列數據的方法:

(1)String getSting(String columnName):獲取名稱為columnName的列的String數據;

(2)int getInt(String columnName):獲取名稱為columnName的列的int數據

(3)double getDouble(String columnName):獲取名稱為columnName的列的double數據;

(4)boolean getBoolean(String columnName):獲取名稱為columnName的列的boolean數據;

(5)Object getObject(String columnName):獲取名稱為columnName的列的Object數據

6、SQL註入

6.1 什麽是SQL攻擊

在需要用戶輸入的地方,用戶輸入的是SQL語句的片段,最終用戶輸入的SQL片段與我們dao中寫的SQL語句合成一個完整的SQL語句!例如用戶在登錄時輸入的用戶名和密碼都是為SQL語句的片段

6.2 演示SQL攻擊

首先我們需要創建一張用戶表,用來存儲用戶的信息

    CREATE TABLE user(
    uid CHAR(32)PRIMARY KEY,
    username   VARCHAR(30)UNIQUE KEY NOT NULL,
    PASSWORD   VARCHAR(30)
    );
 
    INSERT INTO userVALUES(‘U_1001‘, ‘zs‘, ‘zs‘);
    SELECT * FROM user;

現在用戶表中只有一行記錄,就是zs。

下面我們寫一個login()方法

    public void login(String username, String password) {
        Connectioncon = null;
        Statementstmt = null;
        ResultSet rs= null;
        try {
            con =JdbcUtils.getConnection();
            stmt =con.createStatement();
            String sql = "SELECT * FROM user WHERE " +
                    "username=‘" +username + 
                    "‘and password=‘" + password + "‘";
            rs =stmt.executeQuery(sql);
            if(rs.next()){
                System.out.println("歡迎" +rs.getString("username"));
            } else {
                System.out.println("用戶名或密碼錯誤!");
            }
        } catch(Exception e) {
            throw newRuntimeException(e);
        } finally {
            JdbcUtils.close(con,stmt, rs);
        }       
    }

下面是調用這個方法的代碼:

login("a‘ or ‘a‘=‘a", "a‘or ‘a‘=‘a");

這行當前會使我們登錄成功,因為輸入的用戶名和密碼是SQL語句片段,最終與我們的login()方法中的SQL語句組合在一起,我們來看看組合在一起的SQL語句

SELECT * FROM tab_user WHERE username=‘a‘or ‘a‘=‘a‘ and password=‘a‘ or ‘a‘=‘a‘

6.3防止SQL攻擊

過濾用戶輸入的數據中是否包含非法字符;

分步校驗,先使用用戶名來查詢用戶,如果查找到了,再比較密碼;

使用PreparedStatement

6.4 PreparedStatement是什麽?

PreparedStatement叫預編譯聲明

PreparedStatement是Statement的子接口,你可以使用PreparedStatement來替換Statement

PreparedStatement的好處:

(1)防止SQL攻擊

(2)提高代碼的可讀性,以及可維護性

(3)提高效率

6.5 PreparedStatement的使用

使用Connection的prepareStatement(String sql):即創建它時就讓它與一條SQL模板綁定;

調用PreparedStatement的setXXX()系列方法為問號設置值;

調用executeUpdate()或executeQuery()方法,但要註意,調用沒有參數的方法;

String sql = “select * from tab_studentwhere s_number=?”;
PreparedStatement pstmt =con.prepareStatement(sql);
pstmt.setString(1, “S_1001”);
ResultSet rs = pstmt.executeQuery();
rs.close();
pstmt.clearParameters();
pstmt.setString(1, “S_1002”);
rs= pstmt.executeQuery();

在使用Connection創建PreparedStatement對象時需要給出一個SQL模板,所謂SQL模板就是有“?”的SQL語句,其中“?”就是參數

在得到PreparedStatement對象後,調用它的setxxx()方法為“?”賦值,這樣就可以得到把模板變成一條完整的SQL語句,然後再調用PreparedStatement對象的executeQuery()方法獲取ResultSet對象

註意:PreparedStatement對象獨有的executeQuery()方法是沒有參數的,而Statement的executeQuery()是需要參數(SQL語句)的。因為在創建PreparedStatement對象時已經讓它與一條SQL模板綁定在一起了,所以在調用它的executeQuery()和executeUpdate()方法時就不再需要參數了。

PreparedStatement最大的好處就是在於重復使用同一模板,給予其不同的參數來重復的使用它。這才是真正提高效率的原因

所依,在以後的開發中,無論什麽情況,都去使用PreparedStatement,而不是使用Statement.


7、使用JDBC完成分類表CRUD(增刪改查)的操作

7.1 案例分析

使用JDBC對分類表category進行增刪改查操作


7.2 工具類

“獲得連接”和“釋放資源”兩次代碼將在之後的增刪改查所有功能中都存在,開發中一般遇到此種情況,將采用工具類的方法進行抽取,從而達到代碼的重復利用

技術分享


7.3 獲得連接

技術分享

技術分享


7.4 釋放資源

如果釋放資源采用依次關閉三個對象,那麽第一個對象關閉時拋出了異常,後面兩個對象將無法成功釋放資源,通常我們使用try-catch塊進行處理

方法1:多個try-catch塊,將資源釋放,容易理解

技術分享

技術分享

方法2:try-catch-finally嵌套,資源釋放時如果出錯,將通知調用者

技術分享


7.5 使用properties配置文件

開發中獲得連接的4個參數(驅動、URL、用戶名、密碼)通常都存在配置文件中,方便後期維護,程序如果需要更換數據庫,只需要修改配置文件即可

通常情況下,我們習慣使用properties文件,此文件我們將做如下要求:

<1>文件位置:任意,建議src下

<2>文件名稱:任意,擴展名為properties

<3>文件內容:一行一組數據,格式是“key=value”

(1)key命名自定義,如果是多個單詞,習慣使用點分割。例如:jdbc.driver

(2)value值不支持中文,如果需要使用非英文字符,將進行unicode轉換


7.5.1 創建配置文件

右鍵/New/File,輸入“db.properties”文件名

技術分享

技術分享


7.5.2 加載配置文件:ResourceBundle對象

ResourceBundle提供getBundle()方法用於只提供properties文件即可,之後使用getString(key)通過key獲得value的值

技術分享

技術分享

技術分享

7.5.3 獲得連接

技術分享


7.5.4 加載配置文件:Properties對象(可選)

對應properties文件處理,開發中也會使用Properties對象進行。在v3版本中我們將采用加載properties文件獲得流,然後使用Properties對象進行處理

技術分享

技術分享

技術分享

7.6 實現

7.6.1 模板


技術分享

技術分享

7.6.2 添加:insert into

技術分享

7.6.3 更新:update...set

技術分享

技術分享

7.6.4 刪除:delete

技術分享

7.6.5 通過ID查詢

技術分享

四、JDBC連接池

1、案例分析

實際開發中“獲得連接”或“釋放資源”是非常消耗系統資源的兩個過程,為了解決此類性能問題,通常情況我們采用連接池技術,來共享連接Connection

2、連接池概述

2.1 概念

用池來管理Connection,這樣可以重復使用Connection。有了池,所以我們就不用自己來創建Connection,而是通過池來獲取Connection對象。當使用完Connection後,調用Connection的close()方法也不會真的關閉Connection,而是把Connection"歸還"給池。池就可以再利用這個Connection對象了。

技術分享

2.2 規範

java為數據庫連接池提供了公共的接口:javax.sql.DataSource,各個廠商需要讓自己的連接池實現這個接口。這樣應用程序才可以方便的切換不同廠商的連接池!

常見的連接池:DBCP、C3P0


3、自定義連接池

3.1 案例分析

我們編寫自定義連接池,需要完成一下步驟

3.1.1 創建連接池實現(數據源),並實現接口javax.sql.DataSource。因為我們只使用該接口中getConnection()方法,簡化本案例,我們將自己提供方法1,而沒有實現接口

3.1.2 提供一個集合,用於存放連接,因為移除/添加操作過多,所以選擇LinkedList

3.1.3 本案例在靜態代碼塊中,為連接池初始化3個連接

3.1.4 之後程序如果需要連接,調用實現類的getConnection(),本方法將從連接池(容器List)獲得連接。為了保證當前連接只能提供給一個線程使用,所以我們將連接先從連接池中移除

3.1.5 當用戶使用完連接,釋放資源時,不執行close()方法,而是將連接添加到連接池中


3.2 案例實現

技術分享

3.2.1 提供容器及初始化

技術分享

3.2.2 獲得連接

技術分享

技術分享

3.2.3 歸還連接

技術分享

3.2.4 測試使用

為了體現連接池優勢,我們將采用多線程並發訪問,使同一個連接在不同的時段,被不同的線程使用

技術分享

技術分享

3.3 自定義連接池:方法增強

3.3.1 需求

自定義連接池中存在嚴重問題,用戶調用getConnection()獲得連接後,必須使用release()方法進行連接的歸還,如果用戶調用conn.close()將連接真正的釋放,連接池中將出現無連接可用。

此時我們希望,即使用戶調用了close()方法,連接仍歸還給連接池。close()方法原有功能釋放資源,期望功能:將當前連接歸還連接池。說明close()方法沒有我們希望的功能,我們將對close()方法進行增強,從而實現將連接歸還給連接池的功能

3.3.2 方法增強總結

<1>繼承,子類繼承父類,將父類的方法進行腹復寫,從而進行增強

使用前提:必須有父類,且存在繼承關系

<2>裝飾者設計模式,此設計模式專門用於增強方法

使用前提:必須有接口

缺點:需要將接口的所有方法都實現

<3>動態代理:在運行時動態的創建代理類,完成增強操作。與裝飾者相似

使用前提:必須有接口

難點:需要反射技術

<4>字節碼增強,運行時創建目標類子類,從而進行增強

常見第三方框架:cglib、javassist等

3.3.3 裝飾者設計模式

設計模式:專門為解決一類問題,而編寫的固定格式的代碼。

裝飾者固定結構:接口A,已知實現類C,需要裝飾者創建代理類B

<1>創建類B,並實現接口A

<2>提供類B的構造方法,參數類型為A,用於接收A接口的其他實現類(C)

<3>給類B添加類型為A成員變量,用於存放A接口的其他實現類

<4>增強需要的方法

<5>實現不需要增強的方法,方法體重調用成員變量存放的其他實現類對應的方法

技術分享

3.3.4 實現

3.3.4.1 裝飾類

技術分享

技術分享

3.3.4.2 使用裝飾者(包裝類)

將由DriverManager創建的連接,使用裝飾類包裝一下,然後添加到連接池中,構造方法中將容器pool傳遞進去,方便連接的歸還

技術分享

3.3.4.3 使用連接

技術分享


4、C3P0連接池

C3P0是開源免費的連接池,目前使用它的開源項目有:Spring、Hibernate等。使用第三方工具需要導入jar包,C3P0使用時還需要添加配置文件c3p0-config.xml

4.1 導入jar包

我們使用的0.9.2版本,需要導入2個jar包

技術分享

4.2 配置文件

<1>配置文件名稱:c3p0-config.xml(固定)

<2>配置文件位置:src(類路徑)

<3>配置文件內容:命名配置

技術分享

配置文件內容:默認配置

技術分享

技術分享

4.3 常見配置項

技術分享

技術分享

4.4 編寫工具類

C3P0提供核心工具類:ComboPooledDataSource,如果要使用連接池,必須創建該類的實例對象。

技術分享

技術分享

技術分享

5、DBCP連接池

DBCP也是一個開源的連接池,是Apache Common成員之一,在企業開發中也比較常見,tomcat內置的連接池

5.1 導入jar包

技術分享


5.2 配置文件

<1>配置文件名稱:*.properties

<2>配置文件位置:任意,建議src(classpath/類路徑)

<3>配置文件內容:properties不能編寫中文,不支持在STS中修改,必須使用記事本修改內容,否則中文註釋就亂碼了

技術分享

5.3 常見配置項

技術分享

技術分享

5.4 編寫工具類

技術分享

技術分享

五、使用DBUtils增刪改查的操作

1、案例分析

如果只使用JDBC進行開發,我們會發現冗余代碼過多,為了簡化JDBC開發,我們將采用apache commons組件一個成員:DEUtils。

DBUtils就是JDBC的簡化開發工具包。需要使用技術:連接池(獲得連接),SQL語句都沒有少

2、案例相關知識

2.1、JavaBean組件

javaBean就是一個類,在開發中常用於封裝數據。具有如下特性

<1>需要實現接口:java.io.Serializable,通常偷懶省略了。

<2>提供私有字段:private類型 字段名;

<3>提供getter/setter方法

<4>提供無參構造

技術分享

技術分享

3、DBUtils完成CRUD

3.1 概述

DBUtils是java編程中的數據庫操作實用工具,小巧簡單實用

DBUtils封裝了對JDBC的操作,簡化了JDBC操作,可以少寫代碼

DBUtils三個核心功能介紹

<1>QueryRunner中提供對sql語句操作的API

<2>ResultSetHandler接口,用於定義select操作後,怎樣封裝結果集

<3>DBUtils類,它就是一工具類,定義了關閉資源與事務處理的方法

3.2 QueryRunner核心類

<1>QueryRunner(DataSource ds),提供數據源(連接池),DBUtils底層自動維護連接connection

<2>update(String sql,Object...params),執行更新數據

<3>query(String sql,ResultSetHandler<T>rsh,Object...params),執行查詢

3.3 ResultSetHandler結果集處理類

技術分享技術分享

3.4 DBUtils工具類

closeQuietly(Connection conn) 關閉連接,如果有異常,try後不拋

commitAndCloseQuietly(Connection conn) 提交並關閉連接

rollbackAndCloseQuietly(Connection conn) 回滾並關閉連接

3.5 實現

3.5.1 添加

技術分享

技術分享

3.5.2 更新

技術分享

3.5.3 刪除

技術分享技術分享

3.5.4 通過id查詢

技術分享

3.5.5 查詢所有

技術分享

技術分享

3.5.6 總記錄數

技術分享

本文出自 “鵬哥的博客” 博客,謝絕轉載!

第二天:JDBC和DBUtils