-- 从 hbm.xml 到 Annotations
作者:icess(作者的blog:http://blog.matrix.org.cn/page/icess)
下面让我们先看一个通常用 hbm.xml 映射文件的例子. 有3个类 .HibernateUtil
.java 也就是 Hibernate文档中推荐的工具类,Person
.java 一个持久化的类, Test.java 测试用的类.都在test.hibernate
包中. 每个类的代码如下:
01 package test.hibernate;
02
03 import org.hibernate.HibernateException;
04 import org.hibernate.Session;
05 import org.hibernate.SessionFactory;
06 import org.hibernate.cfg.Configuration;
07
08 public class HibernateUtil {
09 public static final SessionFactory sessionFactory;
10
11 static {
12 try {
13 sessionFactory = new Configuration()
14 .configure()
15 .buildSessionFactory();
16 } catch (HibernateException e) {
17 // TODO Auto-generated catch block
18
19 e.printStackTrace();
20 throw new ExceptionInInitializerError(e);
21 }
22 }
23
24 public static final ThreadLocal<Session> session = new ThreadLocal<Session>();
25
26 public static Session currentSession() throws HibernateException {
27 Session s = session.get();
28
29 if(s == null) {
30 s = sessionFactory.openSession();
31 session.set(s);
32 }
33
34 return s;
35 }
36
37 public static void closeSession() throws HibernateException {
38 Session s = session.get();
39 if(s != null) {
40 s.close();
41 }
42 session.set(null);
43 }
44 }
01 package test.hibernate;
02
03 import java.util.LinkedList;
04 import java.util.List;
05
06 /**
07 *
08 */
09
10 @SuppressWarnings("serial")
11 public class Person implements java.io.Serializable {
12
13 // Fields
14
15 private Integer id;
16
17 private String name;
18
19 private String sex;
20
21 private Integer age;
22
23 private List list = new LinkedList();
24
25 // Collection accessors
26
27 public List getList() {
28 return list;
29 }
30
31 public void setList(List list) {
32 this.list = list;
33 }
34
35 /** default constructor */
36 public Person() {
37 }
38
39 /** constructor with id */
40 public Person(Integer id) {
41 this.id = id;
42 }
43
44 // Property accessors
45
46 public Integer getId() {
47 return this.id;
48 }
49
50 public void setId(Integer id) {
51 this.id = id;
52 }
53
54 public String getName() {
55 return this.name;
56 }
57
58 public void setName(String name) {
59 this.name = name;
60 }
61
62 public String getSex() {
63 return this.sex;
64 }
65
66 public void setSex(String sex) {
67 this.sex = sex;
68 }
69
70 public Integer getAge() {
71 return this.age;
72 }
73
74 public void setAge(Integer age) {
75 this.age = age;
76 }
77
78 }
01 /*
02 * Created on
03 * @author
04 */
05 package test.hibernate;
06
07 import java.sql.SQLException;
08
09 import org.hibernate.FlushMode;
10 import org.hibernate.HibernateException;
11 import org.hibernate.Session;
12 import org.hibernate.Transaction;
13
14 public class Test {
15
16 public static void main(String [] args) {
17 Session s = HibernateUtil.currentSession();
18
19 Transaction tx = s.beginTransaction();
20
21 // Person p = (Person) s.load(Person.class, 1);
22 // System.out.println(p.getName());
23 Person p = new Person();
24
25 p.setAge(19);
26 p.setName("icerain");
27 p.setSex("male");
28 s.save(p);
29 s.flush();
30 /*
31 Person p2 = (Person) s.get(Person.class, new Integer(1));
32 System.out.println(p2.getName());
33 p2.setName("ice..");
34 s.saveOrUpdate(p2);
35 s.flush();
36 Person p3 = (Person) s.get(Person.class, new Integer(2));
37 System.out.println(p3.getName());
38 s.delete(p3);
39 */
40
41 tx.commit();
42 try {
43 System.out.println(p.getName());
44 } catch (Exception e) {
45 // TODO Auto-generated catch block
46 e.printStackTrace();
47 }
48
49 HibernateUtil.closeSession();
50 }
51 }
hibernate.cfg.xml 配置文件如下,利用mysql 数据库.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">org.gjt.mm.mysql.Driver</property>
<property name="hibernate.connection.password">你的数据库密码</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost/数据库名</property>
<property name="hibernate.connection.username">用户名</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="show_sql">true</property>
<property name="hibernate.transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</property>
<property name="hibernate.transaction.auto_close_session">false</property>
<property name="hibernate.hbm2ddl.auto">update</property>
<mapping resource="test/hibernate/annotation/Person.hbm.xml"/>
</session-factory>
</hibernate-configuration>
其中 配置了<property name="hibernate.hbm2ddl.auto">update</property>属性 自动导入数据库ddl.生产的ddl sql语句如下
create table person (id integer not null auto_increment, name varchar(255), sex varchar(255), age integer, person integer, primary key (id))
alter table person add index FKC4E39B5511C4A5C2 (person), add constraint FKC4E39B5511C4A5C2 foreign key (person) references person (id)
而Person.hbm.xml 文件如下:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="test.hibernate.Person" table="person">
<id name="id" type="integer">
<column name="id" />
<generator class="native"></generator>
</id>
<property name="name" type="string">
<column name="name" />
</property>
<property name="sex" type="string">
<column name="sex" />
</property>
<property name="age" type="integer">
<column name="age" />
</property>
<bag name="list" cascade="all">
<key column="person"></key>
<one-to-many class="test.hibernate.Person"/>
</bag>
</class>
</hibernate-mapping>
下面让我们看看利用 Hibernate Annotations 如何做,只要三个类 不再需要 hbm.xml配置文件:
还要把用到的两个jar文件 放入的类路径中. 具体如何做,请参考 Hibernate Annotations 中文文档
http://hb.6644.net
HibernateUtil
.java 也就是 Hibernate文档中推荐的工具类,Person
.java 一个持久化的类, Test.java 测试用的类.都在test.hibernate.annotation
包中. 每个类的代码如下:
01 package test.hibernate.annotation;
02
03 import org.hibernate.HibernateException;
04 import org.hibernate.Session;
05 import org.hibernate.SessionFactory;
06 import org.hibernate.cfg.AnnotationConfiguration;
07 import org.hibernate.cfg.Configuration;
08
09 public class HibernateUtil {
10 public static final SessionFactory sessionFactory;
11
12 static {
13 try {
14 sessionFactory = new AnnotationConfiguration() //注意: 建立 SessionFactory于前面的不同
15 .addPackage("test.hibernate.annotation")
16 .addAnnotatedClass(Person.class)
17
18 .configure()
19 .buildSessionFactory();
20 //new Configuration().configure().buildSessionFactory();
21 } catch (HibernateException e) {
22 // TODO Auto-generated catch block
23
24 e.printStackTrace();
25 throw new ExceptionInInitializerError(e);
26 }
27 }
28
29 public static final ThreadLocal<Session> session = new ThreadLocal<Session>();
30
31 public static Session currentSession() throws HibernateException {
32 Session s = session.get();
33
34 if(s == null) {
35 s = sessionFactory.openSession();
36 session.set(s);
37 }
38
39 return s;
40 }
41
42 public static void closeSession() throws HibernateException {
43 Session s = session.get();
44 if(s != null) {
45 s.close();
46 }
47 session.set(null);
48 }
49 }
01 package test.hibernate.annotation;
02
03 import java.util.LinkedList;
04 import java.util.List;
05
06 import javax.persistence.AccessType;
07 import javax.persistence.Basic;
08 import javax.persistence.Entity;
09 import javax.persistence.GeneratorType;
10 import javax.persistence.Id;
11 import javax.persistence.OneToMany;
12 import javax.persistence.Table;
13 import javax.persistence.Transient;
14
15 /**
16 *
17 */
18
19 @SuppressWarnings("serial")
20 @Entity(access = AccessType.PROPERTY) //定义该类为实体类
21 @Table //映射表
22 public class Person implements java.io.Serializable {
23
24 // Fields
25
26 private Integer id;
27
28 private String name;
29
30 private String sex;
31
32 private Integer age;
33
34 private List list = new LinkedList();
35
36 // Constructors
37 /** default constructor */
38 public Person() {
39 }
40
41 /** constructor with id */
42 public Person(Integer id) {
43 this.id = id;
44 }
45
46 // Property accessors
47 @Id
48 public Integer getId() {
49 return this.id;
50 }
51
52 public void setId(Integer id) {
53 this.id = id;
54 }
55
56 @Basic
57 public String getName() {
58 return this.name;
59 }
60
61 public void setName(String name) {
62 this.name = name;
63 }
64
65 @Basic
66 public String getSex() {
67 return this.sex;
68 }
69
70 public void setSex(String sex) {
71 this.sex = sex;
72 }
73
74 @Basic
75 public Integer getAge() {
76 return this.age;
77 }
78
79 public void setAge(Integer age) {
80 this.age = age;
81 }
82 @Transient //由于本例不打算演示集合映射 所有声明该属性为 Transient
83 public List getList() {
84 return list;
85 }
86
87 public void setList(List list) {
88 this.list = list;
89 }
90
91 }
注意该实体类中的属性都使用了默认值.
Test.java 代码同上
不需要了 hbm.xml 映射文件, 是不是简单了一些 .给人认为简化了一些不是主要目的.主要是可以了解一下 EJB3 的持久化机制 ,提高一下开发效率才是重要的.
好了 .本例就完了 . 感觉怎么样了 .欢迎你来批批.
PS:
生成的数据库表 和 程序执行后的 数据库情况如下
mysql> describe person;
+--------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | YES | | NULL | |
| sex | varchar(255) | YES | | NULL | |
| age | int(11) | YES | | NULL | |
| person | int(11) | YES | MUL | NULL | |
+--------+--------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)
mysql> select * from person;
+----+---------+------+------+--------+
| id | name | sex | age | person |
+----+---------+------+------+--------+
| 1 | icerain | male | 19 | NULL |
+----+---------+------+------+--------+
1 row in set (0.03 sec)
讨论一下 hbm.xml 与 Annotations的优缺点,看看那种情况最适合你.
首先,讨论一下 xml 配置文件的优点, 个人认为主要优点就是当你改变底层配置时 不需要改变和重新编译代码,只需要在xml 中更改就可以了,例如 Hibernate.cfg.xml 当你要更改底层数据库时, 只要更改配置文件就可以了.Hibernate会为你做好别的事情.
那么xml的缺点呢,个人认为有以下几点:
-
描述符多,不容易记忆,掌握 要深入了解还有看DTD文件
-
无法做自动校验,需要人工查找
-
读取和解析xml配置要消耗一定时间,导致应用启动慢,不便于测试和维护
-
当系统很大时,大量的xml文件难以管理
-
运行中保存xml配置需要消耗额外的内存
-
在O/R Mapping的时候需要在java文件和xml配置文件之间交替,增大了工作量
其中第一 二点 借助于先进的IDE 可能不是什么问题. 但是对初学者还是个问题 ^_^.
下面我们看看 Annotations的 特性吧! 可以解决xml遇到的问题,有以下优点
-
描述符减少。以前在xml配置中往往需要描述java属性的类型,关系等等。而元数据本身就是java语言,从而省略了大量的描述符
-
编译期校验。错误的批注在编译期间就会报错。
-
元数据批注在java代码中,避免了额外的文件维护工作
-
元数据被编译成java bytecode,消耗的内存少,读取也很快,利于测试和维护
关于 映射文件是使用 hbm.xml 文件还是使用 Annotations 我们来看看2者的性能吧. 先声明一下,个人认为映射文件一旦配置好就不会在很大程度上改变了.所以使用xml文件并不会带来很大的好处.如果你认为 映射文件在你的项目中也经常变化,比如一列String数据 ,今天你使用 length="16" 明天你认为 该数据的长度应该更长才能满足业务需求 于是改为length="128" 等等类似的问题 . 如果你经常有这方面的变动的话,下面的比较你可以不用看了 , 你应该使用 xml文件 因为Annotations 无法很好的满足你的要求.
现在让我们就来看看2者的性能比较吧.
(说明: 这里只是比较查找 插入 的时间快慢,没有比较除运行时间以外的其他性能,如 内存占用量 等等)
先来看看测试程序和配置.
首先在 Hibernate.cfg.xml 文件中去掉了
<property name="hibernate.hbm2ddl.auto">update</property>
这一行, 因为在前面的实验中以及建立了数据库表了 不再需要更新了.如果你是第一次运行该例子 还是要该行的.
Test.java 如下:
/*
* Created on 2005
* @author
*/
package test.hibernate.annotation;
import org.hibernate.Session;
import org.hibernate.Transaction;
public class Test {
public static void main(String [] args) {
long start = 0;
long end = 0;
start = System.currentTimeMillis(); //程序开始时间
Session s = HibernateUtil.currentSession();
long mid = System.currentTimeMillis(); //初始化完毕的时间 (可能此时并没有初始化完毕^_^)
Transaction tx = s.beginTransaction();
/********************测试读取的代码************************/
Person p = null;
for(int i = 1; i <= 100; i ++) {
p = (Person) s.get(Person.class, i);
System.out.println(p.getName());
}
System.out.println(p.getName());
/********************测试读取1次的代码************************/
Person p = null;
p = (Person) s.get(Person.class, 1);
System.out.println(p.getName());
/*********************测试插入的代码*************************************/
/*
for (int i = 0; i < 100; i ++) {
Person p = new Person();
p.setAge(i+1);
p.setName("icerain"+i);
p.setSex("male"+i);
s.save(p);
s.flush();
}
*/
tx.commit();
HibernateUtil.closeSession();
end = System.currentTimeMillis(); //测试结束时间
System.out.println("String[] - start time: " + start);
System.out.println("String[] - end time: " + end);
System.out.println("Init time : " + (mid-start)); // 打印初始化用的时间
System.out.println("Last time is :" +(end - mid) ); //打印 数据操作的时间
System.out.println("Total time : " +(end - start)); //打印总时间
}
}
Annotations 包中的Person.java 如下
package test.hibernate.annotation;
import java.util.LinkedList;
import java.util.List;
import javax.persistence.AccessType;
import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.GeneratorType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;
/**
* Person generated by hbm2java
*/
@SuppressWarnings("serial")
@Entity(access = AccessType.PROPERTY)
@Table
public class Person implements java.io.Serializable {
private Integer id;
private String name;
private String sex;
private Integer age;
private List list = new LinkedList();
// Constructors
/** default constructor */
public Person() {
}
/** constructor with id */
public Person(Integer id) {
this.id = id;
}
// Property accessors
@Id(generate=GeneratorType.AUTO)
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
@Basic
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
@Basic
public String getSex() {
return this.sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Basic
public Integer getAge() {
return this.age;
}
public void setAge(Integer age) {
this.age = age;
}
@Transient
public List getList() {
return list;
}
public void setList(List list) {
this.list = list;
}
}
其他的代码几乎没有改变:
下面的每种类型的测试都测试了3次以上, 取中间的测试时间.
测试机器配置:
CPU: AMD Athlon (xp) 2000+
内存: 784880KB
硬盘: 三星 SP0812N
读取一次 的比较:(单位: 毫秒)
使用Annotations 的测试数据 | 使用Xml文件的测试数据 | 简要说明 | ||
Init time : | 2444 | Init time : | 2431 | 测试前我认为该项结果xml应该比较大,要读取映射文件啊,实际情况不是这样,不知道为什么? |
Last time is : | 62 | Last time is : | 85 | 相差比较大不知道为什么? |
Total time : | 2506 | Total time : | 2516 | xml文件总体上慢了一点 |
读取100次的比较:
使用Annotations 的测试数据 | 使用Xml文件的测试数据 | 简要说明 | ||
Init time : | 2437 | Init time : | 2422 | 和前面初始化差不多 |
Last time is : | 438 | Last time is : | 484 | 有时间差 |
Total time : | 2875 | Total time : | 2906 | 也是xml文件总体上慢了一点 |
插入100次的比较:
使用Annotations 的测试数据 | 使用Xml文件的测试数据 | 简要说明 | ||
Init time : | 2453 | Init time : | 2469 | 和前面初始化差不多 |
Last time is : | 469 | Last time is : | 656 | 有时间差 |
Total time : | 2922 | Total time : | 3125 | 也是xml文件总体上慢了一点 |
从上面的三次对比中大家可以看到 初始化的部分几乎两者是一样的, 在数据操作上面 使用xml文件 总是比使用Annotations 慢一点.在我们只操纵一个只有几个属性的小持久化类的操作中就有 几十毫秒的差距. 几十毫秒在计算机中算不算很大 大家应该都知道,我就不在多说了.
总结: 经过 xml 文件 和Annotations 的优缺点和 性能上的对比.现在使用那个作为你持久化映射策略.我相信大家都会正确选择的.
测试后记: 经过多次测试 感觉有时候很不稳定 ,有的时候很稳定不知道是测试有问题还是别的问题.大家可以自己测试一下. 有什么新的发现 请大家讨论讨论.