Mybatis源碼解析1—— JDBC


  在之前的文章中,我為大家介紹了 Mybatis 的詳細用法,算是基礎教程。 

  詳細鏈接:Mybatis 基礎教程

  言歸正傳,只懂基礎可不行,接下來將給大家帶來高階的源碼解析教程,從淺入深,通過源碼解析,圖例結合,抽絲剝繭,讓大家看的不吃力,並且能夠深刻理解 Mybatis 這個框架的底層實現原理,讓大家學到的不僅僅是這個框架用法,而是通過這個框架理解其設計思想。

  

1、JDBC

  我相信所有開發者第一次與數據庫打交道時,就是通過 JDBC 來實現的,第一次通過程序獲取到數據庫中的數據時,那種高興,那種吃驚,反正我依稀歷歷在目。            

  什么是 JDBC:JDBC 是Java與數據庫交互的API,用來規范客戶端程序如何來訪問數據庫的應用程序接口。

  通常分為兩組API:

  ①、面向Java應用程序開發人員的API,是一個標准的API,且不同數據庫具有不同的實現。

  ②、面向數據庫驅動開發人員,是前者的底層支持。

  因為大家都是Java開發人員,這里我們就介紹第一組API。

  JDBC API主要位於JDK中的java.sql包中(之后擴展的內容位於javax.sql包中)

  下面介紹幾個關鍵的接口,注意這都是JDK內部提供的一些接口,具體實現得看具體的數據庫驅動,比如MySQL,我們通常會在maven或gradle配置mysql-connectior-java ,下面接口的具體實現就在其中。

  ①、Driver:驅動程序,會將自身加載到DriverManager中去.

  ②、DriverManager:負責加載各種不同驅動程序(Driver),並根據不同的請求,向調用者返回相應的數據庫連接(Connection)。

  ③、Connection:數據庫連接,負責與進行數據庫間通訊,SQL執行以及事務處理都是在某個特定Connection環境中進行的。可以產生用以執行SQL的Statement。

  ④、Statement:用來執行SQL查詢和更新(針對靜態SQL語句和單次執行)。

  ⑤、PreparedStatement:用來執行包含動態參數的SQL查詢和更新(預編譯,在服務器端編譯,允許重復執行以提高效率)。

  ⑥、CallableStatement:用來調用數據庫中的存儲過程。

  ⑦、ResultSet:用來存儲數據庫查詢操作返回的結果。

  ⑧、SQLException:表示在數據庫連接的建立、SQL語句的執行、關閉等過程中發生了異常。

2、完整的交互過程

  下面,我們通過JDBC來完成一次數據庫查詢操作,步驟如下:

  ①、加載數據庫驅動

  ②、獲取數據庫連接(通過數據庫URL,用戶名,密碼)

  ③、定義SQL語句

  ④、通過數據庫連接,創建 Statement 對象或者 PreparedStatement 對象

  ⑤、通過 Statement 對象執行 SQL 語句,得到結果集 ResultSet 對象

  ⑥、讀取 ResultSet 對象,轉換成我們要的 JavaBean

  ⑦、關閉數據庫連接、Statement、ResultSet等對象,釋放相關資源

代碼實現如下:

package com.itcoke.bean;

public class Person {

    private Long pid;

    private String pname;

    private Integer page;

    public Long getPid() {
        return pid;
    }

    public void setPid(Long pid) {
        this.pid = pid;
    }

    public String getPname() {
        return pname;
    }

    public void setPname(String pname) {
        this.pname = pname;
    }

    public Integer getPage() {
        return page;
    }

    public void setPage(Integer page) {
        this.page = page;
    }

    @Override
    public String toString() {
        return "Person{" +
                "pid=" + pid +
                ", pname='" + pname + '\'' +
                ", page=" + page +
                '}';
    }
}

 

package com.itcoke.jdbc;

import com.itcoke.bean.Person;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class JDBCUtils {
    //MySQL數據庫驅動,注意多了一個cj,com.mysql.jdbc.Driver和mysql-connector-java 5一起用
    public static String driverClass = "com.mysql.cj.jdbc.Driver";
    //MySQL用戶名
    public static String userName = "root";
    //MySQL密碼
    public static String passWord = "root1234";
    //MySQL URL
    public static String url = "jdbc:mysql://localhost:3306/mybatis-study";
    //定義數據庫連接
    public static Connection conn = null;
    //定義聲明數據庫語句,使用 預編譯聲明 PreparedStatement提高數據庫執行性能
    public static PreparedStatement ps = null;
    //定義返回結果集
    public static ResultSet rs = null;

    public static List<Person> selectPersonByName(String pname){
        List<Person> personList = new ArrayList<>();

        try {
            // 1、加載數據庫驅動
            Class.forName(driverClass);
            // 2、獲取數據庫連接
            conn = DriverManager.getConnection(url,userName,passWord);
            // 3、定義 sql 語句,?表示占位符
            String sql = "select * from person where pname=?";
            // 4、獲取預編譯處理的statement
            ps = conn.prepareStatement(sql);
            //設置sql語句中的參數,第一個為sql語句中的參數的?(從1開始),第二個為設置的參數值
            ps.setString(1, "itcoke");
            // 5、向數據庫發出 sql 語句查詢,並返回結果集
            rs = ps.executeQuery();
            while(rs.next()){
                // 6、讀取ResultSet,轉換成我們要的對象
                Person person = new Person();
                person.setPid(rs.getLong("pid"));
                person.setPname(rs.getString("pname"));
                personList.add(person);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            // 7、關閉數據庫連接
            if(rs!=null){
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(ps!=null){
                try {
                    ps.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(conn!=null){
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
        return personList;
    }

    public static void main(String[] args){
        List<Person> personList = JDBCUtils.selectPersonByName("itcoke");
        System.out.println(personList);
    }
}

  PS:數據庫名稱為 mybatis-study,表為 person

  

 

3、缺點分析

  ①、問題一:每執行一次增刪改查,就要建立連接,關閉連接,頻繁的獲取連接和關閉連接,會造成數據庫資源浪費,影響數據庫性能。

  設想解決:使用數據庫連接池管理數據庫連接

  ②、問題二:將 sql 語句硬編碼到程序中,如果sql語句修改了,那么需要重新編譯 Java 代碼,不利於系統維護

  設想解決:將 sql 語句可配置化,比如設置到 xml 文件中,這樣即使 sql 語句變化了,我們也不需要對 Java 代碼進行修改

  ③、問題三:在 PreparedStatement 中設置參數,對占位符設置值都是硬編碼在Java代碼中,不利於系統維護

  設想解決:將 sql 語句以及占位符和參數都配置到 xml 文件中

  ④、問題四:從 resultset 中遍歷結果集時,對表的字段存在硬編碼,不利於系統維護

  設想解決:將查詢的結果集自動映射為 Java 對象

  ⑤、問題五:重復性代碼特別多,包括建立建立,加載驅動等

  設想解決:抽取封裝公共代碼

  ⑥、問題六:沒有緩存,如果存在數據量大,且頻繁查詢的情況,這種方式性能特別低

  設想解決:集成緩存框架去操作數據庫

  ⑦、問題七:sql 的移植性不好,如果換個數據庫,那么sql 語句可能要重寫

  設想解決:在 JDBC 和 數據庫之間插入第三方框架,用第三方去生成 sql 語句,屏蔽數據庫的差異

4、ORM框架

  為了解決上面這些缺點,ORM(Object Relational Mapping,對象-關系映射)框架應運而生。

  ORM 模型就是數據庫的表和Java對象的映射關系模型,它主要解決數據庫數據和Java對象的相互映射,通過映射關系,我們可以簡單而迅速的把數據庫數據轉換成Java對象,從而讓開發人員無需對數據庫相關知識深入了解,便可以操作數據庫數據。

  當前市面上比較主流的ORM框架包括 Hibernate、Mybatis、Spring JDBC 等等,各有優缺點,這里可樂不做詳細介紹,本系列文章主要是介紹當前使用最廣泛的 ORM框架——Mybatis。

  其更輕量、更可控的特性,比較適合當前主流業務,特別是大數據量、高並發的場景。

5、小結

  本文我們介紹了JDBC直接操作數據庫的一些問題,引申出解決此類問題的ORM框架,其中 Mybatis 是其中的佼佼者,

  那么為什么 Mybatis 能夠解決 JDBC 直接操作數據庫的痛點?它又是如何實現的呢?

  不要走開,我們下篇文章更精彩!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM