事務&數(shù)據(jù)庫連接池&DBUtils

事務

  1. Transaction 其實指的一組操作,里面包含許多個單一的邏輯。只要有一個邏輯沒有執(zhí)行成功,那么都算失敗。 所有的數(shù)據(jù)都回歸到最初的狀態(tài)(回滾)

  2. 事務的作用:為了確保邏輯的成功。 例子: 銀行的轉賬。

  3. 使用命令行方式演示事務。

    • 關閉自動提交功能。


      關閉自動提交
    • 開啟事務 start transaction;

      開啟事務

    • 提交或者回滾事務:事務提交后或者回滾就結束了

      1. commit; 提交事務, 數(shù)據(jù)將會寫到磁盤上的數(shù)據(jù)庫


        commit
      2. rollback ; 數(shù)據(jù)回滾,回到最初的狀態(tài)


        rollback
  4. 使用代碼方式演示事務

public void testTransaction() throws SQLException {
    Connection conn = null;
    PreparedStatement ps = null;

    try {
        conn = JDBCUtil.getConn();
        String sql = "update bank set money = money - ? where id = ?";
        ps = conn.prepareStatement(sql);

        // 關閉提交 事務只是針對連接連接對象,如果再開一個連接對象,那么還是默認的提交。
        conn.setAutoCommit(false);

        ps.setInt(1, 100);
        ps.setInt(2, 1);
        ps.executeUpdate();

        int a = 10 / 0;

        ps.setInt(1, -100);
        ps.setInt(2, 2);
        ps.executeUpdate();

        // 兩段代碼都執(zhí)行成功 提交事務
        conn.commit();
    } catch (SQLException e) {
        // 出現(xiàn)異常,回滾事務
        conn.rollback();
        e.printStackTrace();
    } finally {
        JDBCUtil.release(conn, ps);
    }

}
  1. 事務的特性
    • 原子性:指的是 事務中包含的邏輯,不可分割。
    • 一致性:指的是 事務執(zhí)行前后。數(shù)據(jù)完整性
    • 隔離性:指的是 事務在執(zhí)行期間不應該受到其他事務的影響
    • 持久性:指的是 事務執(zhí)行成功,那么數(shù)據(jù)應該持久保存到磁盤上。
  2. 事務的安全隱患 :不考慮隔離級別設置,那么會出現(xiàn)以下問題
      1. 臟讀:一個事務讀到另外一個事務還未提交的數(shù)據(jù)
      2. 不可重復讀 :一個事務讀到了另外一個事務提交的數(shù)據(jù) ,造成了前后兩次查詢結果不一致。
      3. 幻讀:一個事務讀到了另一個事務insert的數(shù)據(jù) ,造成前后查詢結果不一致 。
    • 寫:丟失更新


      丟失更新
      1. 悲觀鎖(認為一定會丟失更新):可以在查詢的時候,加入 for update(數(shù)據(jù)庫鎖機制,排他鎖),有點類似序列化


        悲觀鎖
      2. 樂觀鎖(認為一定不會丟失更新):要求程序員自己控制。


        樂觀鎖
  3. 事務的隔離級別:mySql 默認的隔離級別是 可重復讀,Oracle 默認的隔離級別是 讀已提交
    • 讀未提交(Read Uncommitted):可以讀到其他事務未提交的數(shù)據(jù)。引發(fā)問題: 臟讀
    • 讀已提交(Read Committed):只能讀到已提交的數(shù)據(jù)。解決: 臟讀 , 引發(fā): 不可重復讀
    • 可重復讀(Repeatable Read):事務中讀取的數(shù)據(jù)不受其他事務提交數(shù)據(jù)的影響,前后讀取的數(shù)據(jù)一致。解決: 臟讀 、 不可重復讀 , 未解決: 幻讀
    • 可串行化(Serializable) :如果有一個連接的隔離級別設置為了串行化 ,那么誰先打開了事務, 誰就有了先執(zhí)行的權利, 誰后打開事務,誰就只能得著,等前面的那個事務,提交或者回滾后,才能執(zhí)行。 但是這種隔離級別一般比較少用。 容易造成性能上的問題。 效率比較低。解決: 臟讀、 不可重復讀 、 幻讀。
    • 按效率劃分,從高到低 讀未提交 > 讀已提交 > 可重復讀 > 可串行化
    • 按攔截程度 ,從高到底 可串行化 > 可重復讀 > 讀已提交 > 讀未提交

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

  1. 數(shù)據(jù)庫的連接對象創(chuàng)建工作,比較消耗性能。 一開始先在內(nèi)存中開辟一塊空間(集合) , 先往池子里面放置 多個連接對象。 后面需要連接的話,直接從池子里面去。不要去自己創(chuàng)建連接了。 使用完畢, 要記得歸還連接,確保連接對象能循環(huán)利用。Sun公司定義了一個連接池接口DataSource
  2. 自定義連接池
    • 連接池類
    **
    * 實現(xiàn)連接池,一開始在連接池中有十個連接對象
    */
    public class MyDataSource implements DataSource {
    
    List<Connection> list = new ArrayList<>(); // 連接池集合
    
    public MyDataSource() { // 構造函數(shù) 在連接池中初始化十個連接對象
        for (int i = 0; i < 10; i++) {
            list.add(JDBCUtil.getConn());
        }
    }
    
    /**
     * 該連接池對外公布獲取連接池的方法
     * @return
     * @throws SQLException
     */
    @Override
    public Connection getConnection() throws SQLException {
        if(list.size() == 0 ) { // 連接池中沒有連接對象 擴容
            for (int i = 0; i < 5; i++) {
                list.add(JDBCUtil.getConn());
            }
        }
    
        // 移除連接池中第一個連接對象,包裝過后并將它返回
        Connection conn = list.remove(0);
        Connection connWrap = new ConnectionWrap(conn, list);
    
        return connWrap;
    }
    
    • 使用裝飾者模式解決連接池的歸還問題,符合面向接口編程
    public class ConnectionWrap implements Connection {
    
    Connection conn = null;
    List<Connection> list = null;
    
    public ConnectionWrap(Connection conn, List<Connection> list) {
        super();
        this.conn = conn;
        this.list = list;
    }
    
    @Override
    public void close() throws SQLException {
        // 在這里寫歸還操作
        System.out.println("歸還前" + list.size());
        list.add(conn);
        System.out.println("歸還后" + list.size());
    }
    @Override
    public PreparedStatement prepareStatement(String sql) throws SQLException {
        return conn.prepareStatement(sql);
    }
    
    • 連接池的使用
    @Test
    public  void testPool() throws SQLException {
        Connection connection = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        MyDataSource dataSource = new MyDataSource();
        try {
            // 其實在這里得到的是 ConnectionWrap 對象
            connection = dataSource.getConnection();
            String sql = "select * from bank";
    
            ps = connection.prepareStatement(sql);
            rs = ps.executeQuery();
    
            while(rs.next()) {
                String name = rs.getString("name");
                int id = rs.getInt("id");
                int money = rs.getInt("money");
                System.out.println(id + "---" + name + "---" + money);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JDBCUtil.release(connection, ps, rs);
        }
    }
    
  3. 開源連接池
    • DBCP
      1. 導入jar包
      2. 不使用配置文件
      public class DBCPDemo {
        @Test
        public void testDBCP01() {
          Connection conn = null;
          PreparedStatement ps = null;
          ResultSet rs = null;
      
          try {
            // 1. 得到連接池對象
            BasicDataSource dataSource = new BasicDataSource();
      
            // 2. 設置連接屬性
            dataSource.setDriverClassName("com.mysql.jdbc.Driver");
            dataSource.setUrl("jdbc:mysql://localhost:3307/hgzdata");
            dataSource.setUsername("root");
            dataSource.setPassword("root");
      
            // 3. 獲取連接對象
            conn = dataSource.getConnection();
      
            // 4. 數(shù)據(jù)庫操作
            String sql = "select * from bank";
            ps = conn.prepareStatement(sql);
            rs = ps.executeQuery();
            while(rs.next()) {
                int id = rs.getInt("id");
                String name = rs.getString("name");
                int money = rs.getInt("money");
                System.out.println(id + "---" + name + "---" + money);
      
            }
          } catch (SQLException e) {
              e.printStackTrace();
          } finally {
              JDBCUtil.release(conn, ps, rs);
          }
        }
      }
      
      1. 使用配置文件 將配置文件 dbcpconfig.properties 放置在 src 目錄下
      BasicDataSourceFactory factory = new BasicDataSourceFactory();
      Properties properties = new Properties();
      InputStream is = new FileInputStream("src/dbcpconfig.properties");
      properties.load(is);
      DataSource dataSource = factory.createDataSource(properties);
      
    • C3P0
      1. 導入 jar 包
      2. 不使用配置文件
      // 1. 得到連接池對象
      ComboPooledDataSource dataSource = new ComboPooledDataSource();
      
      // 2. 設置連接屬性
      dataSource.setDriverClass("com.mysql.jdbc.Driver");
      dataSource.setJdbcUrl("jdbc:mysql://localhost:3307/hgzdata");
      dataSource.setUser("root");
      dataSource.setPassword("root");
      
      1. 使用配置文件 將配置文件 c3p0-config.xml 放在 src 目錄下
      // 1. 得到連接池對象 默認讀取配置文件 獲取連接信息
      ComboPooledDataSource dataSource = new ComboPooledDataSource("configname");
      

DBUtils

  1. dbutils 只是幫我們簡化了CRUD 的代碼, 但是連接的創(chuàng)建以及獲取工作。 不在他的考慮范圍,導入 jar 包
  2. update 操作
QueryRunner queryRunner = new QueryRunner(new ComboPooledDataSource());
//增加
queryRunner.update("insert into account values (null , ? , ? )", "aa" ,1000);
    
//刪除
queryRunner.update("delete from account where id = ?", 5);
    
//更新
queryRunner.update("update account set money = ? where id = ?", 10000000 , 6);
  1. query 操作 new接口的匿名實現(xiàn)類
public void queryTest() {
    // 獲取查詢對象
    QueryRunner queryRunner = new QueryRunner(new ComboPooledDataSource());

    try {
        User user = queryRunner.query("select * from bank where id = ?", new ResultSetHandler<User>() {

            @Override
            public User handle(ResultSet resultSet) throws SQLException {
                User user = new User();
                while (resultSet.next()) {
                    user.setName(resultSet.getString("name"));
                    user.setMoney(resultSet.getInt("money"));
                }
                return user;
            }
        }, 1);
        System.out.println(user.toString());
    } catch (SQLException e) {
        e.printStackTrace();
    }
}
  1. query 操作 使用框架的 接口實現(xiàn)類 查詢一行數(shù)據(jù)
public void queryTest1() {
    QueryRunner queryRunner = new QueryRunner(new ComboPooledDataSource());

    // 查詢單行數(shù)據(jù)
    try {
        User user = queryRunner.query("select * from bank where id = ?",
                new BeanHandler<User>(User.class)
                , 2);
        System.out.println(user.toString());
    } catch (SQLException e) {
        e.printStackTrace();
    }
}
  1. query 操作 使用框架的 接口實現(xiàn)類 查詢多行數(shù)據(jù)
public void queryTest2() {
    QueryRunner queryRunner = new QueryRunner(new ComboPooledDataSource());

    // 查詢單行數(shù)據(jù)
    try {
        List<User> users = queryRunner.query("select * from bank",
                new BeanListHandler<User>(User.class));
        for (User user: users
             ) {
            System.out.println(user.toString());
        }
    } catch (SQLException e) {
        e.printStackTrace();
    }
}
  1. ResultSetHandler(queryRunner.query()第二個參數(shù)) 常用的實現(xiàn)類
    • 最常用
    BeanHandler,  查詢到的單個數(shù)據(jù)封裝成一個對象
    BeanListHandler, 查詢到的多個數(shù)據(jù)封裝 成一個List<對象>
    
    ArrayHandler,  查詢到的單個數(shù)據(jù)封裝成一個數(shù)組
    ArrayListHandler,  查詢到的多個數(shù)據(jù)封裝成一個集合 ,集合里面的元素是數(shù)組。
    
    MapHandler,  查詢到的單個數(shù)據(jù)封裝成一個map
    MapListHandler,查詢到的多個數(shù)據(jù)封裝成一個集合 ,集合里面的元素是map。 
    
    • 不常用
    ColumnListHandler
    KeyedHandler
    ScalarHandler
    
  2. 模擬DBUtils功能代碼
    • update
    public void update(String sql, Object ...args) {
        Connection conn = null;
        PreparedStatement ps = null;
    
        try {
            ComboPooledDataSource dataSource = new ComboPooledDataSource();
    
            conn = dataSource.getConnection();
    
            ps = conn.prepareStatement(sql);
    
            // 根據(jù)元數(shù)據(jù)參數(shù) 獲取問號個數(shù)
            ParameterMetaData parameterMetaData = ps.getParameterMetaData();
            int paramerterCount = parameterMetaData.getParameterCount();
    
            for (int i = 0; i < paramerterCount ; i++) {
                ps.setObject(i + 1, args[i]);
            }
            ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtil.release(conn,ps);
        }
    }
    
    • query
    public <T> T query(String sql,ResultHandler<T> handler ,Object ...args) {
    
        Connection conn = null;
        PreparedStatement ps= null;
        ResultSet rs = null;
    
        // 1. 獲取連接池
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
    
        try {
            // 2. 獲取連接對象
            conn = dataSource.getConnection();
            ps = conn.prepareStatement(sql);
            // 3. 根據(jù)問號個數(shù)填充參數(shù)
            ParameterMetaData parameterMetaData = ps.getParameterMetaData();
            int paramterCount = parameterMetaData.getParameterCount();
            for (int i = 0; i < paramterCount ; i++) {
                ps.setObject(i+1, args[i]);
            }
            rs = ps.executeQuery();
            // 4. 讓傳入的 結果處理對象來處理 結果集
            T t = (T) handler.handle(rs);
            return t;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            JDBCUtil.release(conn, ps, rs);
        }
    }
    
    結果集處理接口
    public interface ResultHandler<T>{
        T handle(ResultSet rs);
    }
    
    query 方法的調(diào)用
        public void testQuery(){
        User user = query("select * from bank where id = ?", new ResultHandler<User>() {
            @Override
            public User handle(ResultSet rs) {
                User user = new User();
                try {
                    if (rs.next()) {
                        user.setName(rs.getString("name"));
                        user.setMoney(rs.getInt("money"));
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                return user;
            }
        }, 3);
    
        System.out.println(user.toString());
    }
    
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

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

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