Hibernate Annotations 实战

 -- 从 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 : 2444Init time : 2431测试前我认为该项结果xml应该比较大,要读取映射文件啊,实际情况不是这样,不知道为什么?
Last time is :62Last time is :85相差比较大不知道为什么?
Total time : 2506Total time : 2516xml文件总体上慢了一点

   读取100次的比较:

使用Annotations 的测试数据使用Xml文件的测试数据简要说明
Init time : 2437Init time : 2422和前面初始化差不多
Last time is :438Last time is :484有时间差
Total time : 2875Total time : 2906也是xml文件总体上慢了一点

插入100次的比较:

使用Annotations 的测试数据使用Xml文件的测试数据简要说明
Init time : 2453Init time : 2469和前面初始化差不多
Last time is :469Last time is :656有时间差
Total time : 2922Total time : 3125也是xml文件总体上慢了一点

从上面的三次对比中大家可以看到 初始化的部分几乎两者是一样的, 在数据操作上面 使用xml文件 总是比使用Annotations 慢一点.在我们只操纵一个只有几个属性的小持久化类的操作中就有 几十毫秒的差距. 几十毫秒在计算机中算不算很大 大家应该都知道,我就不在多说了.

总结: 经过 xml 文件 和Annotations 的优缺点和 性能上的对比.现在使用那个作为你持久化映射策略.我相信大家都会正确选择的.

测试后记: 经过多次测试 感觉有时候很不稳定 ,有的时候很稳定不知道是测试有问题还是别的问题.大家可以自己测试一下. 有什么新的发现 请大家讨论讨论.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值