JDBC

1、JDBC概述
一、什麼是JDBC、爲何要學習JDBC?java

JDBC(Java DataBase Connectivity)Java數據庫鏈接
其實就是利用Java語言/Java程序鏈接並訪問數據庫的一門技術

雖然以前咱們能夠經過 cmd或者navicat 鏈接數據庫,也能夠對數據庫、表、表記錄等進行操做。
可是,未來在開發中更多的是經過程序來鏈接數據庫,若是是Java語言,經過Java程序鏈接數據庫,就必需要學習JDBC這門技術。

二、如何經過Java程序鏈接mysql數據庫?(快速入門)
建立一個測試類: com.tedu.JdbcTest01mysql

//1.註冊數據庫驅動
//2.獲取數據庫鏈接
//3.獲取傳輸器
//4.發送SQL語句到服務器執行,並返回結果
//5.處理執行的結果
//6.釋放資源

三、JDBC API總結正則表達式

1)Class.forName("com.mysql.cj.jdbc.Driver");
將mysql驅動包中的"com.mysql.cj.jdbc.Driver"類加載到內存中,Driver類中的靜態代碼塊就會執行,而在Driver類的靜態代碼塊中有一行代碼是用於註冊驅動的,所以這行代碼能夠註冊驅動!
註冊驅動: 將mysql驅動交給JDBC程序管理,以便於使用其中的功能
在JDBC4.0之後的版本中,這一步能夠省略,但仍是建議加上

2)DriverManager.getConnection(url,user,password) 
    url:指定要鏈接的是哪個位置的哪個庫
        jdbc:mysql://localhost:3306/jt_db?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    若是鏈接的數據庫端口是3306,端口能夠省略不寫:
        jdbc:mysql://localhost/jt_db?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    若是是鏈接本機上的數據庫,主機名/IP地址能夠省略不寫:
        jdbc:mysql:///jt_db?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    getConnection方法返回一個Connection對象,用於表示Java程序和數據庫服務器之間的鏈接。
3)Statement stat = conn.createStatement()
    conn.createStatement() -- 用於獲取向數據庫發送SQL語句的傳輸器對象
    stat.executeQuery(sql) -- 用於執行查詢類型的SQL語句,返回的是一個ResultSet對象
    stat.executeUpdate(sql)  -- 用於執行新增、刪除、修改類型的SQL語句,返回一個int值,表示影響的記錄行數

4)rs.next() -- 用於將指向數據行的箭頭往下挪動一行,而且返回布爾值(true或false),
    true表示箭頭往下挪動一行後,指向的行有數據;false表示箭頭往下挪動一行後,指向的行沒有數據;

5)在ResultSet結果集對象上提供了獲取數據的方法,常見的有:
    rs.getInt( colName );
    rs.getInt( colCount );
    rs.getString( colName );
    rs.getString( colCount );
    rs.getDobule( colName );
    rs.getDobule( colCount );
    ...
    rs.getObject( colName );
    rs.getObject( colCount );

2、JDBC的增刪改查sql

一、新增:往account表中添加一條記錄:名稱爲'lucy',金額是3500
二、修改:修改account表中名稱爲'lucy'的金額,將金額改成2000
三、刪除:刪除account表中名稱爲'lucy'的記錄

3、junit單元測試框架數據庫

junit(單元測試框架): 能夠不用添加main函數,也不用建立類的實例就能夠執行一個方法
可以用單元測試執行的方法必須知足以下條件:
    1)方法必須是公共的(public)
    2)方法必須是非靜態的
    3)方法必須是無返回值的(void)
    4)方法必須是無參數的
若是執行的方法不知足以上任何一個條件,就會報以下錯誤:
java.lang.Exception: No tests found matching...

junit(單元測試框架)經常使用的三個註解: @Test、@Before、@After
@Test:使用了該註解的方法,每次選中方法名,右鍵-->Run as-->junit test均可以執行該方法
@Before:使用了該註解的方法,每次會在@Test標記的方法以前執行。也就是說,每次在執行@Test標記的方法以前都會執行@Before標記的方法
@After:使用了該註解的方法,每次會在@Test標記的方法以後執行。也就是說,每次在執行@Test標記的方法以後都會執行@After標記的方法

4、PreparedStatement對象安全

Statement 傳輸器
PreparedStatement 傳輸器對象

一、模擬用戶登陸案例服務器

PreparedStatement對象是Statement傳輸器對象的子對象
PreparedStatement對象比Statement對象更加安全,在某些方面執行的效率上也會更高一些!
下面以一個模擬登陸的案例來說解PreparedStatement對象
----------------------------------------------------------
請登陸: 
請輸入用戶名: 
tom'#'
請輸入密碼: 

select * from user where username='tom'#'' and password=''
恭喜您登陸成功!
----------------------------------------------------------
請登陸: 
請輸入用戶名: 
張飛' or '1=1
請輸入密碼: 

select * from user where username='張飛' or '1=1' and password=''
select * from user where username='tom' or '1=1' and password=''
恭喜您登陸成功!
----------------------------------------------------------

二、SQL注入攻擊框架

SQL注入攻擊產生的緣由:因爲SQL語句中的參數是拼接而來的,其中的參數值(username和password的值)是用戶提交過來的,若是用戶在提交參數時,在參數中摻雜一些SQL關鍵字或特殊字符(or、#、-- 、/* */等)就可能會致使SQL語句的語義被篡改,從而執行一些意外的操做(好比用戶名或密碼錯誤也能夠登陸系統或網站)
delete from user where id=1 or 1=1;

三、如何解決SQL注入攻擊問題函數

1)能夠對用戶提交過來的參數進行校驗(例如經過正則表達式對用戶名和密碼進行校驗),若是用戶名或密碼中有相似於 or、#、-- 等符號,就再也不登陸,直接提示用戶輸入不合法,請從新登陸!
2)或者使用JDBC中提供的PreparedStatement對象,能夠解決SQL注入攻擊問題!

PreparedStatement對象是如何解決SQL注入攻擊的?
1)PreparedStatement對象是先將SQL語句的骨架(不包含參數)發送給服務器編譯並肯定下來。
    String sql = "select * from user where username=? and password=?";
    PreparedStatement stat = conn.prepareStatement(sql);
2)再將SQL語句中的參數值傳遞給服務器
    //設置SQL語句中的參數值
    stat.setString( 1, user );
    stat.setString( 2, psw );
    //執行SQL語句,返回執行結果
    ResultSet rs = stat.executeQuery();
因爲前面SQL語句的骨架已經被肯定了,所以SQL參數中即便再包含SQL關鍵字或者特殊符號,也不會影響SQL語句的骨架或語義,只會被當前普通的文原本處理,所以能夠防止SQL注入!

5、數據庫鏈接池
一、什麼是鏈接池?單元測試

池:常量池、線程池、鏈接池等中的池都是一個容器。是指內存中的一片空間
鏈接池:就是將一批鏈接資源存入到一個容器中。目的是爲了實現鏈接的複用,減小鏈接建立和關閉的次數,以此來提升程序執行的效率!

二、爲何要使用鏈接池?

傳統方式中,每次須要鏈接都是直接建立一個鏈接(對象/資源),再基於這個建立的鏈接去訪問數據庫,最後用完鏈接還要關閉!
而每次【建立鏈接】和【關閉鏈接】相比使用鏈接是要消耗大量的時間和資源,致使程序的執行效率很是低下!

爲了提升程序執行的效率,咱們能夠在程序一啓動時,就建立一批鏈接放在一個鏈接池中,供整個程序共享。
當用戶須要鏈接時,不用再去建立鏈接,而是直接從鏈接池中獲取一個鏈接進行使用,在用完鏈接後,也不須要關閉,而是直接將鏈接還回到鏈接池中。這樣一來,用來用去都是鏈接池中的這一批鏈接,必然能夠實現鏈接的複用,減小鏈接建立和關閉的次數。提升程序執行的效率!

三、如何使用c3p0鏈接池?

dbcp/c3p0/druid/hikari
因爲全部的鏈接池技術都實現了SUN公司所提供的DataSource接口
因此鏈接池也叫做"數據源"

    
第01步:在程序中建立一個c3p0鏈接池對象(存放鏈接的容器)
    ComboPooledDataSource pool = new ComboPooledDataSource();
    
第02步:設置鏈接數據庫的基本信息(四個參數)
方式一:將鏈接數據庫的參數經過setXxx方法直接經過java代碼寫死在程序中
    pool.setDriverClass("com.mysql.cj.jdbc.Driver");
    pool.setJdbcUrl("jdbc:mysql:///jt_db?characterEncoding=utf-8&serverTimezone=Asia/Shanghai");
    pool.setUser("root");
    pool.setPassword("root");
    這種方式不推薦使用,由於這種方式將鏈接參數寫死在程序中了,未來一旦參數發生變化,就須要咱們去改程序,改完以後須要對項目從新編譯、打包、部署、運行等,會提升維護成本!
    
方式二:將鏈接數據庫的參數提取到 c3p0.properties(文件名是固定的) 文件中
    而且須要將這個文件放在源碼根目錄(src根目錄)下,文件內容以下:
    -----------------------------------------
    c3p0.driverClass=com.mysql.cj.jdbc.Driver
    c3p0.jdbcUrl=jdbc:mysql:///jt_db?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    c3p0.user=root
    c3p0.password=root
    -----------------------------------------
    再次強調:這個文件的位置和名字都是固定的,由於底層c3p0會到指定的位置找指定名稱的文件,若是沒有按照要求去存放文件或者沒有按照要求去指定文件名稱,都會致使c3p0找不到這個文件,也就沒法讀取其中的配置信息,必然會致使連不上數據庫!
方式三:將鏈接數據庫的參數提取到 c3p0-config.xml(文件名也是固定的) 文件中
    而且須要將這個文件放在源碼根目錄(src根目錄)下,文件內容以下:
    -----------------------------------------
    <?xml version="1.0" encoding="UTF-8"?>
    <c3p0-config>
        <default-config>
            <property name="driverClass">
                com.mysql.cj.jdbc.Driver
            </property>
            <property name="jdbcUrl">
                jdbc:mysql:///jt_db?characterEncoding=utf-8&amp;serverTimezone=Asia/Shanghai
            </property>
            <property name="user">root</property>
            <property name="password">root</property>
        </default-config>
    </c3p0-config>
    -----------------------------------------
    再次強調:這個文件的位置和名字都是固定的,由於c3p0底層會到指定的位置找指定名稱的文件,若是沒有按照要求去存放文件或者沒有按照要求去指定文件名稱,都會致使c3p0找不到這個文件,也就沒法讀取其中的配置信息,必然會致使連不上數據庫!
第04步:從鏈接池中獲取一個鏈接對象進行使用
    Connection conn = pool.getConnection();
    
第05步:將用完的鏈接對象還回到鏈接池中
    conn.close();
若是在程序中沒有使用任何鏈接池,須要鏈接就經過 DriverManager.getConnection獲取(建立)一個鏈接,用完以後,調用conn.close()就是將鏈接資源關閉。
若是使用了鏈接池,經過 鏈接池對象調用 getConnection方法獲取一個鏈接對象,此時獲取的鏈接對象已經被改造了。用完以後,再調用conn.close()方法是將鏈接還回到鏈接池中。也就是說,從鏈接池中獲取的鏈接對象上的close方法被改形成了還鏈接到鏈接池!

四、補充內容:xml文件和properties文件的區別

相同點:這兩種文件在企業開發中均可以做爲配置文件使用,並且用的特別多
不一樣點:1)xml文件缺點:配置信息多,編寫起來比較麻煩,若是須要經過java程序讀取,代碼也比較麻煩。
2)xml文件的優勢:能夠保存有結構的數據(例如在xml文件中保存中國全部的省份以及省份包含的市區)
3)properties文件缺點:配置信息結構是key/value,沒法保存有結構的數據
4)properties文件優勢:配置信息簡潔,若是須要經過java程序讀取,讀取起來也比較方便。

==================================================
1、什麼是事務?

事務:簡單的說,事務是將一堆的SQL語句綁定在一塊兒執行,結果是要麼全都執行成功,要麼全都執行失敗。並且是都成功了纔算成功,但凡是有一條執行失敗,就按全失敗來處理!

舉例: 張三(1000)給李四(1000)轉帳100元
-- 開啓事務(start transaction)
-- 給張三的帳戶金額減去100元
update 帳戶表 set money=money-100 where name='張三'; -- 1000
-- 給李四的帳戶金額加上100元
update 帳戶表 set money=money+100 where name='李四'; -- 1000
-- 提交事務(commit)/ 回滾事務(rollback)

舉例: 網上購物
-- 開啓事務
-- 往訂單表中插入一條訂單信息(用戶、訂單號、商品信息、商品數據量、單價、總金額等)
insert into 訂單表 value(....);
-- 減去商品庫存表中的庫存數量
update 商品庫存表 set count=count-2 where...
-- 提交事務/回滾事務

2、事務的四大特性(重要)

一、原子性:表示事務中的全部操做(SQL)是一個總體,不能被分割,要麼全都執行成功,要麼全都執行失敗!

二、一致性:在事務先後的業務數據之和是保持一致的。
    在轉帳操做以前,張三帳戶金額(1000)和李四帳戶金額(1000)之和爲2000元
    在轉帳操做以後,不管事務提交了仍是回滾了,張三和李四的帳戶金額之和仍是2000元。

三、隔離性:是指全部的事務都是隔離開來的,在一個事務中看不到另一個事務正在進行中的狀態!
    事務1: 查詢A(1000)和B(1000)的帳戶總金額 
    
    事務2: -- 開啓事務
    A帳戶減去100元 -- A:900
    B帳戶加上100元 -- B:1100
    -- 提交事務/回滾事務
    
四、持久性:在事務提交後,對數據的更新操做纔會持久的保存到數據庫中
    -- 開啓事務
    A帳戶(1000元)減去100元 -- A:900
    B帳戶(1000元)加上100元 -- B:1100
    -- 提交事務/回滾事務

3、MySQL中的事務

在mysql中默認一條SQL語句就是一個事務。
若是但願將多條SQL放在一個事務中執行,能夠手動開啓事務,並手動結束事務
開啓事務: start transaction / begin;
結束事務:提交(commit) 和 回滾(rollback)
例子:使用轉帳演示mysql中如何開啓事務以及如何結束事務
    -- 開啓事務
    start transaction;
    -- A帳戶減去100元
    update acc set money=money-100 where name='A';
    -- B帳戶加上100元
    update acc set money=money+100 where name='B';
    select * from acc;
    -- 回滾事務 | 提交事務
    rollback; | commit;
    select * from acc;
相關文章
相關標籤/搜索