1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        看完這篇文章,你也可以手寫MyBatis部分源碼(JDBC)

        共 29892字,需瀏覽 60分鐘

         ·

        2021-07-22 12:50

        一、持久化機制

            持久化(persistence): 把數(shù)據(jù)保存到可掉電式存儲設(shè)備中以供之后使用。大多數(shù)情況下,特別是企業(yè)級應(yīng)用,數(shù)據(jù)持久化意味著將內(nèi)存中的數(shù)據(jù)保存到硬盤上加以”固化”,而持久化的實現(xiàn)過程大多通過各種關(guān)系數(shù)據(jù)庫來完成。就是將內(nèi)存中的數(shù)據(jù)存儲在關(guān)系型數(shù)據(jù)庫中,當然也可以存儲在磁盤文件、XML數(shù)據(jù)文件中。而在 Java中,數(shù)據(jù)庫存取技術(shù)只能通過 JDBC 來訪問數(shù)據(jù)庫。

            JDBC 訪問數(shù)據(jù)庫的形式主要有兩種:

        1. 直接使用 JDBC 的 API 去訪問數(shù)據(jù)庫服務(wù)器 (MySQL/Oracle)。

        2. 間接地使用 JDBC 的 API 去訪問數(shù)據(jù)庫服務(wù)器,使用第三方O/R Mapping工具,如 Hibernate, MyBatis 等。(底層依然是 JDBC ,JDBC 是 Java 訪問數(shù)據(jù)庫的基石,其他技術(shù)都是對 JDBC 的封裝)

        二、JDBC概述

            JDBC (Java DataBase Connectivity)是一個獨立于特定數(shù)據(jù)庫管理系統(tǒng)、通用的SQL數(shù)據(jù)庫存取和操作的公共接口(一組API),定義了用來訪問數(shù)據(jù)庫的標準Java類庫,(java.sql,javax.sql)使用這些類庫可以以一種標準的方法、方便地訪問數(shù)據(jù)庫資源。    JDBC為訪問不同的數(shù)據(jù)庫提供了一種統(tǒng)一的途徑,為開發(fā)者屏蔽了一些細節(jié)問題。    JDBC 的目標是使 Java 程序員使用 JDBC 可以連接任何提供了 JDBC 實現(xiàn) (驅(qū)動程序) 的數(shù)據(jù)庫系統(tǒng),這 樣就使得程序員無需對特定的數(shù)據(jù)庫系統(tǒng)的特點有過多的了解,從而大大簡化和加快了開發(fā)過程。

            簡單來說,JDBC 本身是 Java 連接數(shù)據(jù)庫的一個標準,是進行數(shù)據(jù)庫連接的抽象層,由 Java編寫的一組類和接口組成,接口的實現(xiàn)由各個數(shù)據(jù)庫廠商來完成。

            JDBC編寫的步驟如下:

            ODBC(Open Database Connectivity,開放式數(shù)據(jù)庫連接),是微軟在Windows平臺下推出的。使用者在程序中只需要調(diào)用ODBC API,由 ODBC 驅(qū)動程序?qū)⒄{(diào)用轉(zhuǎn)換成為對特定的數(shù)據(jù)庫的調(diào)用請求。

        三、獲取數(shù)據(jù)庫連接

        3.1、加載注冊驅(qū)動

            java.sql.Driver 接口是所有 JDBC 驅(qū)動程序需要實現(xiàn)的接口。這個接口是提供給數(shù)據(jù)庫廠商使用的,不同數(shù)據(jù)庫廠商提供不同的實現(xiàn)。在程序中不需要直接去訪問實現(xiàn)了 Driver 接口的類,而是由驅(qū)動程序管理器類(java.sql.DriverManager)去調(diào)用這些Driver實現(xiàn)。

        • Oracle的驅(qū)動:oracle.jdbc.driver.OracleDriver

        • mySql的驅(qū)動:com.mysql.jdbc.Driver

            加載驅(qū)動:加載 JDBC 驅(qū)動需調(diào)用 Class 類的靜態(tài)方法 forName(),向其傳遞要加載的 JDBC 驅(qū)動的類名

        • Class.forName(“com.mysql.jdbc.Driver”);

        3.2、注冊驅(qū)動的原理

            注冊驅(qū)動:DriverManager 類是驅(qū)動程序管理器類,負責管理驅(qū)動程序

        • 使用DriverManager.registerDriver(com.mysql.jdbc.Driver)來注冊驅(qū)動

        • 通常不用顯式調(diào)用 DriverManager 類的 registerDriver() 方法來注冊驅(qū)動程序類的實例,因為 Driver 接口的驅(qū)動程序類包含了靜態(tài)代碼塊,在這個靜態(tài)代碼塊中,會調(diào)用 DriverManager.registerDriver() 方法來注冊自身的一個實例。下圖是MySQL的Driver實現(xiàn)類的源碼:

            他通常有以下兩個步驟:

        1. 把 com.mysql.jdbc.Driver 這一份字節(jié)碼加載進 JVM。
        2. 字節(jié)碼被加載進JVM,就會執(zhí)行其靜態(tài)代碼塊.而其底層的靜態(tài)代碼塊在完成注冊驅(qū)動工作,將驅(qū)動注
        冊到DriverManger 中。
        復(fù)制代碼

        3.3、獲取連接對象

        我們一般使用 DriverManager 的 getConnection 方法創(chuàng)建 Connection 對象

        Connection conn = DriverManager.getConnection(url,username,password);
        // url=jdbc:mysql://localhost:3306/jdbcdemo
        // 如果連接的是本機的 MySQL,并且端口是默認的 3306 ,則可以簡寫:
        // url=jdbc:mysql:///jdbcdemo
        // username:當前訪問數(shù)據(jù)庫的用戶名
        // password:當前訪問數(shù)據(jù)庫的密碼
        復(fù)制代碼

        3.3.1、URL詳解

            JDBC URL 用于標識一個被注冊的驅(qū)動程序,驅(qū)動程序管理器通過這個 URL 選擇正確的驅(qū)動程序,從而建立到數(shù)據(jù)庫的連接。

            JDBC URL的標準由三部分組成(協(xié)議:子協(xié)議:子名稱),各部分間用冒號分隔。

        1. 協(xié)議:JDBC URL中的協(xié)議總是jdbc(固定寫法)。

        2. 子協(xié)議:子協(xié)議用于標識一個數(shù)據(jù)庫驅(qū)動程序。

        3. 子名稱:一種標識數(shù)據(jù)庫的方法。子名稱可以依不同的子協(xié)議而變化,用子名稱的目的是為了定位數(shù)據(jù)庫提供足夠的信息。包含主機名(對應(yīng)服務(wù)端的ip地址),端口號,數(shù)據(jù)庫名。

        1. url的常見寫法:

          • jdbc:mysql://主機名稱:mysql服務(wù)端口號/數(shù)據(jù)庫名稱?參數(shù)=值&參數(shù)=值

          • jdbc:mysql://localhost:3306/atguigu

          • jdbc:mysql://localhost:3306/atguigu**?useUnicode=true&characterEncoding=utf8**(如果JDBC程序與服務(wù)器端的字符集不一致,會導(dǎo)致亂碼,那么可以通過參數(shù)指定服務(wù)器端的字符集)

          • jdbc:mysql://localhost:3306/atguigu?user=root&password=123456

        3.4、數(shù)據(jù)庫連接方法

        3.4.1、方式一

            我們可以通過直接獲取注冊驅(qū)動,將數(shù)據(jù)庫信息寫在代碼中,這個的問題也是很明顯的,那就是一個硬編碼問題,當我們需要修改數(shù)據(jù)庫的時候,需要直接修改Java源代碼。

        	@Test
        public void testConnection4() {
        try {
        //1.數(shù)據(jù)庫連接的4個基本要素:
        String url = "jdbc:mysql://localhost:3306/test";
        String user = "root";
        String password = "admin";
        String driverName = "com.mysql.jdbc.Driver";

        //2.加載驅(qū)動 (①實例化Driver ②注冊驅(qū)動)
        Class.forName(driverName);

        //3.獲取連接
        Connection conn = DriverManager.getConnection(url, user, password);
        System.out.println(conn);
        } catch (Exception e) {
        e.printStackTrace();
        }

        }
        復(fù)制代碼

        3.4.2、方式二

            我們?yōu)榱私鉀Q方式一硬編碼的問題,需要將驅(qū)動信息放入配置文件,然后通過讀取的方式來進行賦值。

        	@Test
        public void testConnection5() throws Exception {
        //1.加載配置文件
        InputStream is = ConnectionTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
        Properties pros = new Properties();
        pros.load(is);

        //2.讀取配置信息
        String user = pros.getProperty("user");
        String password = pros.getProperty("password");
        String url = pros.getProperty("url");
        String driverClass = pros.getProperty("driverClass");

        //3.加載驅(qū)動
        Class.forName(driverClass);

        //4.獲取連接
        Connection conn = DriverManager.getConnection(url,user,password);
        System.out.println(conn);

        }
        復(fù)制代碼

        使用配置文件的好處:

        1. 實現(xiàn)了代碼和數(shù)據(jù)的分離,如果需要修改配置信息,直接在配置文件中修改,不需要深入代碼。

        2. 如果修改了配置信息,省去重新編譯的過程。

        四、DAO思想

        4.1、沒有DAO

            在沒有 DAO 的時候,我們的代碼存在大量的重復(fù)。

        4.2、DAO介紹

            DAO(Data Access Object) 數(shù)據(jù)訪問對象是一個面向?qū)ο蟮臄?shù)據(jù)庫接口. 顧名思義就是與數(shù)據(jù)庫打交道,夾在業(yè)務(wù)邏輯與數(shù)據(jù)庫資源中間,將所有對數(shù)據(jù)源的訪問操作抽象封裝在一個公共 API 中。程序書寫就是建立一個接口,接口中定義了此應(yīng)用程序中將會用到的所有事務(wù)方法。DAO 中的主要操作: 增刪改查(CRUD)。

            通過以上圖,DAO 作為組件,那其主要的是方法的設(shè)計,方法設(shè)計需要注意什么呢?

        1. 在保存功能中,調(diào)用者需要傳遞多個參數(shù)進來,然后把這些數(shù)據(jù)保存到數(shù)據(jù)庫中。

        2. 在查詢功能中,結(jié)果集的每行數(shù)據(jù)有多個列的值,然后把這些數(shù)據(jù)返回給調(diào)用者。

        3. 在開發(fā)過程中,如果遇到需要傳遞的數(shù)據(jù)有多個的時候,通常需要使用 JavaBean 對其進行封裝

        4.3、DAO規(guī)范

            DAO本質(zhì)就是一個可以重復(fù)使用的組件。他包括了兩個規(guī)范:

        1. 分包規(guī)范(域名倒寫.項目模塊名.組件)

           cn.util;    //存放工具類
        cn.domain; //裝pss模塊的domain類,模型對象.(Student)
        cn.dao; //裝pss模塊的dao接口.調(diào)用者將需要保存的數(shù)據(jù)封裝到一個對象中,然后傳遞進來
        cn.dao.impl; //裝dao接口的實現(xiàn)類.
        cn.test; //暫時存儲DAO的測試類,以后的測試類不應(yīng)該放這里.
        復(fù)制代碼
        1. 命名規(guī)范

          • DAO 接口 : 表示對某個模型的 CRUD 操作做規(guī)范,以 I 開頭,interface。例如:IStudentDAO

          • DAO 實現(xiàn)類: 表示對某個 DAO 接口的實現(xiàn),例如:EmployeeDAOImpl

          • DAO 測試類: 測試 DAO 組件中的所有方法,例如:EmployeeDAOTest

            調(diào)用建議:面向接口編程

        • 傳統(tǒng)的做法 : EmployeeDAOImpl dao = new EmployeeDAOImpl();

        • 面向接口編程 : IEmployeeDAO dao = new EmployeeDAOImpl();

        五、JDBC之CRUD操作

        5.1、Statement對象及其弊端

        5.1.1、Statement對象

            Statement對象時用于執(zhí)行靜態(tài) SQL 語句并返回它所生成結(jié)果的對象。

        • 通過調(diào)用 Connection 對象的 createStatement() 方法創(chuàng)建該對象。該對象用于執(zhí)行靜態(tài)的 SQL 語句,并且返回執(zhí)行結(jié)果。

        • Statement 接口中定義了下列方法用于執(zhí)行 SQL 語句:

        int excuteUpdate(String sql):執(zhí)行更新操作INSERT、UPDATE、DELETE
        ResultSet executeQuery(String sql):執(zhí)行查詢操作SELECT
        復(fù)制代碼

        5.1.2、使用Statement對象的弊端

            用Statement操作數(shù)據(jù)表存在弊端:

        1. 存在拼串操作,繁瑣

        2. 存在SQL注入問題

            SQL 注入是利用某些系統(tǒng)沒有對用戶輸入的數(shù)據(jù)進行充分的檢查,而在用戶輸入數(shù)據(jù)中注入非法的 SQL 語句段或命令(如:SELECT user, password FROM user_table WHERE user='a' OR 1 = ' AND password = ' OR '1' = '1') ,從而利用系統(tǒng)的 SQL 引擎完成惡意行為的做法。

        public class StatementTest {

        // 使用Statement的弊端:需要拼寫sql語句,并且存在SQL注入的問題
        @Test
        public void testLogin() {
        Scanner scan = new Scanner(System.in);

        System.out.print("用戶名:");
        String userName = scan.nextLine();
        System.out.print("密 碼:");
        String password = scan.nextLine();

        // SELECT user,password FROM user_table WHERE USER = '1' or ' AND PASSWORD = '='1' or '1' = '1';
        String sql = "SELECT user,password FROM user_table WHERE USER = '" + userName + "' AND PASSWORD = '" + password
        + "'";//字符串拼接過于繁雜
        User user = get(sql, User.class);
        if (user != null) {
        System.out.println("登陸成功!");
        } else {
        System.out.println("用戶名或密碼錯誤!");
        }
        }

        // 使用Statement實現(xiàn)對數(shù)據(jù)表的查詢操作
        public <T> T get(String sql, Class<T> clazz) {
        T t = null;

        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;
        try {
        // 1.加載配置文件
        InputStream is = StatementTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
        Properties pros = new Properties();
        pros.load(is);

        // 2.讀取配置信息
        String user = pros.getProperty("user");
        String password = pros.getProperty("password");
        String url = pros.getProperty("url");
        String driverClass = pros.getProperty("driverClass");

        // 3.加載驅(qū)動
        Class.forName(driverClass);

        // 4.獲取連接
        conn = DriverManager.getConnection(url, user, password);

        st = conn.createStatement();

        rs = st.executeQuery(sql);

        // 獲取結(jié)果集的元數(shù)據(jù)
        ResultSetMetaData rsmd = rs.getMetaData();

        // 獲取結(jié)果集的列數(shù)
        int columnCount = rsmd.getColumnCount();

        if (rs.next()) {

        t = clazz.newInstance();

        for (int i = 0; i < columnCount; i++) {
        // //1. 獲取列的名稱
        // String columnName = rsmd.getColumnName(i+1);

        // 1. 獲取列的別名
        String columnName = rsmd.getColumnLabel(i + 1);

        // 2. 根據(jù)列名獲取對應(yīng)數(shù)據(jù)表中的數(shù)據(jù)
        Object columnVal = rs.getObject(columnName);

        // 3. 將數(shù)據(jù)表中得到的數(shù)據(jù),封裝進對象
        Field field = clazz.getDeclaredField(columnName);
        field.setAccessible(true);
        field.set(t, columnVal);
        }
        return t;
        }
        } catch (Exception e) {
        e.printStackTrace();
        } finally {
        // 關(guān)閉資源
        if (rs != null) {
        try {
        rs.close();
        } catch (SQLException e) {
        e.printStackTrace();
        }
        }
        if (st != null) {
        try {
        st.close();
        } catch (SQLException e) {
        e.printStackTrace();
        }
        }

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

        return null;
        }
        }
        復(fù)制代碼

        5.2、PreparedStatement

            我們可以通過調(diào)用 Connection 對象的 preparedStatement(String sql) 方法獲取PreparedStatement對象,PreparedStatement 接口是 Statement 的子接口,它表示一條預(yù)編譯過的 SQL 語句。

            PreparedStatement 對象所代表的 SQL 語句中的參數(shù)用問號(?)來表示,調(diào)用PreparedStatement對象的 setXxx() 方法來設(shè)置這些參數(shù). setXxx() 方法有兩個參數(shù),第一個參數(shù)是要設(shè)置的 SQL 語句中的參數(shù)的索引(從 1 開始),第二個是設(shè)置的 SQL 語句中的參數(shù)的值。

        // 常用方法:
        void setXxx(int parameterIndex,Xxx value); //設(shè)置第幾個占位符的真正參數(shù)值.
        // Xxx 表示數(shù)據(jù)類型,比如 String,int,long,Date等.
        void setObject(int parameterIndex, Object x); //設(shè)置第幾個占位符的真正參數(shù)值.
        int executeUpdate(); //執(zhí)行DDL/DML語句. 注意:沒有參數(shù)
        // 若當前 SQL是 DDL語句,則返回 0.
        // 若當前 SQL是 DML語句,則返回受影響的行數(shù).
        ResultSet executeQuery(); //執(zhí)行DQL語句,返回結(jié)果集.
        close(); //釋放資源
        復(fù)制代碼

        5.3、PreparedStatement vs Statement

        1. PreparedStatement對象比Statement對象的代碼的可讀性和可維護性。

        2. PreparedStatement能最大可能提高性能。

          • DBServer會對預(yù)編譯語句提供性能優(yōu)化。因為預(yù)編譯語句有可能被重復(fù)調(diào)用,所以語句在被DBServer的編譯器編譯后的執(zhí)行代碼被緩存下來,那么下次調(diào)用時只要是相同的預(yù)編譯語句就不需要編譯,只要將參數(shù)直接傳入編譯過的語句執(zhí)行代碼中就會得到執(zhí)行。

          • 在statement語句中,即使是相同操作但因為數(shù)據(jù)內(nèi)容不一樣,所以整個語句本身不能匹配,沒有緩存語句的意義.事實是沒有數(shù)據(jù)庫會對普通語句編譯后的執(zhí)行代碼緩存。這樣每執(zhí)行一次都要對傳入的語句編譯一次。

        3. PreparedStatement 可以防止 SQL 注入 。

        5.4、 ResultSet

            查詢需要調(diào)用PreparedStatementexecuteQuery() 方法,查詢結(jié)果是一個ResultSet 對象,ResultSet 對象以邏輯表格的形式封裝了執(zhí)行數(shù)據(jù)庫操作的結(jié)果集,ResultSet 接口由數(shù)據(jù)庫廠商提供實現(xiàn)。

            ResultSet 返回的實際上就是一張數(shù)據(jù)表。有一個指針指向數(shù)據(jù)表的第一條記錄的前面。

            ResultSet 對象維護了一個指向當前數(shù)據(jù)行的游標,初始的時候,游標在第一行之前,可以通過 ResultSet 對象的 next() 方法移動到下一行。調(diào)用 next()方法檢測下一行是否有效。若有效,該方法返回 true,且指針下移。相當于Iterator對象的hasNext()next()方法的結(jié)合體。

            當指針指向一行時, 可以通過調(diào)用 getXxx(int index)getXxx(int columnName) 獲取每一列的值。*Java與數(shù)據(jù)庫交互涉及到的相關(guān)Java API中的索引都從1開始。*例如:

        getInt(1), getString("name")
        復(fù)制代碼

        5.5、CRUD操作

        5.5.1、User類

        package domain;

        import lombok.AllArgsConstructor;
        import lombok.Data;
        import lombok.NoArgsConstructor;

        /**
        * @author Xiao_Lin
        * @date 2021/1/2 19:44
        */
        @Data
        @NoArgsConstructor
        @AllArgsConstructor
        public class User {
        //id
        private Integer id;
        //用戶名
        private String username;
        //密碼
        private String pwd;

        //構(gòu)造方法的重載
        public User(String username, String pwd) {
        this.username = username;
        this.pwd = pwd;
        }
        }

        復(fù)制代碼

        5.5.2、IUserDAO

        package dao;

        import domain.User;
        import java.util.List;

        /**
        * @author Xiao_Lin
        * @date 2021/1/2 19:46
        */
        public interface IUserDAO {
        public void insert(User user);

        public void delete(Integer id);

        public void update(User user);

        public List<User> selectAll();

        public User selectUserById(Integer id);
        }

        復(fù)制代碼

        5.5.3、UserDAOImpl

        package dao.impl;

        import dao.IUserDAO;

        import dao.utils.DaoUtils;
        import domain.User;
        import java.sql.Connection;
        import java.sql.DriverManager;
        import java.sql.PreparedStatement;
        import java.sql.ResultSet;
        import java.sql.SQLException;
        import java.util.ArrayList;
        import java.util.List;
        import java.util.UUID;

        /**
        * @author Xiao_Lin
        * @date 2021/1/2 19:49
        */

        public class UserDAOImpl implements IUserDAO {
        Connection connection = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        @Override
        public void insert(User user) {
        try {
        connection = DaoUtils.getConnection();
        String sql = "insert into user (username,pwd) values (?,?)";
        ps = connection.prepareStatement(sql);
        ps.setString(1,user.getUsername());
        ps.setString(2,user.getPwd());
        ps.executeUpdate();
        System.out.println("添加成功?。?);
        } catch (SQLException e) {
        e.printStackTrace();
        }finally {
        DaoUtils.close(connection,ps,null);
        }
        }

        @Override
        public void delete(Integer id) {
        try {
        connection = DaoUtils.getConnection();
        ps = connection.prepareStatement("delete from user where id = ?");
        ps.setInt(1,id);
        ps.executeUpdate();
        System.out.println("刪除成功??!");

        } catch (Exception e) {
        e.printStackTrace();
        }finally {
        DaoUtils.close(connection,ps,null);
        }
        }

        @Override
        public void update(User user) {
        try {
        connection = DaoUtils.getConnection();
        ps = connection.prepareStatement("update user set username = ? , pwd = ? where id = ?");
        ps.setString(1,user.getUsername());
        ps.setString(2,user.getPwd());
        ps.setInt(3,user.getId());
        ps.executeUpdate();
        System.out.println("修改成功");
        } catch (Exception e) {
        e.printStackTrace();
        }finally {
        DaoUtils.close(connection,ps,null);
        }

        }

        @Override
        public List<User> selectAll() {
        List<User> users = new ArrayList<>();
        try {
        connection = DaoUtils.getConnection();
        ps = connection.prepareStatement("select * from user");
        rs = ps.executeQuery();
        while (rs.next()){
        users.add(new User(rs.getInt("id"),rs.getString("username"),rs.getString("pwd")));
        }

        } catch (Exception e) {
        e.printStackTrace();
        }finally {
        DaoUtils.close(connection,ps,rs);
        }
        return users;
        }

        @Override
        public User selectUserById(Integer id) {
        User user = null;
        try {
        connection = DaoUtils.getConnection();
        ps = connection.prepareStatement("select * from user where id = ?");
        ps.setInt(1,id);
        rs = ps.executeQuery();
        while (rs.next()){
        user = new User(rs.getInt("id"),rs.getString("username"),rs.getString("pwd"));
        }

        } catch (Exception e) {
        e.printStackTrace();
        }finally {
        DaoUtils.close(connection,ps,rs);
        }
        return user;
        }
        }

        復(fù)制代碼

        5.5.4、DaoUtils

        package dao.utils;

        import java.io.IOException;
        import java.io.InputStream;
        import java.sql.Connection;
        import java.sql.Driver;
        import java.sql.DriverManager;
        import java.sql.PreparedStatement;
        import java.sql.ResultSet;
        import java.sql.SQLException;
        import java.util.Properties;

        /**
        * @author Xiao_Lin
        * @date 2021/1/2 19:56
        */
        public class DaoUtils {
        static Connection connection = null;
        static Properties properties = null;

        static {
        InputStream resourceAsStream = Thread.currentThread().getContextClassLoader()
        .getResourceAsStream("db.properties");
        properties = new Properties();
        try {
        properties.load(resourceAsStream);
        Class.forName(properties.getProperty("DriverClassName"));
        } catch (Exception e) {
        e.printStackTrace();
        }
        }

        public static Connection getConnection() throws SQLException {
        connection= DriverManager.getConnection(properties.getProperty("url"),properties.getProperty("username"),properties.getProperty("password"));
        return connection;
        }

        public static void close(Connection conn , PreparedStatement ps , ResultSet rs){

        try {
        if(rs!=null){
        rs.close();
        }
        } catch (SQLException e) {
        e.printStackTrace();
        }finally{
        try {
        if(ps!=null){
        ps.close();
        }
        } catch (SQLException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        }finally{
        try {
        if(conn!=null){
        conn.close();
        }
        } catch (SQLException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        }
        }

        }

        }

        }

        復(fù)制代碼

        5.5.5、測試類

        package dao.impl;


        import dao.IUserDAO;
        import dao.utils.DaoUtils;
        import domain.User;
        import java.sql.SQLException;
        import java.util.List;
        import org.junit.Test;

        /**
        * @author Xiao_Lin
        * @date 2021/1/2 20:11
        */
        public class UserDAOImplTest {
        IUserDAO userDAO = new UserDAOImpl();
        @Test
        public void insert() {
        userDAO.insert(new User("ghy","123"));

        }

        @Test
        public void delete() {
        userDAO.delete(14);
        }

        @Test
        public void update() {
        userDAO.update(new User(1,"張三","666"));
        }

        @Test
        public void selectAll() {
        List<User> users = userDAO.selectAll();
        users.forEach(System.out::print);
        }

        @Test
        public void selectUserById() {
        User user = userDAO.selectUserById(1);
        System.out.println(user);
        }
        }
        復(fù)制代碼

        六、操作BLOB類型字段

        6.1、BLOB類型簡介

            MySQL中,BLOB是一個二進制大型對象,是一個可以存儲大量數(shù)據(jù)的容器,它能容納不同大小的數(shù)據(jù)。**插入BLOB類型的數(shù)據(jù)必須使用PreparedStatement,因為BLOB類型的數(shù)據(jù)無法使用字符串拼接寫的。**MySQL有四種BLOB類型,他們除了在存儲的最大信息量上不同外,除此之外他們是等同的。

            如果在指定了相關(guān)的Blob類型以后,還報錯:xxx too large,那么在mysql的安裝目錄下,找my.ini文件加上如下的配置參數(shù):max_allowed_packet=16M。同時注意:修改了my.ini文件之后,需要重新啟動mysql服務(wù)。

        6.2、插入BLOB類型

        //獲取連接
        Connection conn = JDBCUtils.getConnection();

        String sql = "insert into customers(name,email,birth,photo)values(?,?,?,?)";
        PreparedStatement ps = conn.prepareStatement(sql);

        // 填充占位符
        ps.setString(1, "張三");
        ps.setString(2, "[email protected]");
        ps.setDate(3, new Date(new java.util.Date().getTime()));
        // 操作Blob類型的變量
        FileInputStream fis = new FileInputStream("zs.png");
        ps.setBlob(4, fis);
        //執(zhí)行
        ps.execute();

        fis.close();
        JDBCUtils.closeResource(conn, ps);

        復(fù)制代碼

        6.3、修改BLOB類型

        Connection conn = JDBCUtils.getConnection();
        String sql = "update customers set photo = ? where id = ?";
        PreparedStatement ps = conn.prepareStatement(sql);

        // 填充占位符
        // 操作Blob類型的變量
        FileInputStream fis = new FileInputStream("coffee.png");
        ps.setBlob(1, fis);
        ps.setInt(2, 25);

        ps.execute();

        fis.close();
        JDBCUtils.closeResource(conn, ps);
        復(fù)制代碼

        6.4、從數(shù)據(jù)庫表中讀取BLOG類型

        String sql = "SELECT id, name, email, birth, photo FROM customer WHERE id = ?";
        conn = getConnection();
        ps = conn.prepareStatement(sql);
        ps.setInt(1, 8);
        rs = ps.executeQuery();
        if(rs.next()){
        Integer id = rs.getInt(1);
        String name = rs.getString(2);
        String email = rs.getString(3);
        Date birth = rs.getDate(4);
        Customer cust = new Customer(id, name, email, birth);
        System.out.println(cust);
        //讀取Blob類型的字段
        Blob photo = rs.getBlob(5);//這里也可以通過列的索引來讀取
        InputStream is = photo.getBinaryStream();
        OutputStream os = new FileOutputStream("c.jpg");
        byte [] buffer = new byte[1024];
        int len = 0;
        while((len = is.read(buffer)) != -1){
        os.write(buffer, 0, len);
        }
        JDBCUtils.closeResource(conn, ps, rs);

        if(is != null){
        is.close();
        }

        if(os != null){
        os.close();
        }

        }

        復(fù)制代碼

        七、批量處理

        八、數(shù)據(jù)庫事務(wù)

        8.1、問題引出

            案例: 銀行轉(zhuǎn)賬, 從張無忌賬戶上給趙敏轉(zhuǎn) 1000 塊錢。

            我們有一張account(賬戶表)。然后我們開始轉(zhuǎn)賬。

        idnamebalance
        1張無忌20000
        2趙敏0

            轉(zhuǎn)賬的步驟大概細分為以下幾個步驟:

        1. 查詢張無忌的賬戶余額是否大于等于1000。余額小于1000就提示溫馨提示:親,你的余額不足。如果余額大于等于1000轉(zhuǎn)賬。

        SELECT * FROM account WHERE name = '張無忌' AND balance >= 1000;
        復(fù)制代碼
        1. 從張無忌的賬戶余額中減少1000。

        UPDATE account SET balance = balance - 1000 WHERE name = '張無忌';
        復(fù)制代碼
        1. 在趙敏的賬戶余額中增加1000。

        UPDATE account SET balance = balance + 1000 WHERE name = '趙敏';
        復(fù)制代碼

            這個時候問題來了,當程序執(zhí)行到第②步和第③步之間,突然出現(xiàn)一個異常,此時會造成轉(zhuǎn)賬前后數(shù)據(jù)不一致的問題,會造成轉(zhuǎn)賬了,但是對方的賬戶上沒有多錢。    造成這個問題的根本原因是因為轉(zhuǎn)入轉(zhuǎn)出是兩個單獨的操作,其中一個失敗后,不會影響到另一個的執(zhí)行。但是在轉(zhuǎn)賬這個業(yè)務(wù)中,我們需要保證進出兩個操作要么都成功,要么都失敗。

            這個時候就需要引出事務(wù)的概念。

        import org.junit.Test;

        @Test
        public void testTx() throws Exception {
        // 賈璉欲執(zhí)事
        // 1 查詢張無忌的賬戶余額是否大于等于1000
        Connection conn = JDBCUtil.getConnection();
        String sql = "SELECT * FROM account WHERE balance>=? AND name=?";
        PreparedStatement pst = conn.prepareStatement(sql);
        // 給 ? 設(shè)置數(shù)據(jù)
        pst.setBigDecimal(1,new BigDecimal("1000"));
        pst.setString(2,"張無忌");
        ResultSet rs = pst.executeQuery();
        if(!rs.next()){
        System.out.println("余額不足");
        return;
        }
        // 2 從張無忌的賬戶余額中減少1000.
        sql = "UPDATE account SET balance = balance-? WHERE name=?";
        pst = conn.prepareStatement(sql);
        //設(shè)置? 的數(shù)據(jù)
        pst.setBigDecimal(1,new BigDecimal("1000"));
        pst.setString(2,"張無忌");
        pst.executeUpdate();
        // 模擬出異常
        int a = 10/0;
        // 3 在趙敏的賬戶余額中增加1000.
        sql = "UPDATE account SET balance = balance+? WHERE name=?";
        pst = conn.prepareStatement(sql);
        //設(shè)置? 的數(shù)據(jù)
        pst.setBigDecimal(1,new BigDecimal("1000"));
        pst.setString(2,"趙敏");
        pst.executeUpdate();
        // 釋放資源
        JDBCUtil.close(conn,pst,rs);
        }
        復(fù)制代碼

        8.2、事務(wù)

            事務(wù)(Transaction,簡寫為tx):一組邏輯操作單元,使數(shù)據(jù)從一種狀態(tài)變換到另一種狀態(tài)。

            事務(wù)處理(事務(wù)操作):保證所有事務(wù)都作為一個工作單元來執(zhí)行,即使出現(xiàn)了故障,都不能改變這種執(zhí)行方式。當在一個事務(wù)中執(zhí)行多個操作時,要么所有的事務(wù)都被提交(commit),那么這些修改就永久地保存下來;要么數(shù)據(jù)庫管理系統(tǒng)將放棄所作的所有修改,整個事務(wù)**回滾(rollback)**到最初狀態(tài)。回滾可以看成是撤銷操作。

            為確保數(shù)據(jù)庫中數(shù)據(jù)的一致性,數(shù)據(jù)的操縱應(yīng)當是離散的成組的邏輯單元:當它全部完成時,數(shù)據(jù)的一致性可以保持,而當這個單元中的一部分操作失敗,整個事務(wù)應(yīng)全部視為錯誤,所有從起始點以后的操作應(yīng)全部回退到開始狀態(tài)。

        8.3、事務(wù)的ACID屬性

        1. 原子性(Atomicity)原子性是指事務(wù)是一個不可分割的工作單位,事務(wù)中的操作要么都發(fā)生,要么都不發(fā)生。

        2. 一致性(Consistency)事務(wù)必須使數(shù)據(jù)庫從一個一致性狀態(tài)變換到另外一個一致性狀態(tài)。

        3. 隔離性(Isolation)事務(wù)的隔離性是指一個事務(wù)的執(zhí)行不能被其他事務(wù)干擾,即一個事務(wù)內(nèi)部的操作及使用的數(shù)據(jù)對并發(fā)的其他事務(wù)是隔離的,并發(fā)執(zhí)行的各個事務(wù)之間不能互相干擾。

        4. 持久性(Durability)持久性是指一個事務(wù)一旦被提交,它對數(shù)據(jù)庫中數(shù)據(jù)的改變就是永久性的,接下來的其他操作和數(shù)據(jù)庫故障不應(yīng)該對其有任何影響。

        8.4、數(shù)據(jù)庫的并發(fā)問題

            對于同時運行的多個事務(wù), 當這些事務(wù)訪問數(shù)據(jù)庫中相同的數(shù)據(jù)時, 如果沒有采取必要的隔離機制, 就會導(dǎo)致各種并發(fā)問題:

        1. 臟讀: 對于兩個事務(wù) T1, T2, T1 讀取了已經(jīng)被 T2 更新但還沒有被提交的字段。之后, 若 T2 回滾, T1讀取的內(nèi)容就是臨時且無效的。

        2. 不可重復(fù)讀: 對于兩個事務(wù)T1, T2, T1 讀取了一個字段, 然后 T2 更新了該字段。之后, T1再次讀取同一個字段, 值就不同了。

        3. 幻讀: 對于兩個事務(wù)T1, T2, T1 從一個表中讀取了一個字段, 然后 T2 在該表中插入了一些新的行。之后, 如果 T1 再次讀取同一個表, 就會多出幾行。

        8.4.1、事務(wù)的隔離性

             數(shù)據(jù)庫事務(wù)的隔離性: 數(shù)據(jù)庫系統(tǒng)必須具有隔離并發(fā)運行各個事務(wù)的能力, 使它們不會相互影響, 避免各種并發(fā)問題。

            一個事務(wù)與其他事務(wù)隔離的程度稱為隔離級別。數(shù)據(jù)庫規(guī)定了多種事務(wù)隔離級別, 不同隔離級別對應(yīng)不同的干擾程度, 隔離級別越高, 數(shù)據(jù)一致性就越好, 但并發(fā)性越弱。

        8.4.2、數(shù)據(jù)庫的隔離級別

            MySQL數(shù)據(jù)庫支持4種事務(wù)隔離級別。Mysql 默認的事務(wù)隔離級別為: REPEATABLE READ。

        8.4.3、設(shè)置隔離級別

            每啟動一個 mysql 程序, 就會獲得一個單獨的數(shù)據(jù)庫連接. 每個數(shù)據(jù)庫連接都有一個全局變量 @@tx_isolation, 表示當前的事務(wù)隔離級別。

        8.4.3.1、查看當前的隔離級別

        SELECT @@tx_isolation;
        復(fù)制代碼

        8.4.3.2、設(shè)置當前mysql隔離級別

        set  transaction isolation level read committed;
        復(fù)制代碼

        8.4.3.3、設(shè)置mysql的全局隔離級別

        set global transaction isolation level read committed;
        復(fù)制代碼

        8.5、事務(wù)的操作步驟

        1. 先定義開始一個事務(wù),然后對數(shù)據(jù)作修改操作。

        2. 執(zhí)行過程中,如果沒有問題就提交(commit)事務(wù),此時的修改將永久地保存下來。

        3. 如果執(zhí)行過程中有問題(異常),回滾事務(wù)(rollback),數(shù)據(jù)庫管理系統(tǒng)將放棄所作的所有修改而回到 開始事務(wù)時的狀態(tài)。

        try{
        //取消事務(wù)的自動提交機制,設(shè)置為手動提交.
        connection對象.setAutoCommit(false);
        //操作1
        //操作2
        //異常
        //操作3
        //....
        //手動提交事務(wù)
        connection對象.commit();
        }catch(Exception e){
        //處理異常
        //回滾事務(wù)
        connection對象.rollback();
        }
        復(fù)制代碼

        8.6、事務(wù)的注意事項

        1. 在默認情況下,事務(wù)會在執(zhí)行完DML操作后會自動提交。

        2. 在進行查詢操作的時候一般是不需要事務(wù)的,但是我們一般也會在查詢中寫事務(wù)

        3. 在寫代碼的時候,如果代碼完全正常沒有異常,但是數(shù)據(jù)庫中的數(shù)據(jù)沒有任何改變的話,說明是沒有提交事務(wù)。

        4. 在MySQL中,只有InnoDB存儲引擎支持事務(wù),支持外鍵,MyISAM是不支持事務(wù)的。

        5. 以后處理事務(wù)的時候,必須在service層中進行控制。

        九、連接池

        9.1、JDBC數(shù)據(jù)庫連接池的必要性

            在使用開發(fā)基于數(shù)據(jù)庫的web程序時,傳統(tǒng)的模式基本是按以下步驟:

        1. 在主程序中建立數(shù)據(jù)庫連接。

        2. 進行sql操作。

        3. 斷開數(shù)據(jù)庫連接。

          這種模式會存在幾個很顯著的問題:

        4. 普通的JDBC數(shù)據(jù)庫連接使用 DriverManager 來獲取,每次向數(shù)據(jù)庫建立連接的時候都要將 Connection 加載到內(nèi)存中,再驗證用戶名和密碼(得花費0.05s~1s的時間)。需要數(shù)據(jù)庫連接的時候,就向數(shù)據(jù)庫要求一個,執(zhí)行完成后再斷開連接。這樣的方式將會消耗大量的資源和時間。**數(shù)據(jù)庫的連接資源并沒有得到很好的重復(fù)利用。**若同時有幾百人甚至幾千人在線,頻繁的進行數(shù)據(jù)庫連接操作將占用很多的系統(tǒng)資源,嚴重的甚至會造成服務(wù)器的崩潰。

        5. **對于每一次數(shù)據(jù)庫連接,使用完后都得斷開,**否則,如果程序出現(xiàn)異常而未能關(guān)閉,將會導(dǎo)致數(shù)據(jù)庫系統(tǒng)中的內(nèi)存泄漏,最終將導(dǎo)致重啟數(shù)據(jù)庫。

        6. 這種開發(fā)不能控制被創(chuàng)建的連接對象數(shù),系統(tǒng)資源會被毫無顧及的分配出去,如連接過多,也可能導(dǎo)致內(nèi)存泄漏,服務(wù)器崩潰。

        9.2、數(shù)據(jù)庫連接池

            為解決傳統(tǒng)開發(fā)中的數(shù)據(jù)庫連接問題,我們可以采用數(shù)據(jù)庫連接池技術(shù)。

            數(shù)據(jù)庫連接池的基本思想:就是為數(shù)據(jù)庫連接建立一個“緩沖池”。預(yù)先在緩沖池中放入一定數(shù)量的連接,當需要建立數(shù)據(jù)庫連接時,只需從“緩沖池”中取出一個,使用完畢之后再放回去。

            數(shù)據(jù)庫連接池負責分配、管理和釋放數(shù)據(jù)庫連接,它允許應(yīng)用程序重復(fù)使用一個現(xiàn)有的數(shù)據(jù)庫連接,而不是重新建立一個

            數(shù)據(jù)庫連接池在初始化時將創(chuàng)建一定數(shù)量的數(shù)據(jù)庫連接放到連接池中,這些數(shù)據(jù)庫連接的數(shù)量是由最小數(shù)據(jù)庫連接數(shù)來設(shè)定的。無論這些數(shù)據(jù)庫連接是否被使用,連接池都將一直保證至少擁有這么多的連接數(shù)量。連接池的最大數(shù)據(jù)庫連接數(shù)量限定了這個連接池能占有的最大連接數(shù),當應(yīng)用程序向連接池請求的連接數(shù)超過最大連接數(shù)量時,這些請求將被加入到等待隊列中。

        9.2.1、數(shù)據(jù)庫連接池的原理以及優(yōu)勢

            使用數(shù)據(jù)庫連接池的優(yōu)點也是很明顯的:

        1. 資源重復(fù)使用

          由于數(shù)據(jù)庫連接得以重用,避免了頻繁創(chuàng)建,釋放連接引起的大量性能開銷。在減少系統(tǒng)消耗的基礎(chǔ)上,另一方面也增加了系統(tǒng)運行環(huán)境的平穩(wěn)性。

        2. 更快的系統(tǒng)反應(yīng)速度

          數(shù)據(jù)庫連接池在初始化過程中,往往已經(jīng)創(chuàng)建了若干數(shù)據(jù)庫連接置于連接池中備用。此時連接的初始化工作均已完成。對于業(yè)務(wù)請求處理而言,直接利用現(xiàn)有可用連接,避免了數(shù)據(jù)庫連接初始化和釋放過程的時間開銷,從而減少了系統(tǒng)的響應(yīng)時間。

        3. 新的資源分配手段

          對于多應(yīng)用共享同一數(shù)據(jù)庫的系統(tǒng)而言,可在應(yīng)用層通過數(shù)據(jù)庫連接池的配置,實現(xiàn)某一應(yīng)用最大可用數(shù)據(jù)庫連接數(shù)的限制,避免某一應(yīng)用獨占所有的數(shù)據(jù)庫資源。

        4. 統(tǒng)一的連接管理,避免數(shù)據(jù)庫連接泄漏

          在較為完善的數(shù)據(jù)庫連接池實現(xiàn)中,可根據(jù)預(yù)先的占用超時設(shè)定,強制回收被占用連接,從而避免了常規(guī)數(shù)據(jù)庫連接操作中可能出現(xiàn)的資源泄露。

        9.2.2、數(shù)據(jù)庫連接池的屬性

            基本屬性:連接池存了連接對象,而連接對象依賴四要素,所以四要素(driverClassName,url,username,password)是基本要求。

            其他屬性:對連接對象做限制的配置

        1. 初始化連接數(shù):在連接池中事先準備好初始化Connection對象。

        2. 最多連接數(shù):在連接池中最多有一定數(shù)量的Connection對象,其他客戶端進入等待狀態(tài)。

        3. 最少連接數(shù):在連接池中最少一定數(shù)量的Connection對象。

        4. 最長等待時間:使用一定時間來申請獲取Connection對象,如果時間到還沒有申請到,則提示,自

        動放棄。

        5. 最長超時時間:如果你在一定時間之內(nèi)沒有任何動作,則認為是自動放棄Connection對象。

        9.3、數(shù)據(jù)庫連接池的分類

            JDBC 的數(shù)據(jù)庫連接池使用javax.sql.DataSource來表示,DataSource 只是一個接口,該接口通常由服務(wù)器(Weblogic, WebSphere, Tomcat)提供實現(xiàn),也有一些開源組織提供實現(xiàn):

        1. DBCP 是Apache提供的數(shù)據(jù)庫連接池。tomcat 服務(wù)器自帶dbcp數(shù)據(jù)庫連接池。速度相對c3p0較快,但因自身存在BUG,Hibernate3已不再提供支持。

        2. C3P0 是一個開源組織提供的一個數(shù)據(jù)庫連接池,**速度相對較慢,穩(wěn)定性還可以。**hibernate官方推薦使用。

        3. Proxool 是sourceforge下的一個開源項目數(shù)據(jù)庫連接池,有監(jiān)控連接池狀態(tài)的功能,穩(wěn)定性較c3p0差一點

        4. BoneCP 是一個開源組織提供的數(shù)據(jù)庫連接池,速度快。

        5. Druid 是阿里提供的數(shù)據(jù)庫連接池,據(jù)說是集DBCP 、C3P0 、Proxool 優(yōu)點于一身的數(shù)據(jù)庫連接池,但是速度不確定是否有BoneCP快。

            DataSource 通常被稱為數(shù)據(jù)源,它包含連接池和連接池管理兩個部分,習慣上也經(jīng)常把 DataSource 稱為連接池,DataSource用來取代DriverManager來獲取Connection,獲取速度快,同時可以大幅度提高數(shù)據(jù)庫訪問速度。

        注意:

        1. 數(shù)據(jù)源和數(shù)據(jù)庫連接不同,數(shù)據(jù)源無需創(chuàng)建多個,它是產(chǎn)生數(shù)據(jù)庫連接的工廠,因此整個應(yīng)用只需要一個數(shù)據(jù)源即可。

        2. 當數(shù)據(jù)庫訪問結(jié)束后,程序還是像以前一樣關(guān)閉數(shù)據(jù)庫連接:conn.close(); 但conn.close()并沒有關(guān)閉數(shù)據(jù)庫的物理連接,它僅僅把數(shù)據(jù)庫連接釋放,歸還給了數(shù)據(jù)庫連接池。

        9.4、DBCP連接池

            DBCP 是 Apache 軟件基金組織下的開源連接池實現(xiàn),該連接池依賴該組織下的另一個開源系統(tǒng):Common-pool。如需使用該連接池實現(xiàn),應(yīng)在系統(tǒng)中增加如下兩個 jar 文件:

        • Commons-dbcp.jar:連接池的實現(xiàn)

        • Commons-pool.jar:連接池實現(xiàn)的依賴庫

             **Tomcat 的連接池正是采用該連接池來實現(xiàn)的。**該數(shù)據(jù)庫連接池既可以與應(yīng)用服務(wù)器整合使用,也可由應(yīng)用程序獨立使用。

            數(shù)據(jù)源和數(shù)據(jù)庫連接不同,數(shù)據(jù)源無需創(chuàng)建多個,它是產(chǎn)生數(shù)據(jù)庫連接的工廠,因此整個應(yīng)用只需要一個數(shù)據(jù)源即可。

            當數(shù)據(jù)庫訪問結(jié)束后,程序還是像以前一樣關(guān)閉數(shù)據(jù)庫連接:conn.close(); 但上面的代碼并沒有關(guān)閉數(shù)據(jù)庫的物理連接,它僅僅把數(shù)據(jù)庫連接釋放,歸還給了數(shù)據(jù)庫連接池。

        9.4.1、DBCP屬性說明

        屬性默認值說明
        initialSize0連接池啟動時創(chuàng)建的初始化連接數(shù)量
        maxActive8連接池中可同時連接的最大的連接數(shù)
        maxIdle8連接池中最大的空閑的連接數(shù),超過的空閑連接將被釋放,如果設(shè)置為負數(shù)表示不限制
        minIdle0連接池中最小的空閑的連接數(shù),低于這個數(shù)量會被創(chuàng)建新的連接。該參數(shù)越接近maxIdle,性能越好,因為連接的創(chuàng)建和銷毀,都是需要消耗資源的;但是不能太大。
        maxWait無限制最大等待時間,當沒有可用連接時,連接池等待連接釋放的最大時間,超過該時間限制會拋出異常,如果設(shè)置-1表示無限等待
        poolPreparedStatementsfalse開啟池的Statement是否prepared
        maxOpenPreparedStatements無限制開啟池的prepared 后的同時最大連接數(shù)
        minEvictableIdleTimeMillis
        連接池中連接,在時間段內(nèi)一直空閑, 被逐出連接池的時間
        removeAbandonedTimeout300超過時間限制,回收沒有用(廢棄)的連接
        removeAbandonedfalse超過removeAbandonedTimeout時間后,是否進 行沒用連接(廢棄)的回收

        9.4.2、獲取連接的方式

        //使用dbcp數(shù)據(jù)庫連接池的配置文件方式,獲取數(shù)據(jù)庫的連接:推薦
        // 創(chuàng)建一個DataSource對象
        private static DataSource source = null;
        static{
        try {
        //創(chuàng)建一個Properties,用于讀取配置文件
        Properties pros = new Properties();
        //讀取配置文件
        InputStream is = DBCPTest.class.getClassLoader().getResourceAsStream("db.properties");
        //加載配置文件
        pros.load(is);
        //根據(jù)提供的BasicDataSourceFactory創(chuàng)建對應(yīng)的DataSource對象
        source = BasicDataSourceFactory.createDataSource(pros);
        } catch (Exception e) {
        e.printStackTrace();
        }

        }
        public static Connection getConnection4() throws Exception {
        Connection conn = source.getConnection();
        return conn;
        }
        復(fù)制代碼
        driverClassName=com.mysql.jdbc.Driver
        url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true&useServerPrepStmts=false
        username=root
        password=123456

        initialSize=10
        #...
        復(fù)制代碼

        9.4.3、注意事項

            由于使用了DBCP,所以配置文件的key我們必須按照他官方給定的要求來書寫。

        9.5、Druid(德魯伊)連接池

            Druid是阿里巴巴開源平臺上一個數(shù)據(jù)庫連接池實現(xiàn),它結(jié)合了C3P0、DBCP、Proxool等DB池的優(yōu)點,同時加入了日志監(jiān)控,可以很好的監(jiān)控DB池連接和SQL的執(zhí)行情況,可以說是針對監(jiān)控而生的DB連接池,可以說是目前最好的連接池之一。

        9.5.1、Druid參數(shù)詳解

        配置缺省說明
        name
        配置這個屬性的意義在于,如果存在多個數(shù)據(jù)源,監(jiān)控的時候可以通過名字來區(qū)分開來。  如果沒有配置,將會生成一個名字,格式是:”DataSource-” +   System.identityHashCode(this)
        url
        連接數(shù)據(jù)庫的url,不同數(shù)據(jù)庫不一樣。例如:mysql :   jdbc:mysql://10.20.153.104:3306/druid2      oracle :   jdbc:oracle:thin:@10.20.149.85:1521:ocnauto
        username
        連接數(shù)據(jù)庫的用戶名
        password
        連接數(shù)據(jù)庫的密碼。如果你不希望密碼直接寫在配置文件中,可以使用ConfigFilter。詳細看這里:github.com/alibaba/dru…
        driverClassName
        根據(jù)url自動識別   這一項可配可不配,如果不配置druid會根據(jù)url自動識別dbType,然后選擇相應(yīng)的driverClassName(建議配置下)
        initialSize0初始化時建立物理連接的個數(shù)。初始化發(fā)生在顯示調(diào)用init方法,或者第一次getConnection時
        maxActive8最大連接池數(shù)量
        maxIdle8已經(jīng)不再使用,配置了也沒效果
        minIdle
        最小連接池數(shù)量
        maxWait
        獲取連接時最大等待時間,單位毫秒。配置了maxWait之后,缺省啟用公平鎖,并發(fā)效率會有所下降,如果需要可以通過配置useUnfairLock屬性為true使用非公平鎖。
        poolPreparedStatementsfalse是否緩存preparedStatement,也就是PSCache。PSCache對支持游標的數(shù)據(jù)庫性能提升巨大,比如說oracle。在mysql下建議關(guān)閉。
        maxOpenPreparedStatements-1要啟用PSCache,必須配置大于0,當大于0時,poolPreparedStatements自動觸發(fā)修改為true。在Druid中,不會存在Oracle下PSCache占用內(nèi)存過多的問題,可以把這個數(shù)值配置大一些,比如說100
        validationQuery
        用來檢測連接是否有效的sql,要求是一個查詢語句。如果validationQuery為null,testOnBorrow、testOnReturn、testWhileIdle都不會其作用。
        testOnBorrowtrue申請連接時執(zhí)行validationQuery檢測連接是否有效,做了這個配置會降低性能。
        testOnReturnfalse歸還連接時執(zhí)行validationQuery檢測連接是否有效,做了這個配置會降低性能
        testWhileIdlefalse建議配置為true,不影響性能,并且保證安全性。申請連接的時候檢測,如果空閑時間大于timeBetweenEvictionRunsMillis,執(zhí)行validationQuery檢測連接是否有效。
        timeBetweenEvictionRunsMillis
        有兩個含義:1)Destroy線程會檢測連接的間隔時間2)testWhileIdle的判斷依據(jù),詳細看testWhileIdle屬性的說明
        numTestsPerEvictionRun
        不再使用,一個DruidDataSource只支持一個EvictionRun
        minEvictableIdleTimeMillis

        connectionInitSqls
        物理連接初始化的時候執(zhí)行的sql
        exceptionSorter
        根據(jù)dbType自動識別   當數(shù)據(jù)庫拋出一些不可恢復(fù)的異常時,拋棄連接
        filters
        屬性類型是字符串,通過別名的方式配置擴展插件,常用的插件有:  監(jiān)控統(tǒng)計用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
        proxyFilters
        類型是List,如果同時配置了filters和proxyFilters,是組合關(guān)系,并非替換關(guān)系

        9.5.2、獲取連接方式

        package com.utils;

        import com.alibaba.druid.pool.DruidDataSource;
        import com.alibaba.druid.pool.DruidDataSourceFactory;
        import java.io.IOException;
        import java.io.InputStream;
        import java.net.URL;
        import java.sql.Connection;
        import java.sql.SQLException;
        import java.util.Properties;
        import javax.sql.DataSource;

        /**
        * @author Xiao_Lin
        * @date 2021/1/3 19:47
        */
        public class DruidUtils {
        static DataSource ds = null;
        private DruidUtils(){

        }

        static {
        InputStream stream = Thread.currentThread().getContextClassLoader()
        .getResourceAsStream("db.properties");
        Properties properties = new Properties();
        try {
        properties.load(stream);
        ds = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
        e.printStackTrace();
        }
        }

        public static Connection getConnection(){
        try {
        return ds.getConnection();
        } catch (SQLException e) {
        e.printStackTrace();
        }
        return null;
        }
        }

        復(fù)制代碼
        DriverClassName = com.mysql.jdbc.Driver
        url = jdbc:mysql:///db?characterEncoding=utf-8&useSSL=false
        username = root
        password = 123456
        復(fù)制代碼

        9.5.3、注意事項

            properties配置文件中的 key 一定要和 DruidDataSource 中對應(yīng)的屬性名一致。

        port java.io.IOException; import java.io.InputStream; import java.net.URL; import java.sql.Connection; import java.sql.SQLException; import java.util.Properties; import javax.sql.DataSource;

        /**

        • @author Xiao_Lin

        • @date 2021/1/3 19:47

        */ public class DruidUtils { static DataSource ds = null; private DruidUtils(){

        }

        static { InputStream stream = Thread.currentThread().getContextClassLoader() .getResourceAsStream("db.properties"); Properties properties = new Properties(); try { properties.load(stream); ds = DruidDataSourceFactory.createDataSource(properties); } catch (Exception e) { e.printStackTrace(); } }

        public static Connection getConnection(){ try { return ds.getConnection(); } catch (SQLException e) { e.printStackTrace(); } return null; } }


        ```properties
        DriverClassName = com.mysql.jdbc.Driver
        url = jdbc:mysql:///db?characterEncoding=utf-8&useSSL=false
        username = root
        password = 123456


        作者:XiaoLin_Java
        鏈接:https://juejin.cn/post/6986807373721501726
        來源:掘金
        著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。



        瀏覽 43
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            无码日韩视频 | 午夜精品毛片 | 稀缺小U女呦一二三四区 | 俺去俺来也WWW色老板 | 天天做爰裸体免费视频 | 张柏芝裸体无删减版照 | 操女生逼逼网站 | 自拍偷拍综合网 | 国产福利视频在线播放 | 大鸡巴操小骚逼 |