1. 程式人生 > >Hibernate學習(4)- 註解的使用

Hibernate學習(4)- 註解的使用

這篇blog主要是總結一下Hibernate註解的使用.主要分為三部分,第一部分宣告實體類的註解,包括宣告實體類過程中的約束條件註解的使用,第二部分主要為關聯關係之間的註解宣告,最後一部分作為其他註解.
1.為什麼要使用註解?
說實話,我覺得使用覺的使用hbm.xml對映檔案能夠是對映關係看起來更明確顯眼些,有哪些實體類,對映到資料庫中有哪些列也是比較明顯的,而且我覺的,配置檔案使專案的解耦更好一點(當然,也許你會說如果要更改對映關係,使不使用配置檔案都是需要更改原始檔的,.hbm.xml檔案也是原始檔,畢竟它和web.xml檔案還是有一些差別的,沒準不適用配置檔案還只需要更改一個檔案,額...想想其實也是的).不過使用配置檔案有一個很明顯的缺點:實體類多了之後,要在不同檔案之間轉換,更麻煩...所以從現在開始,來學習使用註解吧.(目前為止我們還不需要更改pom.xml檔案,目前所用的所有包都已經包括進來了)
2.實體類的定義等相關注解

2.1 定義實體類
package entities;


import javax.persistence.*;
import java.util.Date;

/**
 * Created by tbxsx on 17-5-30.
 */
@Entity
@Table(name = "`provice`")
public class Provice {
    private long id;
    private String name;
    private String email;

    private Date create_date;

    public Provice
() { } @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "`id`") public long getId() { return id; } public void setId(long id) { this.id = id; } @Column(name = "`name`", nullable = false, length = 30) public String getName
() { return name; } public void setName(String name) { this.name = name; } @Column(name = "`email`", nullable = false, unique = true, length = 30) public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } //注意在這裡設定了屬性 insertable = false,columnDefinition = "datetime default now()" 這些會在後面講到 @Column(name = "`create_date`", insertable = false, columnDefinition = "datetime default now()", nullable = false ) public Date getCreate_date() { return create_date; } public void setCreate_date(Date create_date) { this.create_date = create_date; } }

定義如上圖,測試如下:

    @Test
    public void testAnotation(){
        SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
        Session session = sessionFactory.openSession();
        session.beginTransaction();

        Provice provice = new Provice();
        provice.setName("hunan");
        provice.setEmail("[email protected]");
        session.save(provice);
        session.getTransaction().commit();
        System.out.println("Test!");
    }

SQL語句:


Hibernate: 

    create table `provice` (
       `id` bigint not null auto_increment,
        `create_date` datetime default now() not null,
        `email` varchar(30) not null,
        `name` varchar(30) not null,
        primary key (`id`)
    ) engine=MyISAM
Hibernate: 

    alter table `provice` 
       add constraint UK_b1cb2vul5rqbel47yqidov4h3 unique (`email`)
    insert 
    into
        `
        provice` (
            `email`, `name`
        ) 
    values
        (?, ?)

資料庫結果:
這裡寫圖片描述

註解從大到小:
1.Table層次
@Entity:實體類註解(必需)

public @interface Entity {

    /**
     * (Optional) The entity name. Defaults to the unqualified
     * name of the entity class. This name is used to refer to the
     * entity in queries. The name must not be a reserved literal
     * in the Java Persistence query language.
     */
    String name() default "";
}

只有一個名字屬性,名字屬性定義的是實體類名稱(預設為類名).
@Table:

/**
 * Specifies the primary table for the annotated entity. Additional
 * tables may be specified using {@link SecondaryTable} or {@link
 * SecondaryTables} annotation.
 * <p/>
 * If no <code>Table</code> annotation is specified for an entity
 * class, the default values apply.
 *
 * <pre>
 *    Example:
 *
 *    &#064;Entity
 *    &#064;Table(name="CUST", schema="RECORDS")
 *    public class Customer { ... }
 * </pre>
 *
 * @since Java Persistence 1.0
 */
@Target(TYPE)
@Retention(RUNTIME)
public @interface Table {
    /**
     * (Optional) The name of the table.
     * <p/>
     * Defaults to the entity name.
     */
    String name() default "";

    /**
     * (Optional) The catalog of the table.
     * <p/>
     * Defaults to the default catalog.
     */
    String catalog() default "";

    /**
     * (Optional) The schema of the table.
     * <p/>
     * Defaults to the default schema for user.
     */
    String schema() default "";

    /**
     * (Optional) Unique constraints that are to be placed on
     * the table. These are only used if table generation is in
     * effect. These constraints apply in addition to any constraints
     * specified by the <code>Column</code> and <code>JoinColumn</code>
     * annotations and constraints entailed by primary key mappings.
     * <p/>
     * Defaults to no additional constraints.
     */
    UniqueConstraint[] uniqueConstraints() default { };

    /**
     * (Optional) Indexes for the table. These are only used if table generation is in effect.  Defaults to no
     * additional indexes.
     *
     * @return The indexes
     */
    Index[] indexes() default {};
}

和Entity對應的是@Table註解,詮釋與這個類對應的資料庫表的相關屬性(非必需,Hibernate會產生預設名字,預設名為實體類名,即@Entity定義的名字,如果@Entity沒有定義,那麼資料庫對映表名即為類名)當實體類和資料庫中對應表名稱不同時,需要使用該註解.name屬性是在資料庫中表名稱,schema和category是資料庫名稱,unqiqueConstraints設定表約束.更多

2.屬性層次
主鍵:
@Id
宣告此屬性為主鍵。
@EmbeddedId : 組合主鍵

@Entity
public class Login {

    @EmbeddedId
    private PK pk;

    @Embeddable
    public static class PK implements Serializable {

        private String system;

        private String username;

        ...
    }

    ...
}

@GeneratedValue
指定主鍵的生成策略。有如下四個值
TABLE:使用表儲存id值,單獨生成一個表 更多
IDENTITY:identitycolumn,通過auto_increment產生
SEQUENCR :sequence,需要資料庫支援序列
AUTO:根據資料庫的不同使用上面三個(當我使用Mysql並不確定生成策略的時候,使用了Table策略)

一般屬性:
@Column

 /**
 * Is used to specify the mapped column for a persistent property or field.
 * If no <code>Column</code> annotation is specified, the default values apply.
 *
 * <blockquote><pre>
 *    Example 1:
 *
 *    &#064;Column(name="DESC", nullable=false, length=512)
 *    public String getDescription() { return description; }
 *
 *    Example 2:
 *
 *    &#064;Column(name="DESC",
 *            columnDefinition="CLOB NOT NULL",
 *            table="EMP_DETAIL")
 *    &#064;Lob
 *    public String getDescription() { return description; }
 *
 *    Example 3:
 *
 *    &#064;Column(name="ORDER_COST", updatable=false, precision=12, scale=2)
 *    public BigDecimal getCost() { return cost; }
 *
 * </pre></blockquote>
 *
 *
 * @since Java Persistence 1.0
 */
@Target({METHOD, FIELD})
@Retention(RUNTIME)
public @interface Column {

    /**
     * (Optional) The name of the column. Defaults to
     * the property or field name.
     */
    String name() default "";

    /**
     * (Optional) Whether the column is a unique key.  This is a
     * shortcut for the <code>UniqueConstraint</code> annotation at the table
     * level and is useful for when the unique key constraint
     * corresponds to only a single column. This constraint applies
     * in addition to any constraint entailed by primary key mapping and
     * to constraints specified at the table level.
     */
    boolean unique() default false;

    /**
     * (Optional) Whether the database column is nullable.
     */
    boolean nullable() default true;

    /**
     * (Optional) Whether the column is included in SQL INSERT
     * statements generated by the persistence provider.
     */
    boolean insertable() default true;

    /**
     * (Optional) Whether the column is included in SQL UPDATE
     * statements generated by the persistence provider.
     */
    boolean updatable() default true;

    /**
     * (Optional) The SQL fragment that is used when
     * generating the DDL for the column.
     * <p> Defaults to the generated SQL to create a
     * column of the inferred type.
     */
    String columnDefinition() default "";

    /**
     * (Optional) The name of the table that contains the column.
     * If absent the column is assumed to be in the primary table.
     */
    String table() default "";

    /**
     * (Optional) The column length. (Applies only if a
     * string-valued column is used.)
     */
    int length() default 255;

    /**
     * (Optional) The precision for a decimal (exact numeric)
     * column. (Applies only if a decimal column is used.)
     * Value must be set by developer if used when generating
     * the DDL for the column.
     */
    int precision() default 0;

    /**
     * (Optional) The scale for a decimal (exact numeric) column.
     * (Applies only if a decimal column is used.)
     */
    int scale() default 0;
}

可在此定義相關約束條件.
主要講一下columnDefinition 和 insertable,updatable.
比如說需要定義時間的預設值,預設值為插入資料庫的時候.那麼怎麼辦呢?通過設定 columnDefinition,這句話會在生成DDL時候,加入DDL中.如:

@Column(name = "`create_date`", insertable = false, columnDefinition = "datetime default now()", nullable = false
    )
    public Date getCreate_date() {
        return create_date;
    }

生成的DDL為:

`create_date` datetime default now() not null,

直接載入`create_date`後面.
然後設定

 insertable = false

這樣在save的時候產生的語句為:

    insert 
    into
        `
        provice` (
            `email`, `name`
        ) 
    values
        (?, ?)

否則為:

    insert 
    into
        `
        provice` (
            `email`, `name`,`create_date`
        ) 
    values
        (?, ?, ?)

如果為這種情況,因為create_date是資料庫自動生成的,在實體類中,create_date為null,所以插入的create_date將為null.
此外,如果想將create_date設定為物件生成時候的值,可以:

private Date create_date = new Date()

更多1

3.物件關聯關係的註解
首先介紹一下註解:@JoinColumn
我們知道在兩個表之間通過外來鍵來表示關聯關係.
@JoinColumn 可以註釋本表中指向另一個表的外來鍵,相當與一個外來鍵的定義.

    public @interface JoinColumn {

    /**
     * (Optional) The name of the foreign key column.
     * The table in which it is found depends upon the
     * context.
     * <ul>
     * <li>If the join is for a OneToOne or ManyToOne
     *  mapping using a foreign key mapping strategy,
     * the foreign key column is in the table of the
     * source entity or embeddable.
     * <li> If the join is for a unidirectional OneToMany mapping
     * using a foreign key mapping strategy, the foreign key is in the
     * table of the target entity.
     * <li> If the join is for a ManyToMany mapping or for a OneToOne
     * or bidirectional ManyToOne/OneToMany mapping using a join
     * table, the foreign key is in a join table.
     * <li> If the join is for an element collection, the foreign
     * key is in a collection table.
     *</ul>
     *
     * <p> Default (only applies if a single join column is used):
     * The concatenation of the following: the name of the
     * referencing relationship property or field of the referencing
     * entity or embeddable class; "_"; the name of the referenced
     * primary key column.
     * If there is no such referencing relationship property or
     * field in the entity, or if the join is for an element collection,
     * the join column name is formed as the
     * concatenation of the following: the name of the entity; "_";
     * the name of the referenced primary key column.
     */
    String name() default "";

    /**
     * (Optional) The name of the column referenced by this foreign
     * key column.
     * <ul>
     * <li> When used with entity relationship mappings other
     * than the cases described here, the referenced column is in the
     * table of the target entity.
     * <li> When used with a unidirectional OneToMany foreign key
     * mapping, the referenced column is in the table of the source
     * entity.
     * <li> When used inside a <code>JoinTable</code> annotation,
     * the referenced key column is in the entity table of the owning
     * entity, or inverse entity if the join is part of the inverse
     * join definition.
     * <li> When used in a <code>CollectionTable</code> mapping, the
     * referenced column is in the table of the entity containing the
     * collection.
     * </ul>
     *
     * <p> Default (only applies if single join column is being
     * used): The same name as the primary key column of the
     * referenced table.
     */
    String referencedColumnName() default "";

    /**
     * (Optional) Whether the property is a unique key.  This is a
     * shortcut for the <code>UniqueConstraint</code> annotation at
     * the table level and is useful for when the unique key
     * constraint is only a single field. It is not necessary to
     * explicitly specify this for a join column that corresponds to a
     * primary key that is part of a foreign key.
     */
    boolean unique() default false;

    /** (Optional) Whether the foreign key column is nullable. */
    boolean nullable() default true;

    /**
     * (Optional) Whether the column is included in
     * SQL INSERT statements generated by the persistence
     * provider.
     */
    boolean insertable() default true;

    /**
     * (Optional) Whether the column is included in
     * SQL UPDATE statements generated by the persistence
     * provider.
     */
    boolean updatable() default true;

    /**
     * (Optional) The SQL fragment that is used when
     * generating the DDL for the column.
     * <p> Defaults to the generated SQL for the column.
     */
    String columnDefinition() default "";

    /**
     * (Optional) The name of the table that contains
     * the column. If a table is not specified, the column
     * is assumed to be in the primary table of the
     * applicable entity.
     *
     * <p> Default:
     * <ul>
     * <li> If the join is for a OneToOne or ManyToOne mapping
     * using a foreign key mapping strategy, the name of the table of
     * the source entity or embeddable.
     * <li> If the join is for a unidirectional OneToMany mapping
     * using a foreign key mapping strategy, the name of the table of
     * the target entity.
     * <li> If the join is for a ManyToMany mapping or
     * for a OneToOne or bidirectional ManyToOne/OneToMany mapping
     * using a join table, the name of the join table.
     * <li> If the join is for an element collection, the name of the collection table.
     * </ul>
     */
    String table() default "";

    /**
     * (Optional) The foreign key constraint specification for the join column. This is used only if table generation
     * is in effect.  Default is provider defined.
     *
     * @return The foreign key specification
     */
    ForeignKey foreignKey() default @ForeignKey();
}
3.1 @OneToOne
    配合@JoinColumn使用
    我們現在使用Husband和Wife的一對一關係演示:
@Entity
public class Husband {
    private long id;
    private String name;
    private Wife wife;

    public Husband() {
    }

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @OneToOne(fetch = FetchType.LAZY,cascade = CascadeType.PERSIST)
    @JoinColumn(name = "wife_id")
    public Wife getWife() {
        return wife;
    }

    public void setWife(Wife wife) {
        this.wife = wife;
    }
}


@Entity
public class Wife {
    private long id;
    private String name;
    private Husband husband;

    public Wife() {
    }

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @OneToOne(fetch = FetchType.LAZY,cascade = CascadeType.PERSIST)
    @JoinColumn(name = "husband_id")
    public Husband getHusband() {
        return husband;
    }

    public void setHusband(Husband husband) {
        this.husband = husband;
    }
}


    @Test
    public void testOneToOneAnotation(){
        SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        Husband husband = session.get(Husband.class,1l);
        session.delete(husband);

        session.getTransaction().commit();
        System.out.println("Test!");
    }

注意點:
1.@JoinColumn 和@OneToOne中MappedBy的用法.
前者可以理解為在Wife表中建立一個代表Husband的屬性,當然這是一個外來鍵.
後者代表:Husband 在Wife實體類中有一個指向(即Wife類中有一個名字為husband的Husband物件的引用).即mappedBy標籤定義在被擁有方的,他指向擁有方;
mappedBy跟joinColumn/JoinTable總是處於互斥的一方,可以理解為正是由於擁有方的關聯被擁有方的欄位存在,擁有方才擁有了被擁有方。mappedBy這方定義JoinColumn/JoinTable總是失效的,不會建立對應的欄位或者表。
參考
參考
2. Cascade 的使用方法.
Cascade:
CascadeType.REFRESH:級聯重新整理,當多個使用者同時作操作一個實體,為了使用者取到的資料是實時的,在用實體中的資料之前就可以呼叫一下refresh()方法!

CascadeType.REMOVE:級聯刪除,當呼叫remove()方法刪除Husband實體時會先級聯刪除wife的相關資料!

CascadeType.MERGE:級聯更新,當呼叫了Merge()方法,如果husband中的資料改變了會相應的更新OWife中的資料,

CascadeType.ALL:包含以上所有級聯屬性。


(注:以上幾種級聯操作,只能實在滿足資料庫的約束時才能生效)
CascadeType.PERSIST:級聯儲存,當呼叫了Persist() 方法,會級聯儲存相應的資料
比如說:舉例CascadeType.PERSIST.
如果前面的例子中,沒有Cascade屬性,使用save方法儲存Hubband的時候,將會由於它的外來鍵wife沒有儲存,導致出現錯誤(外來鍵不存在),如果使用了該屬性,那麼它會儲存兩個實體類,消除錯誤.
ERROR: HHH000346: Error during managed flush [org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance beforeQuery flushing: entities.Wife]
java.lang.IllegalStateException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance beforeQuery flushing: entities.Wife

參考
3. orphanRemoval 的使用方法
在這裡,如果只設置cascade為persist,不設定該屬性的時候,刪除一個husband,將不會刪除對應的wife,如果該屬性為true,將會刪除兩者.
更多的該屬性用法會出現在多對多或者一對多關係中,如果有中間關係表,刪除實體的時候,該屬性會使hibernate同時刪除中間表的記錄.

3.2 @ManyToOne
3.3 @OneToMany
        多方:
        @ManyToOne
        一方:
        @OneToMany(mappedBy = "person", cascade = CascadeType.ALL, orphanRemoval = true)

3.4 @ManyToMany
    主方擁有者:@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    從擁有者方:@ManyToMany(mappedBy = "addresses")
3.5 @Any
3.6 @ManyToAny

參考

相關推薦

Hibernate學習 註解的使用

這篇blog主要是總結一下Hibernate註解的使用.主要分為三部分,第一部分宣告實體類的註解,包括宣告實體類過程中的約束條件註解的使用,第二部分主要為關聯關係之間的註解宣告,最後一部分作為其他註解. 1.為什麼要使用註解? 說實話,我覺得使用覺的使用hb

hibernate學習hibernate的一級緩存&快照

class 執行 設置 update 原理 查看 技術分享 pri 比對 緩存:提高效率 硬件的 CPU緩存 硬盤緩存 內存 軟件的 io流緩存 hibernate 的一級緩存 也是為了操作數據庫的效率。 證明一級緩存在

Hibernate學習1簡單介紹

清理緩存 並發 connect upgrade 定義 之前 對象 value cat 1.什麽是Hibernate? ????????? 首先,Hibernat

Hibernate 學習

column str 數據庫服務器 app this select log new where 一、Hibernate 簡介 1、Hibernate 簡介   Hibernate是一個開放源代碼的對象關系映射(ORM)框架,它對JDBC進行了非常輕量級的對象封裝,它將POJ

Hibernate學習1- 初識

his HA logs ply func 對數 事務管理 arch 優化 一、概念引入 1、持久化:   狹義概念:數據存儲在物理介質不會丟失。   廣義概念:對數據的CRUD操作都叫做持久化。 2、加載: hibernate裏,數據從數據庫中加載到session。

Hibernate學習2- hibernate.cfg.xml詳解

source nec cfg 更新 閱讀 username 詳解 格式化sql BE 1:主配置文件主要分為三部分:    註意:通常情況下,一個session-factory節點代表一個數據庫;    1.1:第一部分       數據庫連接部分,註意"hibernate

hibernate學習2

1 實體類編寫規則 2 hibernate主鍵生成策略 3實體類操作 (1)crud操作 (2)實體物件狀態 4 hibernate的一級快取 5 hibernate事務操作 (1)事務程式碼規則寫法 6 hibernate其他的api(查詢) 實體類編寫規則 1 實體類裡面屬性私有的

hibernate學習4

Hibernate查詢方式 1 物件導航查詢 (1)根據id查詢某個客戶,再查詢這個客戶裡面所有的聯絡人 2 OID查詢 (1)根據id查詢某一條記錄,返回物件 3 hql查詢 (1)Query物件,寫hql語句實現查詢 4 QBC查詢 (1)Criteria物件 5 本地sql查詢 (1

4天--Hibernate學習day02

今天內容: 1 實體類(web階段叫做javaBean)的編寫規則 2 hibernate主鍵生成策略 (1)native (2)uuid 3 實體類操作 (1)crud操作 (2)實體類物件狀態 4 hibernate的一級快取 5 hib

Hibernate學習

Hibernate是一個持久化,ORM框架。 持久化:瞬時狀態和持久狀態之間的轉換機制。 Hibernate 使用步驟 1 匯入本地jar包或者使用maven pom下載對應hiberante jar包。 2 封裝實體類User(採用正向工程方式

4天--Hibernate學習day03

今天內容 0 列表功能實現 1 表與表之間關係回顧 (1)一對多(客戶和聯絡人) (2)多對多(使用者和角色) 2 hibernate一對多操作 (1)一對多對映配置 (2)一對多級聯儲存

Hibernate學習

自關聯測試案例 1、建立表 drop table if exists t_category ; create table t_category ( id int(5) primary key , name varcha

Hibernate學習———— 一級快取和三種狀態解析

一、一級快取和快照     什麼是一級快取呢?       很簡單,每次hibernate跟資料庫打交道時,都是通過session來對要操作的物件取得關聯,然後在進行操作,那麼具體的過程是什麼樣的呢?         1、首先session將一個物件加入自己的管理範圍內,其實也就是把該物件放入自己的一級

Hibernate學習———— 第一個hibernate工程

一、什麼是Hibernate?       輕量級JavaEE應用的持久層框架,是一個完全的ORM框架。(說完這句話,肯定有很多人懵圈了,下面我來一個個解釋)           持久化:將我們想要儲存的資料儲存到硬碟上,也就是我們電腦的磁碟上,為什麼叫持久化呢,就是資料能夠儲存的很久,所以叫持久化,現在

Hibernate學習———— 雙向多對多對映關係

一、小疑問的解答        問題一:到這裡,有很多學習者會感到困惑,因為他不知道使用hibernate是不是需要自己去建立表,還是hibernate全自動,如果需要自己建立表,那麼主外來鍵這種設定也是自己設定嗎?這讓人感到很困惑,現在就來解決一下這個小疑問(如果知道了的可以直接跳過看下面的多對多對映關係

Hibernate學習———— 一對多對映關係

 序言        前面兩節講了hibernate的兩個配置檔案和hello world!。還有hibernate的一級快取和三種狀態,基本上hibernate就懂一點了,從這章起開始一個很重要的知識點,hibernate的關係對映。一對一、一對多、多對多的講解。,希望你們能從中學到東西,我也從中鞏固自己

Hibernate學習———— cascade(級聯)和inverse關係詳解

序言         寫這篇文章之前,自己也查了很多的資料來搞清楚這兩者的關係和各自所做的事情,但是百度一搜,大多數博文感覺說的雲裡霧裡,可能博主自己清楚是怎麼一回事,但是給一個不懂的人或者一知半解的人看的話,別人也看不懂其中的關係,所以我自己寫博文的時候,會盡量用通俗通俗在通俗的語言去描述一個概念,希望能

Hibernate學習———— hibernate一對一關係對映詳解

 一、一對一關係的概述       一對一關係看起來簡單,其實也挺複雜的。其中關係就包含了四種,單向雙向和主鍵關聯外來鍵關聯。 什麼意思呢,也就是包含了單向一對一主鍵關聯、雙向一對一主鍵關聯,單向一對一外來鍵關聯,雙向一對一外來鍵關聯, 這四種中,單雙向就不用在說了把,就是看你業務需求來去設定是否

Hibernate學習———— 二級快取和事務級別詳講

    序言           這算是hibernate的最後一篇文章了,下一系列會講解Struts2的東西,然後說完Struts2,在到Spring,然後在寫一個SSH如何整合的案例。之後就會在去講SSM,在之後我自己的個人部落格應該也差不多可以做出來了。基本上先這樣定下來,開始完成hibernate的

Hibernate學習———— Hibernate檢索策略(類級別,關聯級別,批量檢索)詳解

 序言          很多看起來很難的東西其實並不難,關鍵是看自己是否花費了時間和精力去看,如果一個東西你能看得懂,同樣的,別人也能看得懂,體現不出和別人的差距,所以當你覺得自己看了很多書或者學了很多東西的時候,你要想想,你花費的也就那麼一點時間,別人花你這麼多時間也能夠學到你所學到的東西,所以還是要繼