日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

大数据WEB阶段(七)JDBC、数据库批处理、数据库连接池

發布時間:2024/4/30 数据库 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 大数据WEB阶段(七)JDBC、数据库批处理、数据库连接池 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

JDBC

一、概述

  • 數據庫驅動: 數據庫廠商提供的用來操作數據庫的jar包

    • JDBC簡介
    • 由于各大數據庫廠商提供的數據庫驅動各不相同, 導致了開發人員的學習成本十分的高. SUN公司為了簡化數據庫的操作, 提供了一套規范, 本質上就是一大堆的接口, 要求各大數據庫廠商在提供驅動時都要實現JDBC這套接口, 實現之后, 只要學會JDBC這套接口, 所有的數據庫驅動就都會使用了!
    • 六個步驟實現JDBC程序
    • 在開發之前 , 首先要導入jar包

      Connection conn = null; Statement stat = null; ResultSet rs = null; try {//1.注冊數據庫驅動Class.forName("com.mysql.jdbc.Driver");//2.獲取數據庫連接conn = DriverManager.getConnection("jdbc:mysql:///mydb5", "root", "root");//3.獲取傳輸器stat = conn.createStatement();//4.利用傳輸器,發送sql到數據庫執行,返回執行結果rs = stat.executeQuery("select * from account");//5.處理結果while(rs.next()){int id = rs.getInt(1);String name = rs.getString("name");double money = rs.getDouble("money");System.out.println(id+name+money);} } catch (Exception e) {e.printStackTrace();throw new RuntimeException(); }finally{//6.釋放資源if(rs!=null){try {rs.close();} catch (SQLException e) {e.printStackTrace();}finally{rs = null;}}if(stat!=null){try {stat.close();} catch (SQLException e) {e.printStackTrace();}finally{stat = null;}}if(conn!=null){try {conn.close();} catch (SQLException e) {e.printStackTrace();}finally{conn = null;}} }
    • JDBC API詳解
    • 注冊數據庫驅動
      • 使用DriverManager.registerDriver(new Driver());注冊數據庫有兩個缺點,首先,通過觀察mysql的中Driver接口的實現類發現在靜態代碼塊中注冊驅動的邏輯,所以這種方式會造成驅動被注冊兩次。另外,這種方式導致了程序和具體的數據庫驅動綁死在了一起,程序的靈活性比較低。
      • 所以推薦使用:Class.forName(“com.mysql.jdbc.Driver”);的方式注冊數據庫驅動。
      • 獲取數據庫連接 conn = DriverManager.getConnection(url,name,password);
    • 數據庫URL

      • URL用于標識數據庫的位置,程序員通過URL地址告訴JDBC程序連接哪個數據庫,URL的寫法為:

        jdbc:mysql://localhost:3306/test ?參數名=參數值
      • 常用數據庫URL地址的寫法:

        Oracle寫法:jdbc:oracle:thin:@localhost:1521:sid SqlServer寫法:jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=sid MySql:jdbc:mysql://localhost:3306/sid Mysql的url地址的簡寫形式: jdbc:mysql:///sid
    • Connection

      • Jdbc程序中的Connection,它用于代表數據庫的鏈接,Connection是數據庫編程中最重要的一個對象,客戶端與數據庫所有交互都是通過connection對象完成的,這個對象的常用方法:

        createStatement():創建向數據庫發送sql的statement對象。 prepareStatement(sql):創建向數據庫發送預編譯sql的PreparedSatement對象。 prepareCall(sql):創建執行存儲過程的callableStatement對象。 setAutoCommit(boolean?autoCommit):設置事務是否自動提交。 commit():在鏈接上提交事務。 rollback():在此鏈接上回滾事務。
    • Statement

      • Jdbc程序中的Statement對象用于向數據庫發送SQL語句, Statement對象常用方法:

        executeQuery(String?sql) :用于向數據庫發送查詢語句。 executeUpdate(String?sql):用于向數據庫發送insert、update或delete語句 execute(String sql):用于向數據庫發送任意sql語句 addBatch(String?sql):把多條sql語句放到一個批處理中。 executeBatch():向數據庫發送一批sql語句執行。
    • ResultSet

      • Jdbc程序中的ResultSet用于代表Sql語句的執行結果。Resultset封裝執行結果時,采用的類似于表格的方式。ResultSet 對象維護了一個指向表格數據行的游標,初始的時候,游標在第一行之前,調用ResultSet.next() 方法,可以使游標指向具體的數據行,進行調用方法獲取該行的數據。
      • ResultSet既然用于封裝執行結果的,所以該對象提供的都是用于獲取數據的get方法:

        獲取任意類型的數據 getObject(int index) getObject(string columnName) 獲取指定類型的數據,例如: getString(int index) getString(String columnName) getInt(columnIndex) getInt(columnLabel) getDouble(columnIndex) getDouble(columnLabel) ... 操作游標的方法,例如: next():移動到下一行 Previous():移動到前一行 absolute(int row):移動到指定行 beforeFirst():移動resultSet的最前面。 afterLast() :移動到resultSet的最后面。 ...
    • 釋放資源
      • Jdbc程序運行完后,切記要釋放程序在運行過程中,創建的那些與數據庫進行交互的對象,這些對象通常是ResultSet, Statement和Connection對象。
      • 特別是Connection對象,它是非常稀有的資源,用完后必須馬上釋放,如果Connection不能及時、正確的關閉,極易導致系統宕機。Connection的使用原則是盡量晚創建,盡量早的釋放。
      • 為確保資源釋放代碼能運行,資源釋放代碼也一定要放在finally語句中。
    • JDBC的增刪改查
    • //jdbc 增 public class Demo_01 {public static void main(String[] args) {Connection conn = null;Statement stat = null;ResultSet rs = null;try {//注冊驅動Class.forName("com.mysql.jdbc.Driver");//獲取連接conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/cszj","root","root");//獲取傳輸器stat = conn.createStatement();//通過傳輸器將sql語句傳輸到mysql中int rows = stat.executeUpdate("insert into user values(7 ,'劉備','1234565')");//結果System.out.println("該操作影響了:"+rows+"行");} catch (Exception e) {e.printStackTrace();}finally{if(rs !=null){try {rs.close();} catch (SQLException e) {e.printStackTrace();}finally{rs = null;}}if(stat !=null){try {stat.close();} catch (SQLException e) {e.printStackTrace();}finally{stat = null;}}if(conn !=null){try {conn.close();} catch (SQLException e) {e.printStackTrace();}finally{conn = null;}}}} }
    • //jdbc 刪 public class Demo_01 {public static void main(String[] args) {Connection conn = null;Statement stat = null;ResultSet rs = null;try {//注冊驅動Class.forName("com.mysql.jdbc.Driver");//獲取連接conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/cszj","root","root");//獲取傳輸器stat = conn.createStatement();//通過傳輸器將sql語句傳輸到mysql中int rows = stat.executeUpdate("delete from user where id = 5");//結果System.out.println("該操作影響了:"+rows+"行");} catch (Exception e) {e.printStackTrace();}finally{if(rs !=null){try {rs.close();} catch (SQLException e) {e.printStackTrace();}finally{rs = null;}}if(stat !=null){try {stat.close();} catch (SQLException e) {e.printStackTrace();}finally{stat = null;}}if(conn !=null){try {conn.close();} catch (SQLException e) {e.printStackTrace();}finally{conn = null;}}}} }
    • //jdbc 改 public class Demo_01 {public static void main(String[] args) {Connection conn = null;Statement stat = null;ResultSet rs = null;try {//注冊驅動Class.forName("com.mysql.jdbc.Driver");//獲取連接conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/cszj","root","root");//獲取傳輸器stat = conn.createStatement();//通過傳輸器將sql語句傳輸到mysql中int rows = stat.executeUpdate("update user set id = 5 where user='奧特曼'");//結果System.out.println("該操作影響了:"+rows+"行");} catch (Exception e) {e.printStackTrace();}finally{if(rs !=null){try {rs.close();} catch (SQLException e) {e.printStackTrace();}finally{rs = null;}}if(stat !=null){try {stat.close();} catch (SQLException e) {e.printStackTrace();}finally{stat = null;}}if(conn !=null){try {conn.close();} catch (SQLException e) {e.printStackTrace();}finally{conn = null;}}}} }
    • //jdbc 查 public class Demo_01 {public static void main(String[] args) {Connection conn = null;Statement stat = null;ResultSet rs = null;try {//注冊驅動Class.forName("com.mysql.jdbc.Driver");//獲取連接conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/cszj","root","root");//獲取傳輸器stat = conn.createStatement();//通過傳輸器將sql語句傳輸到mysql中rs = stat.executeQuery("select * from user");//結果while(rs.next()){int id = rs.getInt("id");String user = rs.getString("user");String password = rs.getString("password");System.out.println(id+":"+user+":"+password);}} catch (Exception e) {e.printStackTrace();}finally{if(rs !=null){try {rs.close();} catch (SQLException e) {e.printStackTrace();}finally{rs = null;}}if(stat !=null){try {stat.close();} catch (SQLException e) {e.printStackTrace();}finally{stat = null;}}if(conn !=null){try {conn.close();} catch (SQLException e) {e.printStackTrace();}finally{conn = null;}}}} }
    • 自定義JDBCUtils 工具類

    • 創建配置文件 config.properties

      driverClass=com.mysql.jdbc.Driver jdbcUrl=jdbc:mysql://localhost:3306/cszj user=root password=root
    • 創建數據庫工具類

      /*** 數據庫工具類* */ public class JDBCUtils {//只在類被加載時讀取一次配置文件private static Properties prop = new Properties();static{try {String path = JDBCUtils.class.getClassLoader().getResource("config.properties").getPath();prop.load(new FileInputStream(path));} catch (Exception e) {e.printStackTrace();} }//獲取連接public static Connection getConnection(){String driverClass = prop.getProperty("driverClass");String jdbcUrl = prop.getProperty("jdbcUrl");String user = prop.getProperty("user");String password = prop.getProperty("password");Connection conn = null;try {//注冊驅動Class.forName(driverClass);//獲取連接conn = DriverManager.getConnection(jdbcUrl,user,password);} catch (Exception e) {e.printStackTrace();}return conn;}//釋放資源public static void close(Connection conn , Statement stat, ResultSet rs){if(rs !=null){try {rs.close();} catch (SQLException e) {e.printStackTrace();}finally{rs =null ;}}if(stat !=null){try {stat.close();} catch (SQLException e) {e.printStackTrace();}finally{stat =null ;}}if(conn !=null){try {conn.close();} catch (SQLException e) {e.printStackTrace();}finally{conn =null ;}} } }
    • 修改數據路連接代碼

      //jdbc 查 public class Demo_01 {public static void main(String[] args) {Connection conn = null;Statement stat = null;ResultSet rs = null;try {//獲取連接conn = JDBCUtils.getConnection();//獲取傳輸器stat = conn.createStatement();//通過傳輸器將sql語句傳輸到mysql中rs = stat.executeQuery("select * from user");//結果while(rs.next()){int id = rs.getInt("id");String user = rs.getString("user");String password = rs.getString("password");System.out.println(id+":"+user+":"+password);}} catch (Exception e) {e.printStackTrace();}finally{JDBCUtils.close(conn, stat, rs);}} }
  • PreparedStatement

  • 注入攻擊演示

    public static void main(String[] args) {Scanner scan = new Scanner(System.in);//提示用戶登陸System.out.println("請登陸");//提示輸入用戶名System.out.println("請輸入用戶名:");String username = scan.nextLine();//提示輸入密碼System.out.println("請輸入密碼:");String password = scan.nextLine();login(username,password);}private static void login(String username, String password) {Connection conn = null;Statement stat = null;ResultSet rs = null;try {conn = JDBCUtils.getConnection();stat = conn.createStatement();String sql = "select * from user where username='"+username+"' and password='"+password+"'";//當輸入“張三'#”后,sql變為://select * from user where username='張三'#' and password=''//當輸入“張三' or '2=2”后,sql變為://select * from user where username='張三' or '2=2' and password=''rs = stat.executeQuery(sql);if(rs.next()){System.out.println("恭喜,登陸成功");}else{System.out.println("用戶名或密碼錯誤");}} catch (Exception e) {e.printStackTrace();throw new RuntimeException();}finally{JDBCUtils.close(conn, stat, rs);}}
  • 由于后臺的SQL是拼接而來的, 其中的參數是用戶提交的, 如果用戶在提交參數時, 參雜了一些SQL關鍵字或者特殊符號, 就有可能會導致SQL語句語意的改變, 從而造成一些意外的操作!
  • Preparedment

  • 優點:

  • 可以防止SQL注入攻擊
  • 通過PreparedStatement對象發送sql, 是先把sql語句的骨架發送給數據庫編譯并確定下來, 后面發送的只能是參數的值, 不能影響sql語句的骨架, 即使參數中包含sql關鍵字或特殊符號, 也只會當成普通的文本來處理!
  • 通過 方法來設置參數 , 省去了拼接sql語句的麻煩
  • 可以提高程序運行的效率

  • PreparedStatement對象發送的sql語句(骨架)到數據庫編譯后會被數據緩存下來, 如果下次執行的sql與緩存中的相匹配, 就不再編譯而是直接使用緩存中的語句, 可以減少sql語句編譯的次數, 提高程序執行的效率!

  • Statement對象發送的sql語句到數據庫之后也會編譯, 但是Statement對象是先拼接好再發送sql到數據庫, 如果每次參數不同, 整條sql也就不同. 所以每次都需要編譯!

  • 代碼改造

    //jdbc 查 public class Demo_01 {public static void main(String[] args) {Connection conn = null;PreparedStatement ps = null;ResultSet rs = null;try {//獲取連接conn = JDBCUtils.getConnection();//獲取傳輸器ps = conn.prepareStatement(" select * from user");//通過傳輸器將sql語句傳輸到mysql中rs = ps.executeQuery();//結果while(rs.next()){int id = rs.getInt("id");String user = rs.getString("user");String password = rs.getString("password");System.out.println(id+":"+user+":"+password);}} catch (Exception e) {e.printStackTrace();}finally{JDBCUtils.close(conn, ps, rs);}} }
  • 二、批處理

  • 批處理概述
  • 假設現有一大堆的sql要到數據庫執行, 如果一條一條發送, 有多少條就 需要發送多少次, 效率低下
  • 可以通過批處理提高發送sql語句的效率: 可以將這一大堆的sql添加到 一個批中, 一次性將批發送給數據庫, 數據庫收到后打開批, 依次執行其中 sql語句, 這樣可以減少sql語句發送的次數, 從而提高程序執行的效率!
  • Statement方式實現批處理

  • 優點: 可以再一次批處理中添加結構不同的語句
  • 缺點:
  • 不能防止sql注入
  • 沒有預編譯機制 , 效率賊低
  • 當發送結構相同的sql語句時 , sql語句的骨架每次都需要編譯 。
  • 案例:

    //批處理 public class Demo_02 {public static void main(String[] args) {Connection conn = null;Statement stat = null;ResultSet rs = null;try{//獲取連接conn = JDBCUtils.getConnection();//獲取傳輸器stat = conn.createStatement();stat.addBatch("use cszj;");stat.addBatch("create if not exists table tb_batch(id int primary key auto_increment, name varchar(20))");stat.addBatch("insert into tb_batch values(null,'a')");stat.addBatch("insert into tb_batch values(null,'b')");stat.addBatch("insert into tb_batch values(null,'c')");stat.addBatch("insert into tb_batch values(null,'d')");stat.executeBatch();System.out.println("執行完成");}catch(Exception e){e.printStackTrace();}finally{JDBCUtils.close(conn, stat, rs);}} }
  • PreparedStatement方式實現批處理

  • 優點:
  • 可以防止sql注入攻擊
  • 采用預編譯機制 , 可以提高程序執行效率
  • 當發送多條結構相同的sql時 , sql骨架只發送編譯一次
  • 缺點:
  • 不能在一次批處理中添加不同結構的語句
  • 案例:

    //批處理 public class Demo_02 {public static void main(String[] args) {Connection conn = null;PreparedStatement ps = null;ResultSet rs = null;try{//獲取連接conn = JDBCUtils.getConnection();//獲取傳輸器ps = conn.prepareStatement("insert into tb_batch values(null ,? )");for( int i = 0;i<10;i++){ps.setString(1, "a"+i);ps.addBatch();}ps.executeBatch();System.out.println("執行完成");}catch(Exception e){e.printStackTrace();}finally{JDBCUtils.close(conn, ps, rs);}} }
  • 三、數據庫連接池

  • 概述
  • 用戶每次請求都需要向數據庫獲得鏈接,而數據庫創建連接通常需要消耗相對較大的資源,創建時間也較長。假設網站一天10萬訪問量,數據庫服務器就需要創建10萬次連接,極大的浪費數據庫的資源,并且極易造成數據庫服務器內存溢出、宕機。
  • 頻繁的開關連接相當的耗費資源,所以我們可以設置一個連接池,在程序啟動時就初始化一批連接,在程序中共享,需要連接時從池中獲取,用完再還回池中,通過池共享連接,減少開關連接的次數,提高程序的效率。
  • 自定義一個簡易的數據庫連接池

    import java.io.PrintWriter; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.util.LinkedList; import java.util.List; import java.util.logging.Logger; import javax.sql.DataSource;public class MyPool implements DataSource{private static List<Connection> list = new LinkedList<Connection>();static{try {Class.forName("com.mysql.jdbc.Driver");for (int i = 0; i < 5; i++) {Connection conn = DriverManager.getConnection("jdbc:mysql:///mydb5","root","root");list.add(conn);}} catch (Exception e) {e.printStackTrace();throw new RuntimeException();}}@Overridepublic Connection getConnection() throws SQLException {if(list.isEmpty()){for (int i = 0; i < 3; i++) {Connection conn = DriverManager.getConnection("jdbc:mysql:///mydb1");list.add(conn);}}Connection conn = list.remove(0);System.out.println("成功從數據庫中獲取一個連接,連接池中還剩"+list.size()+"個連接..");return conn;}public void returnConn(Connection conn){try {if(conn != null && !conn.isClosed()){list.add(conn);System.out.println("成功向數據庫還回一個連接,連接池中還剩"+list.size()+"個連接..");}} catch (SQLException e) {e.printStackTrace();throw new RuntimeException();}}@Overridepublic PrintWriter getLogWriter() throws SQLException {// TODO Auto-generated method stubreturn null;}@Overridepublic void setLogWriter(PrintWriter out) throws SQLException {// TODO Auto-generated method stub}@Overridepublic void setLoginTimeout(int seconds) throws SQLException {// TODO Auto-generated method stub}@Overridepublic int getLoginTimeout() throws SQLException {// TODO Auto-generated method stubreturn 0;}@Overridepublic Logger getParentLogger() throws SQLFeatureNotSupportedException {// TODO Auto-generated method stubreturn null;}@Overridepublic <T> T unwrap(Class<T> iface) throws SQLException {// TODO Auto-generated method stubreturn null;}@Overridepublic boolean isWrapperFor(Class<?> iface) throws SQLException {// TODO Auto-generated method stubreturn false;}@Overridepublic Connection getConnection(String username, String password)throws SQLException {// TODO Auto-generated method stubreturn null;} }測試自定義連接池import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement;public class TestMyPool {public static void main(String[] args) {MyPool pool = new MyPool();Connection conn= null;Statement stat = null;ResultSet rs = null;try {conn = pool.getConnection();stat = conn.createStatement();rs = stat.executeQuery("select * from account where id=1");if(rs.next()){System.out.println(rs.getInt("id")+":"+rs.getString("name")+":"+rs.getDouble("money"));}System.out.println("執行完畢");} catch (Exception e) {e.printStackTrace();throw new RuntimeException();}finally{if(rs!=null){try {rs.close();} catch (SQLException e) {e.printStackTrace();}finally{rs = null;}}if(stat!=null){try {stat.close();} catch (SQLException e) {e.printStackTrace();}finally{stat = null;}}pool.returnConn(conn);}} }
  • 如上代碼寫的連接池,還需要在使用完連接后記得不能關閉連接,而是要調用returnConn方法將連接還回池中。
  • 我們想能不能想辦法改造conn的close方法,使close方法不會真的關閉連接而是將連接還回池中。
  • 改造close方法

  • 繼承
  • 寫一個類繼承要改造的類,對于不想改造的方法不覆蓋,對于想要改造的方法復寫該方法,將代碼改造為自己需要的邏輯代碼。
  • 這種方式只能在還沒有對象的情況下使用,現在Connection對象已經存在了,再用繼承復寫的方式是不行的,所以我們不采用。
  • 裝飾
  • 實現裝飾設計模式:
  • 寫一個裝飾類, 要求裝飾類和被裝飾者所屬的類實現同一個接口或者繼承同一個父類
  • 裝飾類必須提供構造方法接收被裝飾者, 并將被裝飾者保存在類的內部
  • 對于想要改造的方法直接進行改造, 對于不想改造的方法, 直接調用原有對象(被裝設者)上的方法
  • 案例:

    裝飾者類 public class ConnectionDecorate implements Connection{private Connection conn = null;private MyPool pool = null;public ConnectionDecorate(Connection conn,MyPool pool){this.conn = conn;this.pool = pool;}@Overridepublic void close() throws SQLException {pool.returnConn(conn);}修改MyPool類:在獲取連接方法中,return前加入如下代碼: //先進行包裝Connection connDecorate = new ConnectionDecorate(conn,this);return connDecorate;測試類最后只需關閉連接即可
  • 開源數據庫連接池c3p0

  • 概述
  • 我們手寫的連接池是比較簡陋的,是為了講解連接池的原理。其實在真實開發中可以使用開源的數據庫連接池。其中C3P0是比較常用的一種。
  • c3p0示例

  • 導入c3p0包
  • 創建測試類

    public static void main(String[] args) {Connection conn = null;Statement stat = null;ResultSet rs = null;try {//創建連接池ComboPooledDataSource cpds = new ComboPooledDataSource();//設置連接數據庫的基本信息cpds.setDriverClass("com.mysql.jdbc.Driver");cpds.setJdbcUrl("jdbc:mysql:///mydb5");cpds.setUser("root");cpds.setPassword("root");//從連接池中獲取連接conn = cpds.getConnection();stat = conn.createStatement();rs = stat.executeQuery("select * from account where id=1");if(rs.next()){System.out.println(rs.getInt("id")+" : "+rs.getString("name")+" : "+rs.getDouble("money"));}} catch (Exception e) {e.printStackTrace();throw new RuntimeException();}finally{JDBCUtils.close(conn, stat, rs);} }
  • 上面的方法是將數據寫死在代碼中,不利于代碼的維護 , 需要對代碼進行改造

  • C3p0會默認讀取一個配置文件,為c3p0-config.xml,(注意,名字不能改變,否則c3p0將無法讀取)我們在src或者類似的源碼目錄下,創建一個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:///mydb5</property><property name="user">root</property><property name="password">root</property></default-config> </c3p0-config>
  • 另一種方式:在src或者類似的源代碼目錄下, 創建一個c3p0.properties文件 , 配置文件內容如下:

    c3p0.driverClass=com.mysql.jdbc.Driver c3p0.jdbcUrl=jdbc:mysql:///mydb5 c3p0.user=root c3p0.password=root
  • 總結

    以上是生活随笔為你收集整理的大数据WEB阶段(七)JDBC、数据库批处理、数据库连接池的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。