hibernate 總結


三、Hibernate核心API(理解)
    Configuration類:
        Configuration對象用於配置和啓動Hibernate。Hibernate應用通過Configuration實例來指定對象-關係映射文
        件的位置或者動態配置Hibernate的屬性,然後創建SessionFactory實例。
    SessionFactory接口:
        一個SessionFactory實例對應一個數據存儲源。應用從SessionFactory中獲取Session實例。
        1)它是線程安全的,這意味着它的一個實例能夠被應用的多個線程共享。
        2)它是重量級的,這意味着不能隨意創建或者銷燬,一個數據庫只對應一個SessionFactory。
          通常構建SessionFactory是在某對象Bean的靜態初始化代碼塊中進行。
          如果應用只是訪問一個數據庫,只需創建一個SessionFactory實例,並且在應用初始化的時候創建該實例。
          如果應用有同時訪問多個數據庫,則需爲每個數據庫創建一個單獨的SessionFactory。
    Session接口:
        是Hibernate應用最廣泛的接口。它提供了和持久化相關的操作,如添加,刪除,更改,加載和查詢對象。
        1)它是線程不安全的,因此在設計軟件架構時,應儘量避免多個線程共享一個Session實例。
        2)Session實例是輕量級的,這意味着在程序可以經常創建和銷燬Session對象,
          例如爲每個客戶請求分配單獨的Session實例。
          原則:一個線程一個Session;一個事務一個Session。
    Transaction接口:
        是Hibernate的事務處理接口,它對底層的事務接口進行封裝。
    Query和Criteria接口:
        這兩個是Hibernate的查詢接口,用於向數據庫查詢對象,以及控制執行查詢的過程。
        Query實例包裝了一個HQL查詢語句。
        Criteria接口完全封裝了基於字符串形式的查詢語句,比Query接口更面向對象。Criteria更擅長於執行動態查詢。
        補充:find方法也提供數據查詢功能,但只是執行一些簡單的HQL查詢語句的快捷方式(已過時),遠沒有Query接口強大!


四、Hibernate開發步驟:(重點:必須掌握)
   開始:(設置環境變量和配置)
         在myeclipse裏導入Hibernate的文件包(包括各數據庫的驅動和其他的jar包,對版本敏感,注意各版本的兼容)
         按hibernate規範編寫名字爲hibernate.cfg.xml文件(默認放在工程文件夾下)
   步驟一:設計和建立數據庫表
        可以用Hibernate直接生成映射表。
        Oracle裏建表:create table t_ad (oid number(15) primary key,
                     ACTNO varchar(20) not null unique,BALANCE number(20));
   步驟二:持久化類的設計
     POJO----
     POJO 在Hibernate 語義中理解爲數據庫表所對應的Domain Object。(此類中只含有屬性、構造方法、get/set方法)
     這裏的POJO就是所謂的“Plain Ordinary Java Object”,字面上來講就是無格式普通Java對象,
     簡單的可以理解爲一個不包含邏輯代碼的值對象(ValueObject 簡稱VO)。

   步驟三:持久化類和關係數據庫的映射
     編寫*.hbm.xml文件
     ---該文件配置持久化類和數據庫表之間的映射關係
     <class name=“POJO的類全路徑” table=“對應的庫表名”     //這兩項一定要配置,其它的都可以不配置
   discriminator-value=“discriminator_value”   //區分不同子類的值,多態時使用。默認與類名一樣
   dynamic-update=“true | false” //是否動態更新SQL。false:每次都更新所有屬性;true:只更新修改的
   dynamic-insert=“true | false” //是否動態插入SQL。false:每次都插入所有屬性;true:只插入非空的
   select-before-update=“true | false” //是否在update前查詢對象是否被修改過,修改過才update
   polymorphism=“implicit | explicit”  //設置多態是顯性(explicit)的還是隱性(implicit)的
   where=“查詢時使用的SQL的條件子句”  //查詢時使用的SQL的條件子句
   lazy=“true | false” //設置延遲加載策略
  />
     一個實體對應一個xml文件,組件用id,非組件用property。
     *.hbm.xml文件樣板:
        <?xmlversion="1.0"?>
        <!DOCTYPE hibernate-mappingPUBLIC
           "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
            "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
        <hibernate-mappingpackage="com.tarena.ebank.biz.entity"><!--package指文件所在的包名 -->
           <classname="Account" table="student"><!-- name:POJO類的名; table數據庫裏對應的表名-->
             <id name="oid" column="OID"><!-- OID:(唯一,中性)表自動生成的(需要另外添加hilo表) -->
                <generator class="hilo">
                   <param name="table">t_hi</param>
                   <param name="column">hi</param>
             </generator></id>
             <property name="actNo" column="ACTNO"unique="true" not-null="true"/>
             <property name="bal" column="BALANCE"not-null="true"/>
           </class>
        </hibernate-mapping>

   步驟四:Hibernate配置文件
     hibernate.cfg.xml或hibernate.properties
     1.需要配置那些信息:持久化映射,方言,特性,登陸信息
         多數使用默認的設置。
         A、dialect:方言,就是拼驅動程序和SQL語句。每種數據庫對應一種方言。其實就是指定了用那一種數據庫。
            Oracle數據庫方言:org.hibernate.dialect.OracleDialect
            MySql數據庫方言:org.hibernate.dialect.MySQLDialect
         B、ObjectPersistence:對象持久化。把內存中的數據保存到一個永久的介質中,比如說數據庫。
         C、ORM:對象關係映射,是一個自動的過程
         注:持久對象與臨時對象最大的區別是有沒有數據庫id標識。
    2.hibernate.cfg.xml的樣板:
      <?xml version='1.0' encoding='UTF-8'?>
      <!DOCTYPE hibernate-configuration PUBLIC
         "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
          "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
      <hibernate-configuration>
      <session-factory>
            <!-- 數據庫連接配置 -->
        <propertyname="connection.url">jdbc:mysql://localhost:3306/test</property>
        <propertyname="connection.driver_class">com.mysql.jdbc.Driver</property>
        <propertyname="connection.username">root</property>
        <property name="connection.password">password</property>
            <!-- 自動建表語句:create覆蓋舊錶,update自動更新,none不理會 -->
        <propertyname="hbm2ddl.auto">update</property>
            <!-- 是否在控制檯上打印SQL(Hibernate把語句轉化爲SQL語句),默認false-->
        <propertyname="show_sql">true</property>
            <!-- 緩存策略,數據量不大可不寫  -->
        <propertyname="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
        <propertyname="cache.use_query_cache">false</property>
        <propertyname="cache.use_second_level_cache">false</property>
            <!-- 不同數據庫使用的SQL選擇 -->
        <propertyname="dialect">org.hibernate.dialect.MySQLDialect</property>
        <propertyname="myeclipse.connection.profile">mysql</property>
            <!-- 連接池配置,練習時不寫,使用默認的 -->
        <propertyname="connection.pool_size">1</property>
            <!--決定是採用thread或jta或自定義的方式來產生session,練習時不寫,使用默認的  -->
        <propertyname="current_session_context_class">thread</property>
            <!--*.hbm.xml文件路徑,各關聯表要一同寫上 -->
        <mapping resource="many_to_one/vo/Customer.hbm.xml"/>
        <mappingresource="com/tarena/ebank/biz/entity/Order.hbm.xml" />
      </session-factory>
      </hibernate-configuration>

    步驟五:使用Hibernate API
        //讀取Hibernate.cfg.xml配置文件,並讀到內存中爲後續操作作準備
        Configuration config = newConfiguration().configure();
        //SessionFactory緩存了生成的SQL語句和Hibernate在運行時使用的映射元數據。
        SessionFactory sessionFactory =config.buildSessionFactory();
        //Session是持久層操作的基礎,相當於JDBC中的Connection。
        Session session =sessionFactory.openSession();

       try{  //爲保持事務的原子性,必須捕捉異常。所有事務都放在這一代碼塊裏。
            //操作事務時(增、刪、改)必須顯式的調用Transaction(默認:autoCommit=false)。
            Transactiontx = session.beginTransaction();
            for(int i=0;i<=1000; i++){
             Student stu = new Student(...);
             session.save(stu);//set value to stu
             //批量更新:爲防止內存不足,分成每20個一批發送過去。
             if(i%20==0){session.flush();session.clear();}//不是大批量更新,則不需要寫這一行
             //默認時,會自動flush:查詢之前、提交時。
            }tx.commit();//提交事務,Hibernate不喜歡拋異常,如有需要,自己捕捉。

           //查詢方法。如果有必要,也可以用事務(調用Transaction)
            String hql ="from Student s where s.stuNo like ? and s.Sal > ?";//Student是類而不是表
            List list =session.createQuery(hql)
                              .setString(0, "a00_").setDouble(1, 3000.0)//設置HQL的第一二個問號取值
                              .list();//Hibernate裏面,沒有返回值的都默認返回List
            StringBuffersb = new StringBuffer();
            for(Studentst :(List<Student>)list){//(List<Student>)強制類型轉換
             sb.append(st.getOid()+"  "+st.getName()+"\n");//拿到Student類裏的屬性
           }System.out.print(sb.toString());//直接打印sb也可以,它也是調用toString,但這樣寫效率更高
        } catch (HibernateException e) {
           e.printStackTrace();
            session.getTransaction().rollback();//如果事務不成功,則rollback
        } finally {
           session.close();//注意關閉順序,session先關,Factory最後關(因爲它可以啓動多個session)
           sessionFactory.close();//關閉SessionFactory,雖然這裏沒看到它,但在HbnUtil裏開啓了。
        }
五、 Hibernate主鍵策略(上面的步驟三的一部分)
    <id><generator class=“主鍵策略”/></id>
    主鍵:在關係數據庫中,主鍵用來標識記錄並保證每條記錄的唯一性(一般可保證全數據庫唯一)。必須滿足以下條件:
        1)不允許爲空。
        2)不允許主鍵值重複。
        3)主鍵值不允許改變。
    1.自然主鍵:以有業務含義的字段爲主鍵,稱爲自然主鍵。
        優點:不用額外的字段。
        缺點:當業務需求發生變化時,必須修改數據類型,修改表的主鍵,增加了維護數據庫的難度。
    2.代理主鍵:增加一個額外的沒有任何業務含義的一般被命名爲ID的字段作爲主鍵。
        缺點:增加了額外字段,佔用部分存儲空間。
        優點:提高了數據庫設計的靈活性。
    Hibernate用對象標識(OID)來區分對象:
        Student stu =(Student)session.load(Student.class,101); //這代碼加載了OID爲101的Student對象
    Hibernate推薦使用代理主鍵,因此Hibernate的OID與代理主鍵對應,一般採用整數型,包括:short、int、long。

    1、主鍵生成策略: (Hibernate支持多種主鍵生成策略)
    generator節點中class屬性的值:
      1) assigned:assigned:由用戶自定義ID,無需Hibernate或數據庫參與。
         是<generator>元素沒有指定時的默認生成策略。
           <idname="id" column="id"><generator class="assigned"/></id>
      2) hilo:通過hi/lo(高/低位)算法生成主鍵,需要另外建表保存主鍵生成的歷史狀態(這表只需要一個列和高位初始值)。
         hi/lo算法產生的標識只在一個特定的DB中是唯一的。所有數據庫都可用。
         如果同一個數據庫裏多張表都需要用;可以建多張主鍵表,也可以共用同一字段,但最好是用同一張主鍵表的不同字段。
           <idname="id" column="id"><generator class="hilo">
              <param name="table">high_val</param><!--指定高位取值的表-->
              <param name="column">nextval</param> <!--指定高位取值的列-->
              <param name="max_lo">5</param><!--指定低位最大值,當取到最大值是會再取一個高位值再運算-->
          </generator></id>
      3) sequence:採用數據庫提供的Sequence機制。
         Oracle,DB2等數據庫都提供序列發生器生成主鍵,Hibernate也提供支持。
           <idname="id" column="id"><generatorclass="sequence">
              <param name="sequence">序列名</param>
          </generator></id>
      4) seqhilo:功能同hilo,只是自動建表保存高位值。主鍵生成的歷史狀態保存在Sequence中。
         只能用於Oracle等支持Sequence的數據庫。
           <idname="id" column="id"><generatorclass="hilo">
              <param name="sequence">high_val_seq</param> <paramname="max_lo">5</param>
          </generator></id>
      5) increment:主鍵按數值順序遞增。
         作用類型:long,short,int
         使用場景:在沒有其他進程同時往同一張表插數據時使用,在cluster下不能使用
      6) indentity:採用數據庫提供的主鍵生成機制。特點:遞增。(Oracle不支持)
         通常是對DB2,Mysql,MS Sql Server, Sybase, Hypersonic SQL(HSQL)內置的標識字段提供支持。
         返回類型:long,short,int 
           <idname="id" column="id"><generatorclass="identity"/></id>
         注:使用MySql遞增序列需要在數據庫建表時對主健指定爲auto_increment屬性。用Hibernate建表則不需要寫。
           (oid int primarykey auto_increment)
      7) native:由Hibernate根據底層數據庫自行判斷採用indentity, hilo或sequence中的一種。
         是最通用的實現,跨數據庫時使用。Default.sequence爲hibernate_sequence
           <idname="id" column="id"><generatorclass="native"/></id>
      8) foreign:由其他表的某字段作爲主鍵,通常與<one-to-one>聯合使用;共享主健(主鍵與外鍵),兩id值一樣。
           <idname="id" column="id" type="integer"><generator class="foreign">
              <param name="property">car</param>
          </generator></id>
      9) UUID:
         uuid.hex:由Hibernate基於128位唯一值產生算法生成十六進制數(長度爲32的字符串---使用了IP地址)。
         uuid.string:與uuid.hex一樣,但是生成16位未編碼的字符串,在PostgreSQL等數據庫中會出錯。
         特點:全球唯一;ID是字符串。
      10)select:通過DB觸發器(trigger)選擇一些唯一主鍵的行,返回主鍵值來分配主鍵
      11)sequence-identity:特別的序列發生策略,使用DB序列來生成值,通常與JDBC3的getGenneratedKeys一起用,
         使得在執行insert時就返回生成的值。Oracle 10g(支持JDK1.4)驅動支持這一策略。

    2、複合主鍵策略
       步驟一:創建數據庫表,設定聯合主鍵約束
       步驟二:編寫主持久化類以及主鍵類;編寫主鍵類時,必須滿足以下要求:
          1)實現Serializable接口
          2)覆蓋equals和hashCode方法
          3)屬性必須包含主鍵的所有字段
       步驟三:編寫*.hbm.xml配置文件
          <composite-idname="dogId" class="composite.vo.DogId">
           <key-property name="name" type="string"><columnname="d_name"/></key-property>
           <key-property name="nick" type="string"><columnname="d_nick"/></key-property>
          </composite-id>
六、 Hibernate的查詢方案(應該熟悉各種查詢的使用方法)
    1、利用Session接口提供的load方法或者get方法
    2、Hibernate提供的主要查詢方法
       1)Criteria Query(條件查詢)的步驟:
         (1)通過Session來創建條件查詢對象Criteria
            Criteriacriteria = session.createCriteria(Course.class);
         (2)構建條件---創建查詢條件對象Criterion
            Criterioncriterion1 = Property.forName("id").ge(39);//通過Property來創建
            Criterioncriterion2 = Restrictions.le("cycle", 5); //通過Restrictions來創建
         (3)查詢對象關聯條件
           criteria.add(criterion1);
         (4)執行條件查詢
           List<Course> courses = criteria.list();
       2)HQL(Hibernate Qurey Language)
         特點: 語法上與SQL類似; 完全面向對象的查詢; 支持繼承、多態、關聯
         (1) FROM子句
             例如:查詢所有的學生實例
             Queryquery=session.createQuery("from Student"); query.list();
         (2) SELECT子句
             選擇哪些對象和屬性返回到結果集
          A、SELECT語句後可以跟多個任意類型的屬性,返回結果保存在Object類型的數組中
             //A、B、C、都是查詢學生的姓名和年齡
             Queryquery=session.createQuery("select stu.name,stu.age from Student asstu");
            List<Object[]> os=query.list();//返回的Object數組中有兩個元素,第一個是姓名,第二個是年齡
          B、SELECT語句後可以跟多個任意類型的屬性,返回結果也可以保存在List中
             Queryquery=session.createQuery
              ("select new List(stu.name,stu.age) from Student as stu");
            List<List> lists=query.list();
          C、SELECT語句後可以跟多個任意類型的屬性,返回結果也可以是一個類型安全的POJO對象
             Queryquery=session.createQuery
              ("select new Student(stu.name,stu.age) from Student as stu");
            List<Student> stuList=query.list();//注意:Student類必須有Student(String,int)的構造方法
          D、SELECT子句中可以使用聚集函數、數學操作符、連接
             支持的聚集函數:avg、sum、min、max、count ….
         (3) WHERE子句,限制返回結果集的範圍
         (4) ORDER BY子句,對返回結果集進行排序
       3)Native SQL(原生SQL查詢)
         可移植性差:資源層如果採用了不同的數據庫產品,需要修改代碼---非不得已,不推薦使用
         步驟一:調用Session接口上的createSQLQuery(String sql)方法,返回SQLQuery
         步驟二:在SQLQuery對象上調用addEntity(Class pojoClass) //設置查詢返回的實體
           例如:SQLQuery query =session.createSQLQuery(“select * from student limit2,10”)
               query.addEntity(Student.class);
               List<Student> stuList=query.list();
七、 Hibernate對象的狀態
    實體對象的三種狀態:
    1) 暫態(瞬時態)(Transient)---實體在內存中的自由存在,它與數據庫的記錄無關。
        po在DB中無記錄(無副本),po和session無關(手工管理同步)
        如:Customercustomer = new Customer(); customer.setName("eric");
        這裏的customer對象與數據庫中的數據沒有任何關聯
    2) 持久態(Persistent)---實體對象處於Hibernate框架的管理中。
        po在DB中有記錄,和session有關(session自動管理同步)
    3)遊離態(脫管態)(Detached)
        處於Persistent狀態的實體對象,其對應的Session實例關閉之後,那麼,此對象處於Detached狀態。
        po在DB中有記錄,和session無關(手工管理同步)
      無名態:po處於遊離態時被垃圾回收了。沒有正本,只有DB中的副本。
      po處於暫態時被垃圾回收了,則死亡。(唯一可以死亡的狀態)

    實質上,這三個狀態是:持久對象的正副本與同步的關係
    原則:儘量使用持久態。
    三態的轉換:
        暫態--->持久態
            A.調用Session接口上的get()、load()方法
            B.調用Session接口上的save()、saveOrUpdate()方法
        持久態--->暫態
            delete();
        遊離態--->持久態
            update()、saveOrUpdate()、lock();
            (lock不建議用,危險;肯定沒變化時用,有則用updata)
        持久態--->遊離態
            evict()、close()、clear()
            (一般用evict,只關閉一個實體的連接;close關閉整個連接,動作太大)
八、 映射(重點掌握和理解,注意配置的細節)
    關聯關係:A有可能使用B,則AB之間有關聯關係(Java裏指A有B的引用)。
            雙邊關係、傳遞性、方向性、名稱、角色(權限)、數量(1:1;1:m;n:m)、關聯強度
    委託:整體跟部分之間是同一類型。    代理:整體跟部分之間不是同一類型。
    A. 單一實體映射:最簡單、基本映射(最重要);任何其他映射種類的基礎。
       原則:1.類->表;一個類對應一個表。
            2.屬性->字段:普通屬性、Oid;一個屬性對應一個字段。
    B. 實體關係映射:
       a.關聯關係映射:(最難、量最多)
           1.基數關係映射:
             一對一(one to one) (共享主鍵、唯一外鍵)
             一對多(one to many) (1:m) 作級聯,刪one後連着刪many
             多對一(many to one) (m:1) 不作級聯,刪many中一個,不刪one
             多對多(many to many)(n:m = 1:n + m:1)
           2.組件關係映射:(一個類作爲另一個類的零件,從屬於另一個類,沒有自己的XML)
             單一組件關係映射
             集合組件關係映射
       b.繼承關係映射:(最普遍。兩個類有繼承關係,在本質上他們就是一對一關係。共享主健。)
           有三種映射方案:
           1.一個類一個表(效率很低;最後考慮使用,一般是數據量較大和父子類重複字段不多的時候用)
             只有當子類中的屬性過多時才考慮每個類建一個表的策略。
           2.一個實體一個表(多表查詢效率低,不考慮多態時用)
             不考慮多態時,最好是用只針對具體類建表,而考慮多態時儘量使用所有類建一個表
           3.所有類一個表(查詢效率最高,結構簡單;字段數不超過100個時使用,首選)

      c.集合映射(值類型)
           Set   不重複、無順序
           List  可重複、有順序
           Map  
           Bag   可重複、無順序(bag本身也是list實現的)
    雙向關聯(Bidirectional associations)(相當於兩個單向關聯) 
    單向關聯(Unidirectional associations)

    "一"方的配置:
    <!-- 表明以Set集合來存放關聯的對象,集合的屬性名爲orders;一個"customer"可以有多個"order" -->
    <!-- inverse="true"表示將主控權交給order,由order對象來維護關聯關係,
         也就是說order對象中的關聯屬性customer的值的改變會反映到數據庫中 -->
    <set name="orders"cascade="save-update" inverse="true">
        <!-- 表明數據庫的orders表通過外鍵customer_id參照customer表 -->
        <keycolumn="customer_id"/>   
        <!-- 指明Set集合存放的關聯對象的類型 -->
        <one-to-manyclass="many_to_one.vo.Order"/>
    </set>

    "多"方的配置:
    <many-to-one
        name="customer"
       class="many_to_one.vo.Customer"
        column="customer_id"
        not-null="true"
        cascade="save-update"
        />

    cascade屬性:設定級聯操作(插入、修改、刪除)。
    cascad屬性值                描述
   -------------------------------------------------------------------------
   none                保存、更新或刪除當前對象時,忽略其他關聯對象,默認屬性值
   save-update          通過Session的save()、update()以及saveOrUpdate()方法來保持、更新當前對象時級聯
                        所有關聯的新建對象,並且級聯更新所有有關聯的遊離對象
   delete              當通過Session的delete()方法來刪除當前對象時,級聯刪除所有關聯對象
   all                 包含所有的save-update以及delete行爲
    delete-orphan        刪除所有和當前對象解除關聯關係的對象
    all-delete-orphan    包含all與delete-orphan的動作

    inverse屬性:表示是否將當前屬性的值的變化反映到數據庫中去。
            false --- 表示反映到數據庫中
            true ---表示不反映到數據庫中
    Set的lazy屬性:
       A.不設置lazy值,默認true    現象:查詢Customer時,不會主動查詢關聯表Orders(SQL語句)
       B.設置lazy=false         現象:出現查詢Orders表的SQL語句

    3、多對多
        默認情況下,由兩方共同維護關聯關係。也就是兩個對象關聯屬性的值的改變都會反映到數據庫中。
九、 Hibernate控制的事務
 事務保證原子操作的不可分,也就是操作的同時成功或同時失敗。
 hibernate的事務隔離級別和JDBC中大致相同。
  設置時要在hibernate.cfg.xml配置
  <propertyname="hibernate.connection.isolation">4</property>
  1: 讀未提交的數據(Read uncommitted isolation) 髒讀
  2: 讀已提交的數據(Read committedisolation)   不可重複讀
  4: 可重複讀級別(Repeatable readisolation)    幻讀
  8: 可串行化級別(Serializable isolation)
 hibernate的鎖(悲觀鎖,樂觀鎖)
   1.悲觀鎖是由數據庫本身所實現的,會對數據庫中的數據進行鎖定,也就是鎖行。(更新期間不許其他人更改)
  LockMode.UPGRADE,修改鎖,在get()方法中加上這個設置作爲第三個參數。
  LockMode.NONE 無鎖機制
  LockMode.READ 讀取鎖
  LockMode.WRITE 寫入鎖,不能在程序中直接使用
  還可以使用Session.lock() Query.setLockMode()  Criteria.setLockMode()方法來設置鎖,
  檢測版本號,一旦版本號被改動則報異常。
   2.樂觀鎖,也就是通過對記錄加上某些信息來解決併發訪問的問題。(認爲更新期間不會有其他更改)
  版本檢查;要在其表中多加上一列表示版本信息,會在讀取時讀到這個版本號,並在修改之後更新這個版本號;
  更新瞬間加鎖,並且只有版本號相同纔會予以更新,如果版本號不同,就會拋出例外。
  <version name="version" column="version"type="integer" />


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章