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 | |
+-------+------------------+------+------+----------+---------------------+
| 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>
<!--類別名稱與表格名稱映射-->
<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>
<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"/>
...
<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)"/>
...
<property name="average" formula="(SELECT AVG(u.age) FROM T_USER u)"/>
...