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

事務(wù)

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

  2. 事務(wù)的作用:為了確保邏輯的成功。 例子: 銀行的轉(zhuǎn)賬惹悄。

  3. 使用命令行方式演示事務(wù)春叫。

    • 關(guān)閉自動提交功能。


      關(guān)閉自動提交
    • 開啟事務(wù) start transaction;

      開啟事務(wù)

    • 提交或者回滾事務(wù):事務(wù)提交后或者回滾就結(jié)束了

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


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


        rollback
  4. 使用代碼方式演示事務(wù)

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);

        // 關(guān)閉提交 事務(wù)只是針對連接連接對象,如果再開一個連接對象当纱,那么還是默認(rèn)的提交呛每。
        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í)行成功 提交事務(wù)
        conn.commit();
    } catch (SQLException e) {
        // 出現(xiàn)異常,回滾事務(wù)
        conn.rollback();
        e.printStackTrace();
    } finally {
        JDBCUtil.release(conn, ps);
    }

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


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


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


        樂觀鎖
  3. 事務(wù)的隔離級別:mySql 默認(rèn)的隔離級別是 可重復(fù)讀唉侄,Oracle 默認(rèn)的隔離級別是 讀已提交
    • 讀未提交(Read Uncommitted):可以讀到其他事務(wù)未提交的數(shù)據(jù)。引發(fā)問題: 臟讀
    • 讀已提交(Read Committed):只能讀到已提交的數(shù)據(jù)顷帖。解決: 臟讀 美旧, 引發(fā): 不可重復(fù)讀
    • 可重復(fù)讀(Repeatable Read):事務(wù)中讀取的數(shù)據(jù)不受其他事務(wù)提交數(shù)據(jù)的影響,前后讀取的數(shù)據(jù)一致贬墩。解決: 臟讀 榴嗅、 不可重復(fù)讀 , 未解決: 幻讀
    • 可串行化(Serializable) :如果有一個連接的隔離級別設(shè)置為了串行化 陶舞,那么誰先打開了事務(wù)嗽测, 誰就有了先執(zhí)行的權(quán)利, 誰后打開事務(wù)肿孵,誰就只能得著唠粥,等前面的那個事務(wù),提交或者回滾后停做,才能執(zhí)行晤愧。 但是這種隔離級別一般比較少用。 容易造成性能上的問題蛉腌。 效率比較低官份。解決: 臟讀、 不可重復(fù)讀 烙丛、 幻讀舅巷。
    • 按效率劃分,從高到低 讀未提交 > 讀已提交 > 可重復(fù)讀 > 可串行化
    • 按攔截程度 河咽,從高到底 可串行化 > 可重復(fù)讀 > 讀已提交 > 讀未提交

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

  1. 數(shù)據(jù)庫的連接對象創(chuàng)建工作钠右,比較消耗性能。 一開始先在內(nèi)存中開辟一塊空間(集合) 忘蟹, 先往池子里面放置 多個連接對象飒房。 后面需要連接的話,直接從池子里面去寒瓦。不要去自己創(chuàng)建連接了情屹。 使用完畢, 要記得歸還連接杂腰,確保連接對象能循環(huán)利用垃你。Sun公司定義了一個連接池接口DataSource
  2. 自定義連接池
    • 連接池類
    **
    * 實(shí)現(xiàn)連接池,一開始在連接池中有十個連接對象
    */
    public class MyDataSource implements DataSource {
    
    List<Connection> list = new ArrayList<>(); // 連接池集合
    
    public MyDataSource() { // 構(gòu)造函數(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 ) { // 連接池中沒有連接對象 擴(kuò)容
            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 {
            // 其實(shí)在這里得到的是 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. 導(dǎo)入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. 設(shè)置連接屬性
            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. 導(dǎo)入 jar 包
      2. 不使用配置文件
      // 1. 得到連接池對象
      ComboPooledDataSource dataSource = new ComboPooledDataSource();
      
      // 2. 設(shè)置連接屬性
      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. 得到連接池對象 默認(rèn)讀取配置文件 獲取連接信息
      ComboPooledDataSource dataSource = new ComboPooledDataSource("configname");
      

DBUtils

  1. dbutils 只是幫我們簡化了CRUD 的代碼皆刺, 但是連接的創(chuàng)建以及獲取工作。 不在他的考慮范圍凌摄,導(dǎo)入 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接口的匿名實(shí)現(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 操作 使用框架的 接口實(shí)現(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 操作 使用框架的 接口實(shí)現(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ù)) 常用的實(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. 讓傳入的 結(jié)果處理對象來處理 結(jié)果集
            T t = (T) handler.handle(rs);
            return t;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            JDBCUtil.release(conn, ps, rs);
        }
    }
    
    結(jié)果集處理接口
    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());
    }
    
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市器予,隨后出現(xiàn)的幾起案子浪藻,更是在濱河造成了極大的恐慌,老刑警劉巖乾翔,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件爱葵,死亡現(xiàn)場離奇詭異,居然都是意外死亡反浓,警方通過查閱死者的電腦和手機(jī)萌丈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來雷则,“玉大人辆雾,你說我怎么就攤上這事≡屡” “怎么了乾颁?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長艺栈。 經(jīng)常有香客問我,道長湾盒,這世上最難降的妖魔是什么湿右? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮罚勾,結(jié)果婚禮上毅人,老公的妹妹穿的比我還像新娘。我一直安慰自己尖殃,他們只是感情好丈莺,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著送丰,像睡著了一般缔俄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天俐载,我揣著相機(jī)與錄音蟹略,去河邊找鬼。 笑死遏佣,一個胖子當(dāng)著我的面吹牛挖炬,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播状婶,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼意敛,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了膛虫?” 一聲冷哼從身側(cè)響起草姻,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎走敌,沒想到半個月后碴倾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡掉丽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年跌榔,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捶障。...
    茶點(diǎn)故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡僧须,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出项炼,到底是詐尸還是另有隱情担平,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布锭部,位于F島的核電站暂论,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏拌禾。R本人自食惡果不足惜取胎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望湃窍。 院中可真熱鬧闻蛀,春花似錦、人聲如沸您市。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽茵休。三九已至薪棒,卻和暖如春手蝎,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背盗尸。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工柑船, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人泼各。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓鞍时,卻偏偏與公主長得像,于是被迫代替她去往敵國和親扣蜻。 傳聞我的和親對象是個殘疾皇子逆巍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評論 2 355

推薦閱讀更多精彩內(nèi)容