事務與連接池

一、事務

事務就是一個事情凿宾,組成這個事情可能有多個單元矾屯,要求這些單元,要么全都成功菌湃,要么全都不成功问拘。
在開發(fā)中,有事務的存在惧所,可以保證數(shù)據(jù)完整性骤坐。

事務的操作

create table account(
   id int primary key auto_increment,
   name varchar(20),
   money double
);

insert into account values(null,'aaa',1000);
insert into account values(null,'bbb',1000);
insert into account values(null,'ccc',1000);
  1. mysql下怎樣操作
    方式1:
    start transaction 開啟事務
    rollback 事務回滾(回滾到最開始位置)
    commit 事務提交(沒有commit就不會修改數(shù)據(jù))
    方式2:
    show variables like '%commit%'; 可以查看當前autocommit值.在mysql數(shù)據(jù)庫中它的默認值是"on",代表自動事務(執(zhí)行任何一條mysql語句都會自動提交事務).
    測試:將autocommit的值設置為off
    1.set autocommit=off 關閉自動事務。
    2.必須手動commit才可以將事務提交下愈。
    注意:mysql默認autocommit=on oracle默認的autocommit=off;
  2. jdbc下怎樣操作
    java.sql.Connection接口中有幾個方法是用于可以操作事務
    1.setAutocommit(boolean flag) 如果flag=false;它就相當于start transaction;
    2.rollBack() 事務回滾
    3.commit() 事務提交
// 隨便拋異常版纽绍,僅限演示,開發(fā)中不這么寫
public class TransactionTest1 {

    public static void main(String[] args) throws SQLException {

        // 修改id=2這個人的money=500;

        String sql = "update account set money=500 where id=2";

        Connection con = JdbcUtils.getConnection();
        con.setAutoCommit(false); //開啟事務势似,相當于  start transaction;

        Statement st = con.createStatement();
        st.executeUpdate(sql);

        //事務回滾
        //con.rollback();

        con.commit(); //事務提交
        st.close();
        con.close();

    }
}
// 開發(fā)中應該這么寫
public class TransactionTest2 {

    public static void main(String[] args) {

        // 修改id=2這個人的money=500;

        String sql = "update account set money=500 where id=2";

        Connection con = null;
        Statement st = null;

        try {
            con = JdbcUtils.getConnection();
            con.setAutoCommit(false); // 開啟事務拌夏,相當于 start transaction;

            st = con.createStatement();
            st.executeUpdate(sql);
        } catch (SQLException e) {
            e.printStackTrace();
            // 事務回滾
             try {
                con.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
        } finally {

            try {
                con.commit(); // 事務提交
                st.close();
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

二、事務特性(重點)

  1. 原子性:事務是一個不可分割的工作單位履因,事務中的操作要么都發(fā)生障簿,要么都不發(fā)生。
  2. 一致性:事務前后數(shù)據(jù)的完整性必須保持一致栅迄。
  3. 隔離性:多個用戶并發(fā)訪問數(shù)據(jù)庫時站故,一個用戶的事務不能被其它用戶的事務所干擾,多個并發(fā)事務之間數(shù)據(jù)要相互隔離。
  4. 持久性:一個事務一旦被提交西篓,它對數(shù)據(jù)庫中數(shù)據(jù)的改變就是永久性的愈腾,接下來即使數(shù)據(jù)庫發(fā)生故障也不應該對其有任何影響。

如果不考慮事務的隔離性岂津,會出現(xiàn)什么問題虱黄?

  1. 臟讀:一個事務讀取到另一個事務的未提交數(shù)據(jù)
  2. 不可重復讀:兩次讀取的數(shù)據(jù)不一致(強調(diào)update)
  3. 虛讀(幻讀):兩次讀取的數(shù)據(jù)不一致(強調(diào)insert)
  4. 丟失更新:兩個事務對同一條記錄進行操作,后提交的事務吮成,將先提交的事務的修改覆蓋了橱乱。

解決方案

  1. 事務的隔離級別有哪些?
    • Serializable:可避免臟讀、不可重復讀赁豆、虛讀情況的發(fā)生仅醇。(串行化)
    • Repeatable read:可避免臟讀、不可重復讀情況的發(fā)生魔种。(可重復讀)不可以避免虛讀
    • Read committed:可避免臟讀情況發(fā)生(讀已提交)
    • Read uncommitted:最低級別析二,以上情況均無法保證。(讀未提交)
  2. 怎樣設置事務的隔離級別?
    1.mysql中設置
1.查看事務隔離級別
    select @@tx_isolation   查詢當前事務隔離級別(默認為Repeatable read).
    擴展:oracle中默認是Read committed

2.mysql中怎樣設置事務隔離級別
    set session transaction isolation level 事務隔離級別

2.jdbc中設置

使用java.sql.Connection接口中提供的方法
    void setTransactionIsolation(int level) throws SQLException
    參數(shù)level可以取以下值:
        level - 以下 Connection 常量之一:
        Connection.TRANSACTION_READ_UNCOMMITTED节预、
        Connection.TRANSACTION_READ_COMMITTED叶摄、
        Connection.TRANSACTION_REPEATABLE_READ
        Connection.TRANSACTION_SERIALIZABLE。
        (注意安拟,不能使用 Connection.TRANSACTION_NONE蛤吓,因為它指定了不受支持的事務。)
  1. 演示
1.臟讀
    一個事務讀取到另一個事務的為提交數(shù)據(jù)
    設置A,B事務隔離級別為   Read uncommitted

    set session transaction isolation level  read uncommitted;

    1.在A事務中
        start transaction;
        update account set money=money-500 where name='aaa';
        update account set money=money+500 where name='bbb';

    2.在B事務中
        start transaction;
        select * from account;

    這時糠赦,B事務讀取時会傲,會發(fā)現(xiàn),錢已經(jīng)匯完拙泽。那么就出現(xiàn)了臟讀淌山。

    當A事務提交前,執(zhí)行rollback顾瞻,在commit泼疑, B事務在查詢,就會發(fā)現(xiàn)荷荤,錢恢復成原樣
    也出現(xiàn)了兩次查詢結果不一致問題退渗,出現(xiàn)了不可重復讀.

2.解決臟讀問題
    將事務的隔離級別設置為 read committed來解決臟讀

    設置A,B事務隔離級別為   Read committed

    set session transaction isolation level  read committed;

    1.在A事務中
        start transaction;
        update account set money=money-500 where name='aaa';
        update account set money=money+500 where name='bbb';

    2.在B事務中
        start transaction;
        select * from account;

    這時B事務中,讀取信息時蕴纳,是不能讀到A事務未提交的數(shù)據(jù)的会油,也就解決了臟讀。

    讓A事務古毛,提交數(shù)據(jù) commit;

    這時翻翩,在查詢,這次結果與上一次查詢結果又不一樣了,還存在不可重復讀体斩。

3.解決不可重復讀
    將事務的隔離級別設置為Repeatable read來解決不可重復讀。
    設置A,B事務隔離級別為   Repeatable read;
    set session transaction isolation level  Repeatable read;

    1.在A事務中
            start transaction;
            update account set money=money-500 where name='aaa';
            update account set money=money+500 where name='bbb';

    2.在B事務中
            start transaction;
            select * from account;
    當A事務提交后commit;B事務在查詢颖低,與上次查詢結果一致絮吵,解決了不可重復讀。

4.設置事務隔離級別Serializable ,它可以解決所有問題
    set session transaction isolation level Serializable;

    如果設置成這種隔離級別忱屑,那么會出現(xiàn)鎖表蹬敲。也就是說,一個事務在對表進行操作時莺戒,
    其它事務操作不了伴嗡。
  1. 總結
臟讀:一個事務讀取到另一個事務為提交數(shù)據(jù)
不可重復讀:兩次讀取數(shù)據(jù)不一致(讀提交數(shù)據(jù))---update
虛讀:兩次讀取數(shù)據(jù)不一致(讀提交數(shù)據(jù))----insert

事務隔離級別:
    read uncommitted 什么問題也解決不了.
    read committed 可以解決臟讀,其它解決不了.
    Repeatable read 可以解決臟讀从铲,可以解決不可重復讀,不能解決虛讀.
    Serializable 它會鎖表瘪校,可以解決所有問題.

    安全性:serializable > repeatable read > read committed > read uncommitted
    性能 :serializable < repeatable read < read committed < read uncommitted

    結論: 實際開發(fā)中,通常不會選擇 serializable 和 read uncommitted 名段,
    mysql默認隔離級別 repeatable read 阱扬,oracle默認隔離級別 read committed

三、丟失更新

多個事務對同一條記錄進行了操作伸辟,后提交的事務將先提交的事務操作覆蓋了麻惶。
解決辦法:

  1. 悲觀鎖:(假設丟失更新一定會發(fā)生 ) ----- 利用數(shù)據(jù)庫內(nèi)部鎖機制,管理事務
    提供的鎖機制
    1.共享鎖
    select * from table lock in share mode(讀鎖信夫、共享鎖)
    2.排它鎖
    select * from table for update (寫鎖窃蹋、排它鎖)
    update語句默認添加排它鎖
  2. 樂觀鎖:(假設丟失更新不會發(fā)生) ----- 采用程序中添加版本字段解決丟失更新問題

解決丟失更新:在數(shù)據(jù)表添加版本字段,每次修改過記錄后静稻,版本字段都會更新警没,如果讀取是版本字段,與修改時版本字段不一致姊扔,說明別人進行修改過數(shù)據(jù) (重改)

四惠奸、連接池

就是創(chuàng)建一個容器,用于裝入多個Connection對象恰梢,在使用連接對象時佛南,從容器中獲取一個Connection,使用完成后嵌言,在將這個Connection重新裝入到容器中嗅回。這個容器就是連接池。(DataSource)也叫做數(shù)據(jù)源.
我們可以通過連接池獲取連接對象.
優(yōu)點:節(jié)省創(chuàng)建連接與釋放連接 性能消耗 ---- 連接池中連接起到復用的作用 摧茴,提高程序性能

自定義連接池

  1. 創(chuàng)建一個MyDataSource類绵载,在這個類中創(chuàng)建一個LinkedList<Connection>
private LinkedList<Connection> ll;
ll = new LinkedList<Connection>();
  1. 在其構造方法中初始化List集合,并向其中裝入5個Connection對象
for (int i = 0; i < 5; i++) {
    Connection con = JdbcUtils.getConnection();
    ll.add(con);
}
  1. 創(chuàng)建一個public Connection getConnection() 從List集合中獲取一個連接對象返回.

  2. 創(chuàng)建一個public void readd(Connection) 這個方法是將使用完成后的Connection對象重新裝入到List集合中.

代碼問題

1.連接池的創(chuàng)建是有標準的.
    在javax.sql包下定義了一個接口 DataSource          
    簡單說,所有的連接池必須實現(xiàn)javax.sql.DataSource接口娃豹,

    我們的自定義連接池必須實現(xiàn)DataSource接口焚虱。

2.我們操作時,要使用標準懂版,怎樣可以讓 con.close()它不是銷毀鹃栽,而是將其重新裝入到連接池.

    要解決這個問題,其本質(zhì)就是將Connection中的close()方法的行為改變躯畴。

    怎樣可以改變一個方法的行為(對方法功能進行增強)
        1.繼承
        2.裝飾模式
            1.裝飾類與被裝飾類要實現(xiàn)同一個接口或繼承同一個父類
            2.在裝飾類中持有一個被裝飾類引用
            3.對方法進行功能增強民鼓。
        3.動態(tài)代理
            可以對行為增強
            Proxy.newProxyInstance(ClassLoacer ,Class[],InvocationHandler);

    結論:Connection對象如果是從連接池中獲取到的,那么它的close方法的行為已經(jīng)改變了蓬抄,不在是銷毀丰嘉,而是重新裝入到連接池。

方法增強

  1. 繼承增強(不好)

    public class Demo1 {
        public static void main(String[] args) {
            Person1 p=new Student1();
            p.eat();
        }
    }
    
    class Person1 {
        public void eat(){
            System.out.println("吃兩個饅頭");
        }
    }
    
    class Student1 extends Person1 {
        public void eat(){
            super.eat();
            System.out.println("加兩個雞腿");
        }
    }
    
  2. 裝飾模式(不好)

  3. 動態(tài)代理

    import javax.sql.DataSource;
    
    public class MyDataSource implements DataSource {
        private LinkedList<Connection> ll; // 用于裝Connection對象的容器嚷缭。
    
        public MyDataSource() throws SQLException {
            ll = new LinkedList<Connection>();
            // 當創(chuàng)建MyDateSource對象時饮亏,會向ll中裝入5個Connection對象。
            for (int i = 0; i < 5; i++) {
                Connection con = JdbcUtils.getConnection();
                ll.add(con);
            }
        }
    
        public Connection getConnection() throws SQLException {
            if (ll.isEmpty()) {
                for (int i = 0; i < 3; i++) {
                    Connection con = JdbcUtils.getConnection();
                    ll.add(con);
                }
            }
    
            final Connection con = ll.removeFirst();
    
            Connection proxyCon = (Connection) Proxy.newProxyInstance(con.getClass().getClassLoader(), con.getClass().getInterfaces(), new InvocationHandler() {
                        public Object invoke(Object proxy, Method method,
                                Object[] args) throws Throwable {
                            if ("close".equals(method.getName())) {
                                // 這代表是close方法阅爽,它要做的事情是將con對象重新裝入到集合中.
                                ll.add(con);
                                System.out.println("重新將連接對象裝入到集合中");
                                return null;
                            } else {
                                return method.invoke(con, args);// 其它方法執(zhí)行原來操作
                            }
                        }
                    });
            return proxyCon;
        }
    }
    

五克滴、dbcp連接池(了解)

導入兩個jar包:commons-dbcp-1.4.jarcommons-pool-1.5.6.jar

  1. 手動配置(手動編碼)
    BasicDataSource bds = new BasicDataSource();
    
    // 需要設置連接數(shù)據(jù)庫最基本四個條件
    bds.setDriverClassName("com.mysql.jdbc.Driver");
    bds.setUrl("jdbc:mysql:///day18");
    bds.setUsername("root");
    bds.setPassword("abc");
    
    // 得到一個Connection
    Connection con = bds.getConnection();
    

示例:
```java
public class JdbcDemo{
public void test() throws Exception {
BasicDataSource bds = new BasicDataSource();

        // 需要設置連接數(shù)據(jù)庫最基本四個條件
        bds.setDriverClassName("com.mysql.jdbc.Driver");
        bds.setUrl("jdbc:mysql:///day18");
        bds.setUsername("root");
        bds.setPassword("123");

        Connection con = bds.getConnection();

        ResultSet rs = con.createStatement().executeQuery("select * from account");

        while(rs.next()){
            System.out.println(rs.getInt("id")+" "+rs.getString("name"));
        }

        rs.close();
        con.close();
    }

    public static void main(String[] args) throws Exception {
        JdbcDemo jd = new JdbcDemo();
        jd.test();
    }
}
```
  1. 自動配置(使用配置文件)
    Properties props = new Properties();
    FileInputStream fis = new FileInputStream("D:\\java1110\\workspace\\day18_2\\src\\dbcp.properties");
    props.load(fis);
    
    DataSource ds = BasicDataSourceFactory.createDataSource(props);
    
    示例:
    dbcp.properties
    driverClassName=com.mysql.jdbc.Driver
    url=jdbc:mysql:///day18
    username=root
    password=123
    
    JdbcDemo.java
    import org.apache.commons.dbcp2.BasicDataSource;
    import org.apache.commons.dbcp2.BasicDataSourceFactory;
    import java.sql.*;
    import javax.sql.DataSource;
    import java.io.FileInputStream;
    import java.util.Properties;
    
    public class JdbcDemo {
        public void test2() throws Exception {
            Properties props = new Properties();
            // props.setProperty("driverClassName","com.mysql.jdbc.Driver");
            // props.setProperty("url","jdbc:mysql:///day18");
            // props.setProperty("username","root");
            // props.setProperty("password","123");
            FileInputStream fis = new FileInputStream("D:\\code\\java\\JDBC\\src\\dbcp.properties");
            props.load(fis);
    
            DataSource ds = BasicDataSourceFactory.createDataSource(props);
            Connection con = ds.getConnection();
    
            ResultSet rs = con.createStatement().executeQuery("select * from account");
    
            while(rs.next()){
                System.out.println(rs.getInt("id")+" "+rs.getString("name"));
            }
    
            rs.close();
            con.close();
        }
    
        public static void main(String[] args) throws SQLException {
            JdbcDemo jd = new JdbcDemo();
            jd.test2();
        }
    }
    

六、c3p0連接池(必須掌握)

C3P0是一個開源的JDBC連接池优床,它實現(xiàn)了數(shù)據(jù)源和JNDI綁定劝赔,支持JDBC3規(guī)范和JDBC2的標準擴展。目前使用它的開源項目有Hibernate胆敞,Spring等着帽。
dbcp沒有自動回收空閑連接的功能,c3p0有自動回收空閑連接功能移层,它的性能更強大仍翰。

導入包:c3p0-0.9.5.2.jar

  1. 手動
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass("com.mysql.jdbc.Driver");
cpds.setJdbcUrl("jdbc:mysql:///day18");
cpds.setUser("root");
cpds.setPassword("abc");

事例:

public void test() throws Exception {
    BasicDataSource bds = new BasicDataSource();

    ComboPooledDataSource cpds = new ComboPooledDataSource();
    cpds.setDriverClass("com.mysql.jdbc.Driver");
    cpds.setJdbcUrl("jdbc:mysql:///day18");
    cpds.setUser("root");
    cpds.setPassword("123");

    Connection con = cpds.getConnection();

    ResultSet rs = con.createStatement().executeQuery("select * from account");

    while(rs.next()){
        System.out.println(rs.getInt("id")+" "+rs.getString("name"));
    }

    rs.close();
    con.close();
}
  1. 自動(使用配置文件)
    c3p0的配置文件可以是properties也可以是xml.
    c3p0的配置文件如果名稱叫做 c3p0.properties or c3p0-config.xml 并且放置在classpath路徑下(對于web應用就是classes目錄),那么c3p0會自動查找观话。
    注意:我們其時只需要將配置文件放置在src下就可以予借。

    使用:ComboPooledDataSource cpds = new ComboPooledDataSource(); 它會在指定的目錄下查找指定名稱的配置文件,并將其中內(nèi)容加載频蛔。

    c3p0-config.xml

    <?xml version="1.0" encoding="UTF-8"?>
    
    <c3p0-config>
        <default-config>
            <property name="driverClass">com.mysql.jdbc.Driver</property>
            <property name="jdbcUrl">jdbc:mysql:///day18</property>
            <property name="user">root</property>
            <property name="password">123</property>
        </default-config>
    </c3p0-config>
    
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末灵迫,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子晦溪,更是在濱河造成了極大的恐慌瀑粥,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件三圆,死亡現(xiàn)場離奇詭異狞换,居然都是意外死亡避咆,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進店門修噪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來查库,“玉大人,你說我怎么就攤上這事黄琼∨虮ǎ” “怎么了?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵适荣,是天一觀的道長。 經(jīng)常有香客問我院领,道長弛矛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任比然,我火速辦了婚禮丈氓,結果婚禮上,老公的妹妹穿的比我還像新娘强法。我一直安慰自己万俗,他們只是感情好,可當我...
    茶點故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布饮怯。 她就那樣靜靜地躺著闰歪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蓖墅。 梳的紋絲不亂的頭發(fā)上库倘,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天,我揣著相機與錄音论矾,去河邊找鬼教翩。 笑死,一個胖子當著我的面吹牛贪壳,可吹牛的內(nèi)容都是我干的饱亿。 我是一名探鬼主播,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼闰靴,長吁一口氣:“原來是場噩夢啊……” “哼彪笼!你這毒婦竟也來了?” 一聲冷哼從身側響起蚂且,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤杰扫,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后膘掰,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體章姓,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡佳遣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了凡伊。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片零渐。...
    茶點故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖系忙,靈堂內(nèi)的尸體忽然破棺而出诵盼,到底是詐尸還是另有隱情,我是刑警寧澤银还,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布风宁,位于F島的核電站,受9級特大地震影響蛹疯,放射性物質(zhì)發(fā)生泄漏戒财。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一捺弦、第九天 我趴在偏房一處隱蔽的房頂上張望饮寞。 院中可真熱鬧,春花似錦列吼、人聲如沸幽崩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽慌申。三九已至,卻和暖如春理郑,著一層夾襖步出監(jiān)牢的瞬間太示,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工香浩, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留类缤,地道東北人。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓邻吭,卻偏偏與公主長得像餐弱,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子囱晴,可洞房花燭夜當晚...
    茶點故事閱讀 45,573評論 2 359

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

  • 問題:事務是什么膏蚓,有什么用? 事務就是一個事情,組成這個事情可能有多個單元畸写,要求這些單元驮瞧,要么全都成功,要么全都不...
    yeller閱讀 747評論 0 0
  • 1. Java基礎部分 基礎部分的順序:基本語法枯芬,類相關的語法论笔,內(nèi)部類的語法采郎,繼承相關的語法,異常的語法狂魔,線程的語...
    子非魚_t_閱讀 31,664評論 18 399
  • JDBC概述 在Java中蒜埋,數(shù)據(jù)庫存取技術可分為如下幾類:JDBC直接訪問數(shù)據(jù)庫、JDO技術最楷、第三方O/R工具整份,如...
    usopp閱讀 3,543評論 3 75
  • 1.事務 1.1事務的四大特性(ACID) 原子性:事務中的所有操作要么全部執(zhí)行成功,要么執(zhí)行全部失敗籽孙。 一致性:...
    joshul閱讀 435評論 0 1
  • 自從開學以來烈评,一個星期沒有寫東西了呢,也不是因為忙犯建,還是因為自己太懶了吧讲冠,寧愿扣手機也不愿去讀讀書呀看看報,胎挎, 剛...
    ZScissors閱讀 155評論 0 0