hibernate主鍵生成策略

不設限發表於2011-12-05
 


使用mySql生成聯合主鍵需要注意的地方:
生成聯合主鍵的各個列不可以為空,首先要設定好,也不可以有一個為主鍵,
因為主鍵只有一個,如果聯合主鍵其中的一個為主鍵的話,那麼就沒有必要再去

生成聯合主鍵了.

使用聯合主鍵的時候要注意:聯合主鍵需要實現serializable介面,實現了這個介面後還最好要重寫
equals和toString()方法,這兩個方法如果不寫的話,剛開始是看不到錯誤的,但是等你開始真正的執行的時候就會報錯了.

應該是任何的資料庫的主鍵生成策略都是一樣的.

在改造系統自動生成的聯合主鍵的過程中注意修改好:
比如系統自動生成的是:
<composite-id name="id" class="com.dada.foreign.TwoId">
            <key-property name="id" type="java.lang.Integer">
                <column name="id" />
            </key-property>
            <key-property name="name" type="java.lang.String">
                <column name="name" length="20" />
            </key-property>
</composite-id>

如果想自己改造一下這個實體,讓它只是需要一個類就可以,這種情況下
要這樣來修改:

<composite-id>
            <key-property name="id" type="java.lang.Integer">
                <column name="id" />
            </key-property>
            <key-property name="name" type="java.lang.String">
                <column name="name" length="20" />
            </key-property>
</composite-id>

就是隻是需要把composite標籤裡面的屬性給刪除掉就好了,然後再修改
系統自動生成的實體類,這個時候需要把包含在那個生成聯合逐漸的實體
類中的屬性都放入到生成實體的主體類當中,而且需要提供每個屬性的
get和set方法,之後再新增一個只是包含了聯合主鍵的構造方法就可以了.
然後再把沒有用的屬性都給刪除掉,在對實體進行操作的時候,記住要首先
使用構造方法生成主鍵,然後再執行其他的操作.

聯合主鍵是需要生成這個主鍵的hashCode和equals方法的

public class TwoId implements java.io.Serializable {

 // Fields

 private Integer id;
 private String name;

 getter and setters

 public boolean equals(Object other) {
  if ((this == other))
   return true;
  if ((other == null))
   return false;
  if (!(other instanceof TwoId))
   return false;
  TwoId castOther = (TwoId) other;

  return ((this.getId() == castOther.getId()) || (this.getId() != null
    && castOther.getId() != null && this.getId().equals(
    castOther.getId())))
    && ((this.getName() == castOther.getName()) || (this.getName() != null
      && castOther.getName() != null && this.getName()
      .equals(castOther.getName())));
 }

 public int hashCode() {
  int result = 17;

  result = 37 * result + (getId() == null ? 0 : this.getId().hashCode());
  result = 37 * result
    + (getName() == null ? 0 : this.getName().hashCode());
  return result;
 }

}

--------------------------------------------------------------------------------------------------
使用annotation生成聯合主鍵

1.把提供主鍵的類設定為@Embeddable表明這個類可以作為一個嵌入的類,然後在需要此類的地方進行引用時為這個
元件的引用加上@Id註解就可以了.

2.將元件的屬性設定為Embeddable,就是說這個元件類本身是不帶有註解的,但是對它進行引用的實體就需要在使用
它的時候對他加上Embedded這個註解

3.將實體類設定為IdClass,然後把實體中所有屬於主鍵的屬性給標註為@Id就行了.

三種方式的使用情況,第一種的使用情況是非常少的,因為第二種就可以直接的比它少了一步,第二種方式是最常用的,
第三種也有用的,但是用的少,為什麼呢?
這個是因為如果使用了聯合主鍵之後,自己總還是需要從資料庫中去讀取東西的,這個時候呢就有必要把它給讀出來,
怎麼讀呢?使用物件導向的方法就需要提供主鍵的物件,這個時候就需要使用到它了.

--------------------------------------------------------------------------------------------------
一對一單相外來鍵關聯

這種情況就會導致的是在此表中會多生成一個欄位那就是wife_id是作為這個表的一個外來鍵.
如果要指定生成的外來鍵欄位就需要這樣設定:

  @OneToOne
 @JoinColumn(name="wifeId")
 private Wife1 wife;

 

@Entity
public class Husband1 {
 @Id
 @GeneratedValue
 private Integer id;
 private String name;
 @OneToOne
 private Wife1 wife;

 public Wife1 getWife() {
  return wife;
 }


}

當然雙向關聯就容易了,只需要在wife實體中也持有一個對Husband的引用就可以了.
但是又這樣的一個需求,就是,需要設定雙向的關聯,但是實際上只需要在自己或者對方
設定一個引用就可以了,就是說,兩個表雖然是雙向的關聯,但是並不需要設定兩個外來鍵
這樣設定是多餘的,這種情況下應該怎麼弄呢?
這個時候應該在不需要設定外來鍵的一方中設定好mappedBy就好了,如下

@OneToOne(mappedBy="wife")
 private Husband1 husband1;

其中mappedBy的意思是這個外來鍵關係是由對方來主導的,使用的是對方的wife屬性來主導的.
這樣設定之後就可以達到使用一個外來鍵而得到雙向關聯的目的了.

在xml中這種雙向關聯,只生成一個外來鍵的設定方式是:
<one-to-one name="wife" property-ref="student">
同樣的,這裡的student也是對方實體裡面的一個引用.

只要有雙向關聯必然需要設定mapped

如果是設定為一對一的主鍵關聯的話,就把
 @JoinColumn(name="wifeId")
 修改為
 @PrimaryKeyJoinColumn
--------------------------------------------------------------------------------------------------
多對多單向關聯
單向關聯只需要在一方設定好關聯關係就好了,另外的一方不要設定的.

@Entity
public class Student {
 @Id
 private int id;
 private String name;
 @ManyToMany
 private Set<Teacher> teachers = new HashSet<Teacher>();

}
注意在這個地方設定@ManyToMany的mappedBy屬性是沒有用的,因為
它根本不需要生成外來鍵,而是生成一箇中間表,這個時候如果想要指定
生成的中間表的名稱和中間表中的欄位的名字的話,就需要設定:

@JoinTable(
   name="t_s",//指明中間表的名字
   joinColumns={@JoinColumn(name="stu_id")},//指明本實體
   //的主鍵在中間表中的外來鍵的名字
   inverseJoinColumns={@JoinColumn(name="tea_id")}
   //指明另外一方實體在中間表中的外來鍵的名字.
 )
 
 
在xml配置中中間表的名稱是在類的hbm的<set>標籤中配置的.
 
 

 ---------------------------------------------------------------------------------------------------------------------------------------------------

 Hibernate更新指定的欄位

1需求,有一個表裡面記錄的是學生的姓名,學號和他的愛好論文這個時候如果這個表需要更新,
那麼在hibernate中預設的更新是把所有的欄位全部都更新一遍,因此就會出現在更新一個人的
愛好的時候卻需要把論文也更新的情況,這種情況很顯然很耗資源也沒有必要,這個時候可以這樣
配置
在對應的論文欄位上加上註解@Colum(updatable=false);
或者在對應的xml的配置檔案中配置一下<property>標籤的update屬性也是可以的.

但是上面的這兩種方式都太不靈活了,因此需要使用第三種方式就是在xml中配置
<hibernate-mapping>
 <class catalog="shopping" name="com.dada.entities.Notebook" table="notebook" dynamic-update="true">
 這個時候呢就是dynamic-update="true"這句話起作用了,它可以動態的去修改.
 
  不過這種動態的改變有個限制就是隻能夠在xml檔案中去配置,而不可以在annotation中去配置,所以
  如果需要使用動態的改變的話,就使用xml去配置這個檔案就好了.
  當然如果不想使用這種動態的配置的話,也可以使用類似於sql的hql語句來實現對特定的一個欄位進行修改.
 
 
 但是即便是動態的修改它還是有條件的,條件就是動態修改的條件,如何才能知道哪些屬性改變了呢?
 肯定是拿現在的屬性跟資料庫的屬性進行了比較了,那怎麼跟資料庫裡面的資料進行比較呢?
 那肯定是把資料庫裡面的東西給load進入記憶體裡面了,這樣才能對修改後的內容跟修改之前的內容進行比較
 
 所以就是執行動態代理的條件就是,被執行動態代理的物件是持久化的也就是被load或者get進入記憶體的.
 
 但是有的時候會出現這種需求就是跨session去修改資料庫,這個時候應該使用的是merge()它的意思是合併
 這個在hibernate中實現的細節是:先從資料庫中去load這個物件,之後再把要合併的東西跟資料庫裡面的進行
 對比,然後再去修改到資料庫裡面.
 
 clear()清空快取內容;
 flush:強制快取內容與資料庫同步;
 
 比如
 p.setName("a");
 p.setName("aa");
 session.save(p);
 這個時候hibernate中會生成一條語句這個時候只是執行最後一次的更新.
 如果使用的是
 p.setName("a");
 session.flush();
 p.setName("aa");
 session.save(p);
 
  session.flush();
 這句話會產生這樣的效果:
 1.hibernate會向資料庫傳送兩條語句,一條是執行第一個更改
 2.執行第二次更改
 
 如果把session.flush();換成為session.clear();
 那麼結果就是記憶體被清空,然後呢持久化的物件都沒有了這個時候,當然就談不上是更改了.
 
 當然在執行commit();方法的時候,系統就會預設的實現flush();這個方法的.
 

 


 

相關文章