Java web基礎篇——JDBC

JDBC API 允許用戶訪問任何形式的表格數(shù)據(jù),尤其是存儲在關系數(shù)據(jù)庫中的數(shù)據(jù)。
執(zhí)行流程如下:

  • 連接數(shù)據(jù)源,如:數(shù)據(jù)庫。
  • 為數(shù)據(jù)庫傳遞查詢和更新指令。
  • 處理數(shù)據(jù)庫響應并返回的結果。
1.JDBC操作數(shù)據(jù)庫的步驟

加載驅動。

Class.forName("oracle.jdbc.driver.OracleDriver");

創(chuàng)建連接。

Connection con = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:orcl", "scott", "tiger");

創(chuàng)建語句。

PreparedStatement ps = con.prepareStatement("select * from emp where sal between ? and ?");
ps.setInt(1, 1000);
ps.setInt(2, 3000);

執(zhí)行語句。

 ResultSet rs = ps.executeQuery();

處理結果。

    while(rs.next()) {
        System.out.println(rs.getInt("empno") + " - " + rs.getString("ename"));
    }

關閉資源。

    finally {
        if(con != null) {
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

提示:關閉外部資源的順序應該和打開的順序相反,也就是說先關閉ResultSet、再關閉Statement、在關閉Connection。上面的代碼只關閉了Connection(連接),雖然通常情況下在關閉連接時,連接上創(chuàng)建的語句和打開的游標也會關閉,但不能保證總是如此,因此應該按照剛才說的順序分別關閉。此外,第一步加載驅動在JDBC 4.0中是可以省略的(自動從類路徑中加載驅動),但是我們建議保留

一次完整增刪改查的java代碼案例:

public class DbUtil {
    public static final String URL = "jdbc:mysql://localhost:3306/imooc";
    public static final String USER = "liulx";
    public static final String PASSWORD = "123456";
    private static Connection conn = null;
    static{
        try {
            //1.加載驅動程序
            Class.forName("com.mysql.jdbc.Driver");
            //2. 獲得數(shù)據(jù)庫連接
            conn = DriverManager.getConnection(URL, USER, PASSWORD);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection(){
        return conn;
    }
}

//模型
package liulx.model;

import java.util.Date;

public class Goddess {

    private Integer id;
    private String user_name;
    private Integer sex;
    private Integer age;
    private Date birthday; //注意用的是java.util.Date
    private String email;
    private String mobile;
    private String create_user;
    private String update_user;
    private Date create_date;
    private Date update_date;
    private Integer isDel;
    //getter setter方法。。。
}

//---------dao層--------------
package liulx.dao;

import liulx.db.DbUtil;
import liulx.model.Goddess;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

public class GoddessDao {
    //增加
    public void addGoddess(Goddess g) throws SQLException {
        //獲取連接
        Connection conn = DbUtil.getConnection();
        //sql
        String sql = "INSERT INTO imooc_goddess(user_name, sex, age, birthday, email, mobile,"+
            "create_user, create_date, update_user, update_date, isdel)"
                +"values("+"?,?,?,?,?,?,?,CURRENT_DATE(),?,CURRENT_DATE(),?)";
        //預編譯
        PreparedStatement ptmt = conn.prepareStatement(sql); //預編譯SQL,減少sql執(zhí)行

        //傳參
        ptmt.setString(1, g.getUser_name());
        ptmt.setInt(2, g.getSex());
        ptmt.setInt(3, g.getAge());
        ptmt.setDate(4, new Date(g.getBirthday().getTime()));
        ptmt.setString(5, g.getEmail());
        ptmt.setString(6, g.getMobile());
        ptmt.setString(7, g.getCreate_user());
        ptmt.setString(8, g.getUpdate_user());
        ptmt.setInt(9, g.getIsDel());

        //執(zhí)行
        ptmt.execute();
    }

    public void updateGoddess(){
        //獲取連接
        Connection conn = DbUtil.getConnection();
        //sql, 每行加空格
        String sql = "UPDATE imooc_goddess" +
                " set user_name=?, sex=?, age=?, birthday=?, email=?, mobile=?,"+
                " update_user=?, update_date=CURRENT_DATE(), isdel=? "+
                " where id=?";
        //預編譯
        PreparedStatement ptmt = conn.prepareStatement(sql); //預編譯SQL,減少sql執(zhí)行

        //傳參
        ptmt.setString(1, g.getUser_name());
        ptmt.setInt(2, g.getSex());
        ptmt.setInt(3, g.getAge());
        ptmt.setDate(4, new Date(g.getBirthday().getTime()));
        ptmt.setString(5, g.getEmail());
        ptmt.setString(6, g.getMobile());
        ptmt.setString(7, g.getUpdate_user());
        ptmt.setInt(8, g.getIsDel());
        ptmt.setInt(9, g.getId());

        //執(zhí)行
        ptmt.execute();
    }

    public void delGoddess(){
        //獲取連接
        Connection conn = DbUtil.getConnection();
        //sql, 每行加空格
        String sql = "delete from imooc_goddess where id=?";
        //預編譯SQL,減少sql執(zhí)行
        PreparedStatement ptmt = conn.prepareStatement(sql);

        //傳參
        ptmt.setInt(1, id);

        //執(zhí)行
        ptmt.execute();
    }

    public List<Goddess> query() throws SQLException {
        Connection conn = DbUtil.getConnection();
        Statement stmt = conn.createStatement();
        ResultSet rs = stmt.executeQuery("SELECT user_name, age FROM imooc_goddess");

        List<Goddess> gs = new ArrayList<Goddess>();
        Goddess g = null;
        while(rs.next()){
            g = new Goddess();
            g.setUser_name(rs.getString("user_name"));
            g.setAge(rs.getInt("age"));

            gs.add(g);
        }
        return gs;
    }

    public Goddess get(){
        Goddess g = null;
        //獲取連接
        Connection conn = DbUtil.getConnection();
        //sql, 每行加空格
        String sql = "select * from  imooc_goddess where id=?";
        //預編譯SQL,減少sql執(zhí)行
        PreparedStatement ptmt = conn.prepareStatement(sql);
        //傳參
        ptmt.setInt(1, id);
        //執(zhí)行
        ResultSet rs = ptmt.executeQuery();
        while(rs.next()){
            g = new Goddess();
            g.setId(rs.getInt("id"));
            g.setUser_name(rs.getString("user_name"));
            g.setAge(rs.getInt("age"));
            g.setSex(rs.getInt("sex"));
            g.setBirthday(rs.getDate("birthday"));
            g.setEmail(rs.getString("email"));
            g.setMobile(rs.getString("mobile"));
            g.setCreate_date(rs.getDate("create_date"));
            g.setCreate_user(rs.getString("create_user"));
            g.setUpdate_date(rs.getDate("update_date"));
            g.setUpdate_user(rs.getString("update_user"));
            g.setIsDel(rs.getInt("isdel"));
        }
        return g;
    }
}
2.Statement和PreparedStatement有什么區(qū)別?哪個性能更好?

與Statement相比,

  • PreparedStatement接口代表預編譯的語句,它主要的優(yōu)勢在于可以減少SQL的編譯錯誤并增加SQL的安全性(減少SQL注射攻擊的可能性);
  • PreparedStatement中的SQL語句是可以帶參數(shù)的,避免了用字符串連接拼接SQL語句的麻煩和不安全;
  • 當批量處理SQL或頻繁執(zhí)行相同的查詢時,PreparedStatement有明顯的性能上的優(yōu)勢,由于數(shù)據(jù)庫可以將編譯優(yōu)化后的SQL語句緩存起來,下次執(zhí)行相同結構的語句時就會很快(不用再次編譯和生成執(zhí)行計劃)。

補充:為了提供對存儲過程的調(diào)用,JDBC API中還提供了CallableStatement接口。存儲過程(Stored Procedure)是數(shù)據(jù)庫中一組為了完成特定功能的SQL語句的集合,經(jīng)編譯后存儲在數(shù)據(jù)庫中,用戶通過指定存儲過程的名字并給出參數(shù)(如果該存儲過程帶有參數(shù))來執(zhí)行它。雖然調(diào)用存儲過程會在網(wǎng)絡開銷、安全性、性能上獲得很多好處,但是存在如果底層數(shù)據(jù)庫發(fā)生遷移時就會有很多麻煩,因為每種數(shù)據(jù)庫的存儲過程在書寫上存在不少的差別。

3.使用JDBC操作數(shù)據(jù)庫時,如何提升讀取數(shù)據(jù)的性能?如何提升更新數(shù)據(jù)的性能?

要提升讀取數(shù)據(jù)的性能,可以指定通過結果集(ResultSet)對象的setFetchSize()方法指定每次抓取的記錄數(shù)(典型的空間換時間策略);
要提升更新數(shù)據(jù)的性能可以使用PreparedStatement語句構建批處理,將若干SQL語句置于一個批處理中執(zhí)行。

4.在進行數(shù)據(jù)庫編程時,連接池有什么作用?

由于創(chuàng)建連接和釋放連接都有很大的開銷(尤其是數(shù)據(jù)庫服務器不在本地時,每次建立連接都需要進行TCP的三次握手,釋放連接需要進行TCP四次握手,造成的開銷是不可忽視的),為了提升系統(tǒng)訪問數(shù)據(jù)庫的性能,可以事先創(chuàng)建若干連接置于連接池中,需要時直接從連接池獲取,使用結束時歸還連接池而不必關閉連接,從而避免頻繁創(chuàng)建和釋放連接所造成的開銷,這是典型的用空間換取時間的策略(浪費了空間存儲連接,但節(jié)省了創(chuàng)建和釋放連接的時間)。池化技術在Java開發(fā)中是很常見的,在使用線程時創(chuàng)建線程池的道理與此相同?;贘ava的開源數(shù)據(jù)庫連接池主要有:C3P0、ProxoolDBCP、BoneCPDruid等。

補充:在計算機系統(tǒng)中時間和空間是不可調(diào)和的矛盾,理解這一點對設計滿足性能要求的算法是至關重要的。大型網(wǎng)站性能優(yōu)化的一個關鍵就是使用緩存,而緩存跟上面講的連接池道理非常類似,也是使用空間換時間的策略。可以將熱點數(shù)據(jù)置于緩存中,當用戶查詢這些數(shù)據(jù)時可以直接從緩存中得到,這無論如何也快過去數(shù)據(jù)庫中查詢。當然,緩存的置換策略等也會對系統(tǒng)性能產(chǎn)生重要影響,對于這個問題的討論已經(jīng)超出了這里要闡述的范圍。

5.什么是DAO模式?

DAO(Data Access Object)顧名思義是一個為數(shù)據(jù)庫或其他持久化機制提供了抽象接口的對象,在不暴露底層持久化方案實現(xiàn)細節(jié)的前提下提供了各種數(shù)據(jù)訪問操作。在實際的開發(fā)中,應該將所有對數(shù)據(jù)源的訪問操作進行抽象化后封裝在一個公共API中。用程序設計語言來說,就是建立一個接口,接口中定義了此應用程序中將會用到的所有事務方法。在這個應用程序中,當需要和數(shù)據(jù)源進行交互的時候則使用這個接口,并且編寫一個單獨的類來實現(xiàn)這個接口,在邏輯上該類對應一個特定的數(shù)據(jù)存儲。DAO模式實際上包含了兩個模式,一是Data Accessor(數(shù)據(jù)訪問器),二是Data Object(數(shù)據(jù)對象),前者要解決如何訪問數(shù)據(jù)的問題,而后者要解決的是如何用對象封裝數(shù)據(jù)。

6.事務的ACID是指什么?
  • 原子性(Atomic):事務中各項操作,要么全做要么全不做,任何一項操作的失敗都會導致整個事務的失??;
  • 一致性(Consistent):事務結束后系統(tǒng)狀態(tài)是一致的;
  • 隔離性(Isolated):并發(fā)執(zhí)行的事務彼此無法看到對方的中間狀態(tài);
  • 持久性(Durable):事務完成后所做的改動都會被持久化,即使發(fā)生災難性的失敗。通過日志和同步備份可以在故障發(fā)生后重建數(shù)據(jù)。

補充:關于事務,在面試中被問到的概率是很高的,可以問的問題也是很多的。首先需要知道的是,只有存在并發(fā)數(shù)據(jù)訪問時才需要事務。當多個事務訪問同一數(shù)據(jù)時,可能會存在5類問題,包括3類數(shù)據(jù)讀取問題(臟讀、不可重復讀和幻讀)和2類數(shù)據(jù)更新問題(第1類丟失更新和第2類丟失更新)。

數(shù)據(jù)并發(fā)訪問所產(chǎn)生的問題,在有些場景下可能是允許的,但是有些場景下可能就是致命的,數(shù)據(jù)庫通常會通過鎖機制來解決數(shù)據(jù)并發(fā)訪問問題,按鎖定對象不同可以分為表級鎖和行級鎖;按并發(fā)事務鎖定關系可以分為共享鎖和獨占鎖,具體的內(nèi)容大家可以自行查閱資料進行了解。
直接使用鎖是非常麻煩的,為此數(shù)據(jù)庫為用戶提供了自動鎖機制,只要用戶指定會話的事務隔離級別,數(shù)據(jù)庫就會通過分析SQL語句然后為事務訪問的資源加上合適的鎖,此外,數(shù)據(jù)庫還會維護這些鎖通過各種手段提高系統(tǒng)的性能,這些對用戶來說都是透明的(就是說你不用理解,事實上我確實也不知道)。ANSI/ISO SQL 92標準定義了4個等級的事務隔離級別,如下表所示:


事務

需要說明的是,事務隔離級別和數(shù)據(jù)訪問的并發(fā)性是對立的,事務隔離級別越高并發(fā)性就越差。所以要根據(jù)具體的應用來確定合適的事務隔離級別,這個地方?jīng)]有萬能的原則。

7.JDBC中如何進行事務處理?

答:Connection提供了事務處理的方法,通過調(diào)用setAutoCommit(false)可以設置手動提交事務;當事務完成后用commit()顯式提交事務;如果在事務處理過程中發(fā)生異常則通過rollback()進行事務回滾。除此之外,從JDBC 3.0中還引入了Savepoint(保存點)的概念,允許通過代碼設置保存點并讓事務回滾到指定的保存點。

jdbc的事務處理

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • Spark SQL, DataFrames and Datasets Guide Overview SQL Dat...
    Joyyx閱讀 8,487評論 0 16
  • JDBC概述 在Java中,數(shù)據(jù)庫存取技術可分為如下幾類:JDBC直接訪問數(shù)據(jù)庫、JDO技術、第三方O/R工具,如...
    usopp閱讀 3,640評論 3 75
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內(nèi)部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,727評論 18 399
  • 我會這樣說:第二階:以我為尊:我看見自己,我看見他人;第三階:規(guī)范主導:我看見自己,我看見他人;我看見他人如何看我...
    Joycty閱讀 137評論 0 0
  • 武英殿 10月19日上午,常天書院釋玄老師帶隊學員前往故宮武英殿欣賞趙孟頫書畫展活動圓滿結束。 本是10點集合開始...
    沒有盡頭_0c2e閱讀 280評論 0 0

友情鏈接更多精彩內(nèi)容