映射文件


Hibernate 中將物件與資料庫表格映射關係連接起來的是映射文件,如果使用XML來定義,通常以*.hbm.xml作為檔案名稱,如果要使用Annotation直接定義在.java檔案之中,可以參考 Hibernate Annotations

XML映射文件可以手工撰寫,或是透過工具程式從資料庫表格自動生 成,可以參考 從資 料表生成映射文件與POJO

來看看一個基本的映射文件如何撰寫:
  • User.hbm.xml
<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
<!--類別名稱與表格名稱映射-->
<class name="onlyfun.caterpillar.User" table="user">
<!--id與主鍵映射-->
<id name="id" column="id">
<generator class="native"/>
</id>
<!--類別屬性與表格欄位的映射-->
<property name="name" column="name"/>
<property name="age" column="age"/>
</class>
</hibernate-mapping>

映射文件中主要包括三個部份:類別名稱與表格名稱的映射、id屬性與主鍵的映射、類別屬性與表格欄位的映射。

這份映射文件對應於以下的類別與表格:
  • User.java
package onlyfun.caterpillar;

public class User {
private Integer id;
private String name;
private Integer age;

// 必須要有一個預設的建構方法
// 以使得Hibernate可以使用Constructor.newInstance()建立物件
public User() {}

public Integer getId() {
return id;
}

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

public String getName() {
return name;
}

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

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}
}

  • user 表格
 +-------+------------------+------+------+----------+---------------------+
 | Field | Type             | Null | Key  | Default  | Extra               |
 +-------+------------------+------+------+----------+---------------------+
 | id    | int(11)          |      | PRI  | NULL     | auto_increment      |
 | name  | varchar(100)     |      |      |          |                     |
 | age   | int(11)          | YES  |      | NULL     |                     | 
 +-------+------------------+------+------+----------+---------------------+


在<id>或<property>的設定上,name設定類別上的屬性名,而column對應至表格欄位,如果屬性名稱與欄位名稱相同,則可以省略column屬性設定。

<id>或<property>上,可以設定type屬性,在type上可以設定Java類別的資料 型態,但由於 Java的資料型態與資料庫的資料型態並不是一對一對應的,為此Hibernate提供它自己的資料型態,作為Java資料型態與資料庫資料型態的連接型 態,下面的表格列出型態之間的對應:
Java 資料型態 Hibernate 資料型態 標 準SQL資料型態
byte、java.lang.Byte byte TINYINT
short、java.lang.Short short SMALLINT
int、java.lang.Integer integer INGEGER
long、java.lang.Long long BIGINT
float、java.lang.Float float FLOAT
double、java.lang.Double double DOUBLE
java.math.BigDecimal big_decimal NUMERIC
char、java.lang.Character character CHAR(1)
boolean、java.lang.Boolean boolean BIT
java.lang.String string VARCHAR
boolean、java.lang.Boolean yes_no CHAR(1)('Y'或'N')
boolean、java.lang.Boolean true_false CHAR(1)('Y'或'N')
java.util.Date、java.sql.Date date DATE
java.util.Date、java.sql.Time time TIME
java.util.Date、java.sql.Timestamp timestamp TIMESTAMP
java.util.Calendar calendar TIMESTAMP
java.util.Calendar calendar_date DATE
byte[] binary VARBINARY、BLOB
java.lang.String text CLOB
java.io.Serializable serializable VARBINARY、BLOB
java.sql.Clob clob CLOB
java.sql.Blob blob BLOB
java.lang.Class class VARCHAR
java.util.Locale locale VARCHAR
java.util.TimeZone timezone VARCHAR
java.util.Currency currency VARCHAR

一個設定的方式如下所示:
<property name="name" column="name" type="string"/>

每個內建的Hibernate類型,在 org.hibernate.Hibernate類別中都有對應的常數,例如Hibernate.STRING。基本上,Hibernate會使用 Reflection自動找出屬性的資料型態,所以type屬性在使用POJO時可以省略不設,如果有特別設定,則Hibernate就不用使用 Reflection來找出類型,不過在使用 動 態模型(Dynamic Model),type屬性則是必須設定的,用以得知動態模型中應放入哪種類型的物件。

<generator>設定主鍵的生成方式,可以設定"native"表示由Hibernate自動根據Dialect選擇 採用 identity、hilo、sequence等作為主鍵生成方式,也可以考慮採用uuid由Hibernate根據128位元UUID演算法(128- bit UUID algorithm)生成16進位制數值,並編碼為32位長度的字串,還有其它的主鍵生成方式,可以參考官方手冊的 Generator 說明。

您可以在<hibernate-mapping>上設定package屬性,如此一來,文件中要設置類別名稱時,就不用寫出完整的package,例如:
<hibernate-mapping package="onlyfun.caterpillar">
    <!--類別名稱與表格名稱映射-->
    <class name="User" table="user">
        ....
    </class>
</hibernate-mapping>

在使用HQL時,您可以只使用類別名稱來替代完整名稱:
// 相當於寫"from onlyfun.caterpillar.User"
Query query = session.createQuery("from User"); 

這是Hibernate的auto-import功能,然而如果在不同的package下都有User類別,則Hibernate將無從得知是要使用哪個User類別,您可以在<hibernate-mapping>上設定auto-import屬性為false,關閉auto-import功能,並在HQL中撰寫完整的類別名稱。

另一種解決的方式,是在<hibernate-mapping>中使用<import>設定別名,例如:
<hibernate-mapping>
    <import class="onlyfun.caterpillar.User" rename="DemoUser"/>
</hibernate-mapping>

之後在指定HQL中,即可使用這個別名:
Query query = session.createQuery("from DemoUser"); 

對於一些不能為空的屬性,可以在<property>上加上not-null屬性為true,如此Hibernate可以直接檢查屬性是否為null,而不用等進入到資料庫中再作檢查。

如果有某個屬性,其值取決於表格欄位自己產生的值,而非程式中主動設定的值,例如資料新增時,會由資料庫產生新增時的時間,而這個值想要主動提取至物件的對應屬性,則可以在<property>上設定generated屬性,例如:
...
    <property name="time" column="time"
              insert="false" update="false" generated="always"/>
...

如上設定之後,當物件儲存時,time屬性並不會參與儲存,而是由資料庫產生time欄位值,再SELECT出來設定給time屬性,由於並非實際要儲存屬性,所以設定insert為false,而由於這個欄位由資料庫維護,所以update設定為false。

如果物件上有個屬性,實際上並沒有欄位與之對應,您只是想藉由資料庫中的欄位查詢來取得,例如使用COUNT函式來取得所有的筆數,則您可以使用formula屬性,例如:
...
    <property name="average" formula="(SELECT AVG(u.age) FROM T_USER u)"/>
...