? Hibernate是一個中間層,它的目的是把數據庫中的關系通過一定的規則映射成為對象,讓Java開發人員不用太多的考慮底層數據庫的問題,只需要像通常情況下管理對象一樣的管理數據。在關系數據庫仍將持續占據市場的情況下,它很可觀。在數據持久化領域,即便是輕量級的方案也會是復雜饒舌的,也許如同周杰倫的音樂一樣不知所云。在學習它之前,最好先回想一下以前進行數據庫開發中遇到的問題和不便,想想為什么需要一個持久化層,才能知道很多操作的目的是什么,以及為什么要這么干,在這個問題上我不想做更多的敘述,因為“長久以來……”這樣的句式通常long(不好意思,打不出來)長,會對我的鍵盤和熱情造成很大的磨損。如果讓我寫一本書,那么我會樂意去敘述什么是數據持久化,它有什么好處等等。廢話少說,來了。
? 首先需要配置環境,下載Hibernate 2.1(www.hibernate.org),把lib下的*.jar添加到classpath,你的數據庫JDBC驅動程序也應該在classpath中。打開hibernate.properties,針對你使用的數據庫,配置相應的信息,比如我使用的是MS SQL Server,配置如下:
?## MS SQL Server
?hibernate.dialect net.sf.hibernate.dialect.SQLServerDialect
?hibernate.connection.driver_class com.microsoft.jdbc.sqlserver.SQLServerDriver
?hibernate.connection.url jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=zizz
?hibernate.connection.username sa
?hibernate.connection.password
? 其中很大部分是已經寫好的,只需要取掉注釋即可,我自己只是修改了數據庫名稱、帳號、密碼。建立一個名為zizz的數據庫備用。
? 然后把這個文件拷貝到你的應用的根目錄下。
? 我們談論了很多次映射,應該首先來看看這個映射是如何完成的。假設一個最簡單的應用,寫一個功能最單一的留言板,設計的數據有留言的編號、留言者名稱、留言內容,還有留言時間。足夠簡單吧,換做是你打算怎么干?我猜你要首先建立一個數據庫表格,名字也許叫做guestbook。No,這不是面向對象的方式,不妨首先從對象的角度來考慮。我們當然希望每一條留言都以對象的方式呈現,每個對象應該具有的屬性有:id、author、content、time。偷個懶,沒有畫UML。下面這個類應該是很容易理解的:
?//GuestBook.java
?package org.bromon.zizz;
?import java.util.*;
?public class GuestBook
?{
?private int id;
?private String author;
?private String content;
?private Calendar time;
?private void setId(int id)
?{
? this.id=id;
?}
?public int getId()
?{
? return(id);
?}
?public void setAuthor(String author)
?{
? this.author=author;
?}
?public String getAuthro()
?{
? return(author);
?}
?public void setContent(String content)
?{
? this.content=content;
?}
?public String getContent()
?{
? return(content);
?}
?public void setTime(Calendar time)
?{
? this.time=time;
?}
?public Calendar getTime()
?{
? return(time);
?}
?}
? 基本上是最簡單的Bean了,如果覺得困難的話,請你先回火星等我。
? 需要注意的是setId方法被指定為private,這是因為我希望用這個字段做主鍵,它最好由系統自動生成,所以不應該由用戶來指定,這個方法專為Hibernate準備,所以是私有的。
? 如何把這個類與數據庫映射起來?看看Hibernate的魔法,使用一個XML文件來描述,它應該被命名為GuestBook.hbm.xml:
?< ?xml version="1.0"? >
?< !DOCTYPE hibernate-mapping PUBLIC
??????? "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
??????? "' target=_blank >http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >
?< hibernate-mapping package="org.bromon.zizz" >
??? < class name="GuestBook" table=”guestbook" lazy="true" >
??????? < id name="id" type="integer" unsaved-value="null" >
?? < column name="id" sql-type="int" not-null="true"/ >
?? < generator class="identity"/ >
? < /id >
???????
? < property name="author" column="author" not-null="true" unique="false"/ >
?< property name="content" column="content" not-null="true"/ >
? < property name="time" column="time" not-null="true"/ >
??? < /class >
?< /hibernate-mapping >
?雖然有點陌生,但是很易讀,仔細琢磨一下。
?下面來編寫我們的應用,它的功能是插入數據:
?//Operate.java
?package org.bromon.zizz;
?import net.sf.hibernate.*;
?import net.sf.hibernate.cfg.*;
?import net.sf.hibernate.tool.hbm2ddl.*;
?import java.util.*;
?public class Operate
?{
?public static void main(String args[])
?{
? try
? {
?? Configuration cfg=new Configuration().addClass(GuestBook.class);
?? SessionFactory sessions=cfg.buildSessionFactory();
?? new SchemaExport(cfg).create(true,true);
?? Session session=sessions.openSession();
??
?? GuestBook gb=new GuestBook();
?? gb.setAuthor(“Bromon”);
?? gb.setContent(“留言的內容”);
?? gb.setTime(Calendar.getInstance());
??
?? Transaction ts=session.beginTransaction();
?? session.save(gb);
?? ts.commit();
?? session.close();
? }catch(Exception e)
? {
?? System.out.println(e);
? }
?}
?}
? 編譯吧:javac –d . *.java
? 執行一下:java org.bromon.zizz.Operate
?到數據庫里面看看,表格已經建立好了,并且數據也已經保存。如果把
?new SchemaExport().create(true,true);
?注釋掉,那么系統不會創建表格,而只是在已有的表格中添加新的記錄,當然,如果表格不存在的話,會產生異常。
?你已經看到了Hibernate神奇魔法的5%,它足夠的復雜強大,可以讓你應付復雜的應用。
one-to-one關系
?在絕大多數系統當中不可能只存在一個數據表格,否則就違背了關系數據庫的初衷。表與表的關系比較復雜,可以分為幾種情況:
?● 一對一關聯(one to one)
?● 一對多關聯(one to many)
?● 多對一關聯(many to one)
?● 多對多關聯(many to many)
?按順序來講。假設一個一對一關聯的例子是:
?表格:person
?id 編號(主鍵)
?name 姓名
?email email地址
?表格:spouse
?id 編號(外鍵)
?name 姓名
?person這個表保存用戶信息,而spouse保存用戶配偶的信息。在一般情況下一個人只有一個配偶,這很適合我們一對一的情況。如果你對婚外戀感興趣的話,我們可以在一對多和多對一的關聯中討論這個問題,也許還可以在多對多中^_^(禽獸!)。
?OK,下面設計POJO:
?Person這個類非常簡單:
?/*
?* Created on 2004-4-19
?*/
?package org.bromon.zizz;
?/**
?* @author Bromon
?*/
?public class Person
?{
?private int id;
?private String name;
?private String email;
?public void setId(int id)
?{
? this.id=id;
?}
?public int getId()
?{
? return(id);
?}
?public void setName(String name)
?{
? this.name=name;
?}
?public String getName()
?{
? return(name);
?}
?public void setEmail(String email)
?{
? this.email=email;
?}
?public String getEmail()
?{
? return(email);
?}
?}
?然后編寫它的映射規則,這個應該能夠理解了:
?< ?xml version="1.0"? >
?< !DOCTYPE hibernate-mapping PUBLIC
?"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
?"' target=_blank >http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >?
?< hibernate-mapping package="org.bromon.zizz" >
?< class name="Person" table="person" lazy="true" >
?< id name="id" type="integer" unsaved-value="null" >
?< column name="id" sql-type="int" not-null="true"/ >
?< generator class="identity"/ >
?< /id >
?< property name="name" column="name" not-null="true" unique="false"/ >
?< property name="email" column="email" not-null="false"/ >
?< /class >
?< /hibernate-mapping >
?so easy是不是?一切都按部就班。下面是Souse類:
?/*
?* Created on 2004-4-20
?*/
?package org.bromon.zizz;
?/**
?* @author Bromon
?*/
?public class Spouse
?{
?private int id;
?private String name;
?private Person person;
?public void setId(int id)
?{
? this.id=id;
?}
?public int getId()
?{
? return(id);
?}
?public void setName(String name)
?{
? this.name=name;
?}
?public String getName()
?{
? return(name);
?}
?public void setPerson(Person person)
?{
? this.person=person;
?}
?public Person getPerson()
?{
? return(person);
?}
?}
?注意里面的域person。它的映射文件:
?< ?xml version="1.0"? >
?< !DOCTYPE hibernate-mapping PUBLIC
?"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
?"' target=_blank >http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >?
?< hibernate-mapping package="org.bromon.zizz" >
?< class name="Spouse" table="spouse" lazy="true" >
?< id name="id" type="integer" unsaved-value="null" >
?< column name="id" sql-type="int" not-null="true"/ >
?< generator class="foreign" >
?< param name="property" >person< /param >
?< /generator >
?< /id >
?< property name="name" column="name" not-null="true" unique="false"/ >
?< one-to-one name="person" class="Person" cascade="all" constrained="true" / >
?< /class >
?< /hibernate-mapping >
?這里指明了id的generator是一個外鍵,和person相關聯。然后指定一個one-to-one關系,不難理解是不是?Hibernate的確很符合我們的思維習慣。需要提醒的是,這種關聯關系是單向的,Person并不需要去指定Spouse。
?下面來操作這兩個類:
?/*
?* Created on 2004-4-20
?*/
?package org.bromon.zizz;
?import net.sf.hibernate.*;
?import net.sf.hibernate.cfg.*;
?import net.sf.hibernate.tool.hbm2ddl.*;
?/**
?* @author Bromon
?*/
?public class OperateSpouse
?{
?public static void main(String args[])
?{
? try
? {
?? Configuration cfg=new Configuration().addClass(Spouse.class);
?? cfg.addClass(Person.class);
?? SessionFactory factory=cfg.buildSessionFactory();
?? new SchemaExport(cfg).create(true,true);
?? Session session=factory.openSession();
?
?? Person person=new Person();
?? person.setName("bromon");
?? person.setEmail("bromon@163.com");
?
?? Spouse spouse=new Spouse();
?? spouse.setName("who");
?? spouse.setPerson(person);
??
?? Transaction ts=session.beginTransaction();
?? session.save(person);
?? session.save(spouse);
?? ts.commit();
?? session.close();
? }catch(Exception e)
? {
?? System.out.println(e);
? }
?}
?}
?這個例子和第一篇中的例子非常相似。OK,執行一下,然后看看zizz數據庫,搞掂。
Many-to-One關系
?很明顯一對多或者多對一關系是關系數據庫中非常常見的現象,下面通過父親-兒子的例子來演示一對多關系,多對一關系是類似的,不過在我們的這個例子中不宜采用,否則會帶來倫理學上的問題。
?首先定義Child類:
?/*
? * Created on 2004-5-8
? */
?package org.bromon.zizz;
?/**
? * @author Bromon
? */
?public class Child
?{
? private int id;
? private String name;
? private int fatherId;
? private Person father;
? public Child(){}
?
? /**
?? * @return
?? */
? public Person getFather()
? {
?? return father;
? }
? /**
?? * @return
?? */
? public int getFatherId()
? {
?? return fatherId;
? }
? /**
?? * @return
?? */
? public int getId()
? {
?? return id;
? }
? /**
?? * @return
?? */
? public String getName()
? {
?? return name;
? }
? /**
?? * @param person
?? */
? public void setFather(Person p)
? {
?? father = p;
? }
? /**
?? * @param i
?? */
? public void setFatherId(int i)
? {
?? fatherId = i;
? }
? /**
?? * @param i
?? */
? public void setId(int i)
? {
?? id = i;
? }
? /**
?? * @param string
?? */
? public void setName(String string)
? {
?? name = string;
? }
?}
?這里的fatherId是外鍵,關聯person表的id字段。
?下面是映射文件Child.hbm.xml:
?< ?xml version="1.0"? >
?< !DOCTYPE hibernate-mapping PUBLIC
??????? "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
??????? "' target=_blank >http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >?
?< hibernate-mapping package="org.bromon.zizz" >
? < class name="Child" table="child" lazy="true" >
?? < id name="id" type="integer" unsaved-value="null" >
??? < column name="id" sql-type="int" not-null="true"/ >
??? < generator class="identity"/ >
?? < /id >
???????
?? < property name="name" column="name" not-null="true" unique="false"/ >
?? < many-to-one name="father" column="fatherid" / >
??????
? < /class >
?< /hibernate-mapping >
?需要注意的是fatherId并沒有做為一個property被映射,而是在many-to-one聲明中使用。
?需要對Person..java做修改,添加以下代碼:
?import java.util.*;
?private Set children=new HashSet();
?/**
?* @return
?*/
?public Set getChildren()
?{
? return children;
?}
?/**
?* @param set
?*/
?public void setChildren(Set set)
?{
? children = set;
?}
?然后修改Person.hbm.xml,對添加的代碼做映射:
?< set name="books" lazy="true" inverse="true" cascade="all"? >
? < key column="fatherid"/ >
? < one-to-many class="Child" / >
?< /set >
?這里的key column是child表的外鍵,inverse需要指定為true。
?下面做操作一下,功能是查詢person表中id=1的記錄,作為小孩的父親,然后給child表添加一條新記錄。
?/*
? * Created on 2004-5-8
? */
?package org.bromon.zizz;
?import net.sf.hibernate.*;
?import net.sf.hibernate.cfg.*;
?import net.sf.hibernate.tool.hbm2ddl.*;
?/**
? * @author Bromon
? */
?public class OperateChild
?{
? /**
?? * @param args
?? */
? public static void main(String args[])
? {
?? try
?? {
??? Configuration cfg = new Configuration().addClass(Person.class);
??? cfg.addClass(Child.class);
??? SessionFactory sessions = cfg.buildSessionFactory();
??? new SchemaExport(cfg).create(true, true);
??? Session session = sessions.openSession();
???
??? Child c=new Child();
???
??? /*Query q=session.createQuery("from org.bromon.zizz.Person as p where p.id=1");
??? Person p=(Person)q.list().get(0);*/
???
??? Person p=(Person)session.find("from org.bromon.zizz.Person as p where p.id=?",new Integer(1),Hibernate.INTEGER).get(0);
??? System.out.println(p.getName());
??? c.setName("andy");
??? c.setFather(p);
???
??? Transaction ts = session.beginTransaction();
??? session.save(c);
??? ts.commit();
??? session.close();
?? } catch (Exception e)
?? {
??? System.out.println(e);
?? }
? }
?}
?被注釋掉的部分是HQL的另外一種查詢方法。在這個例子中可以看出對象的查詢非常容易,不需要自己再去封裝數據,修改和刪除對象也很容易:
?//得到一個對象
?Query q=session.createQuery("from org.bromon.zizz.Person as p where p.id=1");
?Person p=(Person)q.list().get(0);
?//修改數據
?p.setName(“Mr Smith”);
?//保存數據
?session.save(p);
?session.flush();
?//刪除數據
?session.delete(p);
?session.flush();