对象关系映射(Object Relative Mapping)简称ORM,是面向对象开发的一个热点,用来解决JDBC开发中手动进行OR映射的繁杂与不便。EJB中的实体Bean在这个领域是很著名的——既因为它的先进而著名,也因为它的低效而著名。有过实体Bean开发经验的人可能都会为实现远程接口造成的效率低下而头痛,在很多不大不小的项目中,使用实体Bean是否得不偿失,争论很大。一个轻量级的持久化方案也许能够解决一些问题,Hibernate应此而生。
基本上是最简单的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();