天天看點

三、ORM(Object/Relational Mapper)---Hibernate對象映射

Hibernate基本類型

Java類型

标準SQL字段類型

boolean

boolean, java.lang.Boolean

BIT

yes_no

CHAR(1) ( 'Y'/'N')

true_false

byte

byte, java.lang.Byte

TINYINT

short

short, java.lang.Short

SMALLINT

integer

int, java.lang.Integer

INTEGER

long

long, java.lang.Long

BIGINT

float

float, java.lang.Float

FLOAT

double

double, java.lang.Double

DOUBLE

big_decimal

java.math.BigDecimal

NUMBERIC

character

java.lang.String

CHAR(1)

string

VARCHAR

date

java.util.Date, java.sql.Date

DATE

time

java.util.Date, java.sql.Time

TIME

timestamp

java.util.Date, java.sql.TimeStamp

TIMESTAMP

calendar

java.util.Calendar

calendar_date

Date

clob

java.sql.Clob

CLOB

blob

java.sql.Blob

BLOB

binary

byte[]

VARBINARY, BLOB

text

seralizable

java.io.Serializable

class

java.lang.Class

local

java.util.Locale

timezone

java.util.TimeZone

currency

java.util.Currency

*.hbm.xml, 主體内容包含表/類映射, id映射, 屬性字段映射三個部分.示例檔案:

<?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="cn.thinkmore.hibernate.pojo.Tuser" table="T_user">

<id column="id" name="id" type="java.lang.Integer">

<generator class="increment" />

</id>

<property column="name" name="name" type="java.lang.String"/>

</class>

</hibernate-mapping>

XML檔案頭定義了檔案的編碼方式, DTD與Hibernate的版本有關, 上面是使用hibernate 3.x使用的DTD.

name: 指定了Java資料封裝的POJO類類型.

table: 指定了資料庫的表名

<id name="id" type="java.lang.Integer" column="id">

name: 指定了映射POJO類的屬性名,

type: POJO類中屬性的資料類型;

column: 資料庫表的主鍵字段;

generator子元素:由其class屬性指定主鍵生成方式:

assigned: 主鍵由應用程式産生, 不需要hibernate幹預

identity: 使用資料庫的主鍵生成機制, 如MySQL, DB2、SQL Server的自增主鍵.

sequence: 使用資料庫的sequence機制, 如Oracle的sequence

uuid.hex: 由Hibernate基于128位唯一值産生算法, 根據ip, 時間, jvm啟動時間, 内部自增量生成十六進制的數值, 編碼後成為一個32位長的字元串. 該方法提供了最好的資料庫插入性能和資料庫平台适應性.

uuid.string: 與uuid.hex類似, 隻是生成的主鍵沒有進行編碼, 隻有16位長度. 在某些資料庫可能出錯.

hilo: 通過hilo算法實作主鍵生成, 需要額外的資料庫表儲存主鍵生成曆史狀态.

seqhilo: 與hilo類似, 隻是主鍵曆史狀态儲存在sequence中, 适用于支援sequence的資料庫.

increment: 主鍵按數值遞增, 但如果多個執行個體同時通路同一個資料庫, 各自生成主鍵,則容易造成主鍵重複.

native: 由hibernate根據資料庫擴充卡中的定義, 自動采用identity, hilo, sequence中的一種方式.

foreign: 外部表的字段作主鍵.

select: hibernate 3 中新增的.需要提供一個唯一的辨別字段進行二次讀取, 以擷取觸發器生成的主鍵值, 通過param子元素進行定義, 比如:

<generator class="select">

<param name="key">key_field</param>

</generator>

該方法主要針對遺留系統的改造工程, 一些早期的系統主鍵依賴于觸發器生成. 當資料庫insert時, 觸發器捕獲這一操作, 并為主鍵指派, 在插入資料庫後, 再次讀取某一識别字段讀取已經插入的資料, 擷取其主鍵值.

複合主鍵使用<composite-id>将取代id元素, 并具有property屬性清單.

<composite-id>

<key-property column="userid" name="userid" type="java.lang.String" />

<key-property column="when" name="when" type="java.sql.Date" />

</composite-id>

複合主鍵的POJO類需要實作equals和hashcode方法, 可以使用apache commons lang包中的工具類實作(commons-lang.jar), 比如:

import org.apache.commons.lang.builder.EqualsBuilder;

import org.apache.commons.lang.builder.HashCodeBuilder;

import org.apache.commons.lang.builder.ToStringBuilder;

...

public String toString(){

return new ToStringBuilder(this)

.append("userid ", getUserid ())

.append("when", getWhen ())

.toString();

}

public boolean equals(Object other){

if(!(other instanceof MyPoJoClass)){

return false;

MyPoJoClass castOther=( MyPoJoClass)other;

return new EqualsBuilder()

.appendSuper(super.equals(other))

.append(this.getUserid (),castOther.getUserid() )

.append(this.getWhen (),castOther.getWhen() )

.isEquals();

public int hashCode(){

return new HashCodeBuilder()

.appendSuper(super.hashCode() )

.append(getUserid ())

.append(getWhen ())

.toHashCode();

裝載複合主鍵的記錄時, 考慮把類對應的對象的主鍵值填充好後作為load的第二個參數.

MyPoJoClass obj = new MyPoJoClass();

obj.setUserid(...);

obj.setWhen(...);

obj=(MyPoJoClass)session.load(MyPoJoClass.class, obj);

可以用一個獨立的類來描述主鍵, 示例:

<composite-id name="keyClassProperty" class="MyPoJoClass">

此時, 需要定義一個新的類KeyClass來作為主鍵類, KeyClass實作equals和hashcode方法, 而在POJO中, 使用屬性名keyClassProperty來表示主鍵, 其類類型為KeyClass.

composite-id除了key-property子節點外, 還具有可選<key-many-to-one>子節點.完整的節點内容:

<composite-id name="propertyName"

class="ClassName"

unsaved-value="any|none" >

<key-property column="colum_name"

name=" propertyName "

type="type_name" />

<key-many-to-one column=" colum_name " calss=" ClassName "

name=" propertyName " />

<property name="name" type="java.lang.String" column="name" />

name: POJO類的屬性名,

type: POJO類中屬性的資料類型; 如果是字元串,可以省略

column: 資料庫中的字段名. 如果和屬性同名, 可以省略.

hibernate3 中的為: org.hibernate.usertype(hibernate2中為net.sf包)下面的兩個接口: UserType 和 CompositeUserType . 它們提供自定義類型的規範, 這裡以UserType為例.

package org.hibernate.usertype;

import java.io.Serializable;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

import org.hibernate.HibernateException;

public interface UserType {

/**

* 傳回本類型所映射的SQL類型, 傳回的是int[]數組. 可以取java.sql.Types中的int靜态常量.

* 比如有: public final static int DOUBLE = 8;

*/

public int[] sqlTypes();

* 定義本類型的nullSafeGet() 方法傳回的資料的類型

public Class returnedClass();

* 定義兩個資料的比較方法, 傳回true表示相等, false表示不等.

public boolean equals(Object x, Object y) throws HibernateException;

* Get a hashcode for the instance, consistent with persistence "equality"

public int hashCode(Object x) throws HibernateException;

* 定義從ResultSet中讀取資料并轉換成自定義類型的方法, 對資料庫null應該考慮

* 參數names 包含了目前自定義類型的映射字段名稱

public Object nullSafeGet(ResultSet rs, String[] names, Object owner)

throws HibernateException, SQLException;

* Hibernate進行資料儲存時被調用的方法

* @param st a JDBC prepared statement

* @param value the object to write, 類型應該是returnedClass()方法傳回的Class指定的類型

* @param index statement parameter index

* @throws HibernateException

* @throws SQLException

public void nullSafeSet(PreparedStatement st, Object value, int index)

* 自定義的對象複制方法, 用作構造傳回對象.

* nullSafeGet方法被調用後, Hibernate獲得了自定義資料對象,

* 這個對象成為資料庫讀出的原始對象, 通過deepCopy方法, Hibernate

* 傳回一個複本給使用者. 髒資料檢查時, 如果兩個對象不等(equals方法傳回false),

* 就會執行資料庫持久化操作.

* @param value the object to be cloned, which may be null

* @return Object a copy

public Object deepCopy(Object value) throws HibernateException;

* Are objects of this type mutable?

* 本類型執行個體是否可變

* @return boolean

public boolean isMutable();

* 把對象轉化為二級緩存中儲存. 經常把目前對象對其他對象的引用, 儲存為其id值.

* Transform the object into its cacheable representation. At the very least this

* method should perform a deep copy if the type is mutable. That may not be enough

* for some implementations, however; for example, associations must be cached as

* identifier values. (optional operation)

*

* @param value the object to be cached

* @return a cachable representation of the object

public Serializable disassemble(Object value) throws HibernateException;

* 把二級緩存中擷取的資料轉換為自定義的對象資料

* Reconstruct an object from the cacheable representation. At the very least this

* method should perform a deep copy if the type is mutable. (optional operation)

* @param cached the object to be cached

* @param owner the owner of the cached object

* @return a reconstructed object from the cachable representation

public Object assemble(Serializable cached, Object owner) throws HibernateException;

* During merge, replace the existing (target) value in the entity we are merging to

* with a new (original) value from the detached entity we are merging. For immutable

* objects, or null values, it is safe to simply return the first parameter. For

* mutable objects, it is safe to return a copy of the first parameter. For objects

* with component values, it might make sense to recursively replace component values.

* @param original the value from the detached entity being merged

* @param target the value in the managed entity

* @return the value to be merged

public Object replace(Object original, Object target, Object owner)

throws HibernateException;

如果一個使用者具有多個email, 可以分别定義多個字段儲存, 也可以定義一個子表專門儲存, 但都有點大動幹戈. 可以考慮用一個字元串字段儲存, 相鄰的email用一個;隔開. 比如:

package cn.thinkmore.hibernate.pojo;

import java.io.Serializable;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.sql.Types;

import java.util.*;

import org.hibernate.Hibernate;

import org.hibernate.HibernateException;

import org.hibernate.usertype.UserType;

public class EmailList implements UserType {

private static final String SPLITTER = ";";

private static final int[] TYPES = new int[] { Types.VARCHAR };

public Object assemble(Serializable cached, Object owner)

throws HibernateException {

return null;

public Object deepCopy(Object value) throws HibernateException {

if (!(value instanceof List)) {

List src = (List) value;

List tar = new ArrayList();

tar.addAll(src);

return tar;

public Serializable disassemble(Object value) throws HibernateException {

public boolean equals(Object x, Object y) throws HibernateException {

if (x == y)

return true;

if (x != null && y != null) {

List xList = (List) x;

List yList = (List) y;

if (xList.size() != yList.size())

return false;

for (int i = 0; i < xList.size(); i++) {

String strX = xList.get(i).toString();

String strY = yList.get(i).toString();

if (!strX.equals(strY))

public boolean isMutable() {

public Object nullSafeGet(ResultSet rs, String[] names, Object owner)

throws HibernateException, SQLException {

String value = Hibernate.STRING.nullSafeGet(rs, names[0]).toString();

if(null!=value)

return parse(value);

private List parse(String value) {

String[] strs = org.apache.commons.lang.StringUtils.split(value,SPLITTER);

List<String> emails = new ArrayList<String>();

for(int i=0; i<strs.length;i++){

emails.add(strs[i]);

return emails;

private String assemble(List list) {

StringBuilder sb = new StringBuilder();

int maxIndex = list.size()-1;

for(int i=0; i<maxIndex;i++){

sb.append(list.get(i)).append(SPLITTER);

if(0<=maxIndex){

sb.append(list.get(maxIndex));

return sb.toString();

public void nullSafeSet(PreparedStatement st, Object value, int index)

if(null!=value&&(value instanceof List)){

String str = assemble((List)value);

Hibernate.STRING.nullSafeSet(st,str,index);

}else if(null!=value){

Hibernate.STRING.nullSafeSet(st,value.toString(), index);

}else{

Hibernate.STRING.nullSafeSet(st,null, index);

public Object replace(Object original, Object target, Object owner)

public Class returnedClass() {

return List.class;

public int[] sqlTypes() {

return TYPES;

public int hashCode(Object x) throws HibernateException {

return 0;

資料關聯關系在實體的子類裡面不會被自動繼承.

一對一的主鍵關聯, 指兩個表的記錄是一對一的關系, 且一個表的主鍵取為另一個表對應的記錄的主鍵.

<class name="cn.thinkmore.hibernate.pojo.Citizen" table="citizen" lazy="true">

<id name="id" column="id">

<generator class="uuid.hex" />

<property name="name" />

<property name="sex"/>

<property name="idcard" />

<property name="address"/>

<one-to-one name="gene" cascade="all" outer-join="true"

class="cn.thinkmore.hibernate.pojo.Gene" />

name: 屬性名

class: java全路徑類名

cascade: 是否級連操作, all , none

outer-join: 是否外連結, true/false

<class name="cn.thinkmore.hibernate.pojo.Gene" table="gene" lazy="true">

<id name="id" column="id" >

<generator class="foreign">

<param name="property">citizen</param>

<property name="dna" />

<property name="chromosome"/>

<property name="species"/>

<one-to-one name="citizen" constrained="true"

class="cn.thinkmore.hibernate.pojo.Citizen" />

Constrained: 次關聯中的限制方.

.....

Session s = HibernateSession3.getSession();

Citizen one = new Citizen();

one.setIdcard("123456789012345678");

one.setName("Mike");

one.setSex("M");

Gene gene = new Gene();

gene.setBloodType("AB");

gene.setDna("Ajodjjd[[joa");

gene.setSpecial("White");

one.setGene(gene);

gene.setCitizen(one);

Transaction trans = s.beginTransaction();

s.saveOrUpdate(one);

trans.commit();

// 兩條資料一起被儲存, 查詢時也會關聯查出來.

Query query = s.createQuery("From Citizen");

List list = query.list();

for (int i = 0; i < list.size(); i++) {

one = (Citizen) list.get(i);

if (null == one)

continue;

gene = (Gene) one.getGene();

if (null != gene){

System.out.println(gene.getId()+":"+gene.getSpecial()+":"+gene.getDna());

一個使用者可以多個email:

<set name="email" table="emails" cascade="all">

<key column="userid" />

<one-to-many calss="....Email" />

</set>

除了在主要方配置一對多, 還要在被控方配置對應的多對一關系:

User:

<set name="email" table="emails" cascade="all" inverse="true">

Email:

<many-to-one name="user" class=".....User" cascade="none" column="userid" />

再有例子:一個職員(是人的子類)前後可能有多條工作(每次工作是一條記錄):

<hibernate-mapping >

<class name="webapp.hibernate.pojo.People" table="people" lazy="false">

<comment>Users may bid for or sell auction items.</comment>

<id name="id">

<generator class="uuid.hex"/>

<property name="birth" type="date"/>

<one-to-one name="gene"

cascade="all"

class="webapp.hibernate.pojo.Gene"/>

<class name="webapp.hibernate.pojo.Employee" table="people" lazy="false">

<set name="jobs" table="joblist" cascade="all" inverse="true">

<key column="empid" />

<one-to-many class="webapp.hibernate.pojo.JobList" />

<class name="webapp.hibernate.pojo.JobList" table="joblist" lazy="false">

<!-- <property name="userid" column="empid"/> -->

<property name="company" />

<property name="begin" type="date"/>

<property name="end" type="date"/>

<many-to-one name="employee" class="webapp.hibernate.pojo.Employee" cascade="none" column="empid" />

public static void testOne2Many() {

Employee e = new Employee();

e.setName("一對多");

e.setIdcard("123456789012345678");

e.setBirth(new java.sql.Date(System.currentTimeMillis()-24L*3600*1000*365*25));

JobList job1 = new JobList();

job1.setEmployee(e);

job1.setCompany("A gongsi");

job1.setBegin(new java.sql.Date(System.currentTimeMillis()-24L*3600*1000*365*5));

job1.setEnd(new java.sql.Date(System.currentTimeMillis()-24L*3600*1000*365*4));

JobList job2 = new JobList();

job2.setEmployee(e);

job2.setCompany("B gongsi");

job2.setBegin(new java.sql.Date(System.currentTimeMillis()-24L*3600*1000*365*4));

job2.setEnd(new java.sql.Date(System.currentTimeMillis()-24L*3600*1000*365*2));

Set<JobList> jobs = new HashSet<JobList>();

jobs.add(job1);

jobs.add(job2);

e.setJobs(jobs);

// 兩個表的資料一起被儲存, 查詢時也會關聯查出來.

s.saveOrUpdate(e);

Query query = s.createQuery("From Employee");

Employee one = (Employee) list.get(i);

System.out.println(one.getName()+":"+one.getBirth()+":");

Set joblist = one.getJobs();

for(Object o:joblist){

JobList j = (JobList)o;

System.out.println("\t工作:"+j.getCompany()+":"+j.getBegin()+":"+j.getEnd());

System.out.println("---------------------------------------");

一個使用者可以在銀行有多個帳号

<class name="webapp.hibernate.pojo.MoneyUser" table="people" lazy="false">

<set name="banks" table="bankuser" cascade="none" >

<many-to-many class="webapp.hibernate.pojo.Bank" column="bankid" />

Bank:

<class name="webapp.hibernate.pojo.Bank" table="bank" lazy="false">

<property name="bankname" />

<property name="type"/>

<property name="createday" type="date"/>

<set name="cutomers" table="bankuser" cascade="none" >

<key column="bankid" />

<many-to-many class="webapp.hibernate.pojo.MoneyUser" column="userid" />

public static void testMany2Many() {

Iterator<MoneyUser> itor =(Iterator<MoneyUser>)s.createQuery("FROM MoneyUser").iterate();

while(itor.hasNext()){

MoneyUser moneyUser = itor.next();

Set banks = moneyUser.getBanks();

Iterator<Bank> bankItor = banks.iterator();

while(bankItor.hasNext()){

Bank bank = bankItor.next();

System.out.println(moneyUser.getName()+" "+bank.getBankname());

public static void testMany2Many2() {

Iterator<Bank> itor = s.createQuery("FROM Bank").iterate();

Bank bank = itor.next();

Set customers = bank.getCutomers();

Iterator<MoneyUser> customerItor = customers.iterator();

while(customerItor.hasNext()){

MoneyUser moneyUser = customerItor.next();

System.out.println(bank.getBankname()+" "+moneyUser.getName());

本文轉自linzheng 51CTO部落格,原文連結:http://blog.51cto.com/linzheng/1080830