一步一步學會JDBC

一、JDBC基本知識

是什麼

  • 英文全稱:Java DataBase Connectivity(java語言連接數據庫)
  • 是sun公司制定一套規範,一套接口

爲什麼要有JDBC(接口)

  • 因爲每一個數據庫的底層實現原理不一樣,恰恰符合面向接口編程,下面解釋

JDBC接口的調用者與實現者

  • 調用者:java程序員,調用這個接口就可以連接數據庫並進行操作,而不管連接的是哪一個數據庫
  • 實現者:各大數據庫廠家,如:MySQL,SQL Sever,Oracle等等,該公司的程序員編寫程序實現JDBC接口

一圖搞懂JDBC接口
在這裏插入圖片描述
驅動:各大數據庫廠家實現JDBC接口的實現類也叫驅動,也就是jar包,需要java程序員到官網上下載

二、java中與JDBC相關的接口

相關的接口與類位於java.sql包下,

接口 解釋
DriverMannger類 管理一組JDBC驅動程序,用於註冊驅動
Connection java程序與特定的數據庫連接
Statement 用於數據庫操作
PreparedStatement 用於數據庫操作,Statement的子接口,下文寫區別
ResultSet 數據庫查詢返回的結果集

java與數據庫的連接與操作靠這幾個接口(類)就可以實現,並且調用前一個接口的方法可以生成,下一個類的對象,是層層遞進的關係

三、JDBC編程的步驟

1.步驟總覽

在這裏插入圖片描述

2.重點說下前三步

後三步的代碼背後沒有多少內容,所以先暫時放放

2.1註冊驅動
  • 告訴java程序,即將要連接的是哪個品牌的數據庫

導入MySQL的jar包(驅動)
右擊項目---->Open Module Settings
在這裏插入圖片描述
找到jar包點擊ok即可

代碼註冊驅動

/**
 * @author Think-Coder
 * @data 2020/5/5 15:11
 */
import java.sql.*;
public class JDBCTest03
{
	public static void main(String[] args){
		try{
			//1.註冊驅動常用方式
			//爲什麼常用:因爲參數是一個字符串,字符串可以寫道xxx.properties文件中
			Class.forName("com.mysql.jdbc.Driver");
		}catch(Exception e){
			e.printStackTrace();
		}
	}
}

com.mysql.jdbc.Driver類源碼

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {}
    
    static {
        try {
        	//這個方法是註冊MySQL驅動的
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

整個註冊驅動方法驅動在static靜態代碼塊中,當執行Class.forName(“com.mysql.jdbc.Driver”);時會加載com.mysql.jdbc.Driver類,此時static靜態代碼塊執行,便會註冊驅動了

2.2.獲取Connection連接:DriverManager.getConnection方法
  • 表示JVM的進程和數據庫進程之間的通道打開了
  • 進程通信,重量級的,使用完一定要關閉

方法參數需要提供

	url(統一資源定位符)
			如:jdbc:mysql://127.0.0.1:3306/selfproj
			jdbc:mysql://  協議
			127.0.0.1      IP地址
			3306           端口號
			selfproj       數據庫名
	賬號:root
	密碼:123456

代碼如下:

conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/selfproj?characterEncoding=utf8","root","123456");

getConnection方法中是三個字符串參數,可以放在外部配置文件中
當更改數據庫時,只要更改外部配置文件就搞定,不需要重新啓動服務,這個很重要
咱們記住一條規律
只要參數是字符串,用戶需要動態變化的,都可以寫在外部配置文件中

2.3.獲取數據庫操作對象:通過Connection接口中的方法創建對象

創建Statement對象,通過Connection接口的createStatement()方法

	try {
	    //1.註冊驅動
	    Class.forName("com.mysql.jdbc.Driver");
	    //2.獲取鏈接
	    conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/selfproj?useUnicode=true&characterEncoding=utf8","root","123456");
	   
	    //3.獲取數據庫操作對象
	    stmt = conn.createStatement();
	    //4.執行sql
	    //存在sql注入問題
	    //假如此時userName="張三",userPwd="張三'or'1'='1"
	    //通過字符串拼接將sql關鍵字or拼接進去
	    //String sql="select * from user where name='張三' and pwd ='張三'or'1'='1'"
	    //不需要正確的用戶名及密碼就能此時就能進入系統
	    String sql = "select * from user where name='"+userName+"'and pwd ='"+userPwd+"'";
	    
	    //以上正好完成了sql語句的拼接
	    //以下代碼的含義是,發送sql語句給DBMS,DBMS進行sql編譯
	    rs = stmt.executeQuery(sql);
	    if(rs.next()){
	        loginSucess = true;
	    }
	}catch (Exception e) {
	    e.printStackTrace();
	}

因爲存在SQL注入問題,平時開發基本不用Statement對象,所以沒有貼完整得代碼

3.JDBC完整的6步

接下來會寫一個用戶登錄完整的代碼
將JDBC的6步完全融合進去,使用了PreparedStatement對象,操作數據庫,是開發中最常用的

public class JDBCTest08 {
    public static void main(String[] args)  {
        //初始化一個界面
        Map<String,String> userLoginInfo = initUI();
        //驗證用戶名和密碼
        boolean loginSucess = false;
        try {
            loginSucess = login(userLoginInfo);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(loginSucess?"登錄成功":"登錄失敗");
    }
    
    //用戶登錄的方法
    public static boolean login(Map<String,String> userLoginInfo){
        //打標記
        boolean loginSucess = false;
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        //單獨定義變量
        String userName = userLoginInfo.get("loginName");
        String userPwd = userLoginInfo.get("loginPwd");

        try {
            //1.註冊驅動
            Class.forName("com.mysql.jdbc.Driver");
            //2.獲取鏈接
            conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/selfproj?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT", "root", "123456");
            
            //3.獲取數據庫預編譯PreparedStatement對象
            //sql語句框子,一個?代表一個佔位符
            
            String sql = "select * from test where id=? and pwd=?";
            //程序執行到此,會發送sql語句框子給DBMS,然後DBMS進行sql語句的預先編譯
            //此時sql語句中沒有用戶輸入的值,很好的解決了sql注入問題
            ps = conn.prepareStatement(sql);
            //給?佔位符傳值
            ps.setString(1, userName);
            ps.setString(2, userPwd);

            //4.執行sql
            rs = ps.executeQuery();
            
            //5.處理返回的結果集
            // rs爲返回的結果集
            if (rs.next()) {
                loginSucess = true;
            }
        }catch (Exception e){
            e.printStackTrace();
        } finally {
            //6.釋放資源
            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 loginSucess;
    }
    //用戶輸入的方法
    private static Map<String, String> initUI() {
        Scanner s = new Scanner(System.in);

        System.out.println("用戶名:");
        String loginName = s.nextLine();

        System.out.println("密碼:");
        String loginPwd = s.nextLine();

        Map<String,String> userLoginInfo = new HashMap<>();
        userLoginInfo.put("loginName",loginName);
        userLoginInfo.put("loginPwd",loginPwd);

        return userLoginInfo;
    }
}

解釋說明

  • 完整的JDBC編程6步就寫完了,JDBC編程6步是針對於查詢的
  • 對於增刪改的,是不需要第5步的,因爲沒有返回查詢結果集
  • 使用增刪改時,只需將第4步ps.executeQuery()變爲ps.executeUpdate()就可以了,代碼我就不寫了,哈哈

下面對比Statement對象和PreparedStatement對象

  • 1.Statement存在sql注入問題,PreparedStatement解決了sql注入問題。
  • 2.PreparedStatement執行效率高,編譯一次執行多次,因爲相同的sql語句不會再編譯
    • 而Statement編譯一次執行一次,因爲基本上sql語句不會相同
  • 3.PreparedStatement會在編譯階段做類型檢查

總結:開發場景中99%使用PreparedStatement對象,在需要SQL注入的業務場景中使用Statement

四.封裝工具類

註冊驅動、連接數據庫和釋放連接經常使用可以封裝到工具類中

public class DBUtil {
    /*
    * 工具類中的構造方法都是私有的
    * 因爲工具類當中的方法是靜態的,不需要new對象,直接採用類名調用
    * */
    private DBUtil(){}

    //靜態代碼塊在類加載時執行,並且只執行一次
    static {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /*
    * 獲取數據庫連接對象
    * */
    public static Connection getConnection()throws Exception{
        return DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/selfproj?useUnicode=true&characterEncoding=utf8","root","123456");
    }
    /*
    * 關閉資源
    * */
    public static void close(Connection conn, Statement ps, ResultSet rs){
        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();
            }
        }
    }
}

使用工具類,實現模糊查詢

   public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        try {
            //使用工具類獲取連接
            conn=DBUtil.getConnection();
            
            //獲取預編譯的數據庫操作對象
            String sql = "select * from user where pwd like ?";
            ps = conn.prepareStatement(sql);
            
            //實現查詢密碼第二位是2的
            ps.setString(1,"_2%");
            rs=ps.executeQuery();
            while(rs.next()){
                System.out.println(rs.getString("pwd"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            //使用工具類釋放資源
            DBUtil.close(conn,ps,rs);
        }
    }

五、總結

JDBC是一套讓各個數據庫廠家實現的接口,通過調用這些接口,實現java程序連接數據庫,很好體現面向接口編程的特點

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章