Hibernate3官方教程-第1章-第1部分

第一章,Hibernate 介绍

※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※

英文原文版权归原作者所有, 本译文转载请注明出处!

译者:abigfrog 联系:QQ:800736, MSN:J2EE@HOTMAIL.COM

※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※

 

1.1 前言
本章是Hibernate新用户的入门教程。 我们从一个简单的命令行程序开始,使用一个内存数据库,并且用易于理解的步骤去编写代码。本教成虽然是为Hibernate初学者准备的,但读者还是需要基本的Java和SQL知识。本教程基于Michael Gloegl的教程。第三方类库需要JDK1.4或5.0,其他或许也需要JDK1.3。
本教程的源代码已包含在发行包内,它在 doc/reference/tutorial/ 目录。
1.2 第1部分 - 第一个Hibernate应用
首先,我们创建一个简单的控制台Hibernate应用。在这里我们使用的是一个Java数据库(HSQL DB),因此不需要额外准备数据库。
假如我们需要一个小型的数据库应用,可以存储我们要参与的事件/会议,以及这些事件的主机信息。
首先,我们需要创建开发目录,并将需要的Java库放进去。从Hibernate网站下载安装包,解压它并将lib目录内所有的库文件拷贝到刚刚我们新建的开发目录内的lib目录。看起来应该类似这样:
.
+ lib
  antlr
. jar
  cglib
. jar
  asm
. jar
  asm-attrs
. jars
  commons-collections
. jar
  commons-logging
. jar
  hibernate3
. jar
  jta
. jar
  dom4j
. jar
  log4j
. jar
这是我们需要的最小的库集合(注意我们也拷贝了hibernate3.jar到lib目录,它是hibernate核心库)。Hibernate包含你可能用到或者用不到的类库,具体可查看lib目录的 README.txt 文件,以知道哪些是必需的,哪些是可选的(事实上,Log4j不是Hibernate必需的,但很多开发者需要)。
接下来,我们创建一个Class,表示我们想要存储在数据库中的事件。
1.2.1.第一个Class
我们第一个持久类是一个具有一些域字段的简单JavaBean:
package  events;
 
import  java.util.Date;
 
public   class  Event {
    
private  Long id;
 
    
private  String title;
    
private  Date date;
 
    
public  Event() {}
 
    
public  Long getId() {
        
return  id;
    }
 
    
private   void  setId(Long id) {
        
this .id  =  id;
    }
 
    
public  Date getDate() {
        
return  date;
    }
 
    
public   void  setDate(Date date) {
        
this .date  =  date;
    }
 
    
public  String getTitle() {
        
return  title;
    }
 
    
public   void  setTitle(String title) {
        
this .title  =  title;
    }
}
可以看到,这个类的getter和setter方法使用的是标准的Javabean命名规范,包括private修饰的域也一样。这种方式是推荐的设计方式,但没必要这样做。Hibernate可以直接访问类的字段域。存取方法的意义在于,提供了重构时的健壮性。无参数的构造函数用来通过反射实例化这个类。
id属性值表示一个特定事件的唯一标识符。如果我们打算使用Hibernate的全部功能,所有的持久实体类都需要这样一个字段(几乎没有重要的依赖的类也一样)。事实上,几乎所有的应用(尤其是Web应用)需要通过该标识识别对象。所以,你应该认为这是一个特性,而非一个限制。然而,我们往往不去处理这个标识字段,所以这个字段的setter方法应该设为私有。当一个对象被保存的时候,只有Hibernate会为它分配一个标识。Hibernate可以访问public、private以及protected的方法,也包括(public、private、protected)修饰的字段。机会留给了你,你可以将它使用在合适的地方。
所以的持久化类都需要一个无参数的构造函数;Hibernate会使用反射机制创建对象。构造函数可以是私有的,然而,对于运行时的代理生成机制和在没有字节码处理单元(instrumentation)的情况下,包的可见度还是必需的。
将下面的Java源程序放在开发目录内名为src的目录中,并且在正确的包目录,放好以后应该类似这样:
.
+ lib
  
< Hibernate and third-party libraries >
+ src
  
+ events
    Event
. java
 
下面,我们会让Hibernate识别这些持久性类。
1.2.2.映射文件
Hibernate需要知道如何加载和存储持久化类,这就是为什么会需要映射文件的原因。映射文件告诉Hibernate要访问数据库的哪张表,以及使用表里的哪个字段。一个映射文件的基本结构如下:
<? 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 >
[...]
</ hibernate-mapping >
注意,Hibernate DTD定义非常精密(sophisticated) ,你可以通过它在编辑器或者IDE中实现XML映射元素或属性的自动完成功能。你最好能打开这个DTD文件看一下,它能帮助你从全局上概览有哪些元素、属性以及它们的默认值是什么,当然还有一些注释。需要指出的是,Hibernate不会从web上加载该文件,但它会首先在应用程序的classpath路径中进行查找。hibernate3.jar包含这个dtd文件,Hibernate的发行包的src目录也有。
在今后的例子中,为了节省代码起见,我们会省略掉DTD定义,但它并不是可有可无的。
在两个hibernate-mapping标签之间,包含一个class标签,所有的持久化实体类(有可能存在依赖那些不是第一层的实体)都需要这样一个映射,以和数据库表联系起来。
< hibernate-mapping >

    
< class  name ="events.Event"  table ="EVENTS" >

    
</ class >

</ hibernate-mapping >
到目前为止,我们只告诉了Hibernate如何从EVENTS表去持久化和加载Event对象类,每个Event对象的实例表示数据库表里的一行数据。现在我们继续使用该映射文件,这次增加了标识字段和数据表的关键字段的对应设置。另外,既然我们没必要关心如何去处理该标识,那么我们就为关键字段定义一个Hibernate标识生成策略。
< hibernate-mapping >

    
< class  name ="events.Event"  table ="EVENTS" >
        
< id  name ="id"  column ="EVENT_ID" >
            
< generator  class ="native" />
        
</ id >
    
</ class >

</ hibernate-mapping >
标识属性定义中的id,name=”id”中的id表示Java 属性定义的名字,Hibernate会使用getter和setter方法对该字段进行存取。column属性告诉Hibernate数据表中的哪一列为关键字段。嵌套的generator节点指定了标识的生成策略,在这儿我们使用native,它会根据当前配置的数据库类型(dialect)选择最好的生成策略。Hibernate支持数据库生成、全局唯一、程序分配(或者其他你写的扩展实现策略)。
最后,我们将持久化类的定义也包含进映射文件里来。默认情况下,任何性质的类都被认为是持久化类。
< hibernate-mapping >

    
< class  name ="events.Event"  table ="EVENTS" >
        
< id  name ="id"  column ="EVENT_ID" >
            
< generator  class ="native" />
        
</ id >
        
< property  name ="date"  type ="timestamp"  column ="EVENT_DATE" />
        
< property  name ="title" />
    
</ class >

</ hibernate-mapping >
和id一样,property的属性name指示Hibernate使用哪个getter和setter方法,那么,这时,Hibernate会尝试寻找getDate()/setDate(),getTitle()/setTitle()也是一样。
为什么名为date的property节点有column属性,而title的没有?如果没有column属性Hibernate会默认将property名作为column名称,因此title节点工作不会有什么问题,然而,date在大多数数据库里是保留关键字,因此我们最好给它赋予另外一个名称。
下一个有趣的事实是,名为title的节点还缺少一个type属性,在映射文件中我们声明和使用的Types,并不是你所想像的Java数据类型。它们也不是SQL数据库类型。事实上,它们被称作Hibernate 映射类型(Hibernate mapping types),转换器可以将它们从Java类型转换到SQL数据类型,反之亦然。再者,如果type属性不存在的话,Hibernate会尝试去检测正确的转换器和映射类型。在某些情况下,这种自动检测(使用Java反射技术)可能不会满足你预期需要。这就是原因所在。Hibernate无法判断是应该映射到一个SQL日期型,timestamp型,还是一个time型的数据库列。通过将timestamp转换器映射到property,我们保留了完整的日期和时间信息。
这份映射文件应该保存为Event.hbm.xml,放于和Event Java源文件相同的目录。映射文件的可以随意命名,但hbm.xml 后缀名是Hibernate开发者社区一个约定俗成的规则。目录结构现在应该类似这个样子:
.
+ lib
  
< Hibernate and third-party libraries >
+ src
  
+ events
    Event
. java
    Event
. hbm . xml 
下面我们继续讨论Hibernate的主要配置。
1.2.3.Hibernate 配置
我们现在有了一个持久化类和它的映射文件。是时候配置Hibernate了。在这之前,我们需要一个数据库。HSQL DB,是一个Java编写的数据库系统,可以从它的网站下载(最新版下载:http://nchc.dl.sourceforge.net/sourceforge/hsqldb/hsqldb_1_8_0_9.zip)。事实上,你只需要下载 hsqldb.jar 。将它放入开发目录的lib目录。
在开发目录的根目录创建一个叫做data的目录,HSQL DB的数据文件储存在这里。现在,在data目录通过命令“java -classpath ../lib/hsqldb.jar org.hsqldb.Server”启动数据库服务,你可以看到它启动并绑定到TCP/IP端口,我们的应用程序稍后会进行连接。如果你想在本教程中从一个新的数据库开始,那么关闭HSQL DB(CVtrl+C),删除data目录的全部文件,然后重新启动。
 
Hibernate是你的应用程序中连接数据库的层,因此它需要连接信息。连接是通过一个连接池提供的,连接池也必需进行配置。Hibernate发行包自带了几个开源的JDBC连接池工具,但现在我们使用Hibernate内置的连接池。注意你需要拷贝必需的库文件到classpath,如果你想使用一个产品级质量的第三方连接池产品的话请进行个别设值。
 
配置Hibernate,可以使用一个简单的hibernate.properties文件,稍微复杂些的hibernate.cfg.xml文件,或者甚至是完全的编程设值。大多数人使用XML配置文件。
 
<? 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 >
 
        
<!--  Database connection settings  -->
        
< property  name ="connection.driver_class" > org.hsqldb.jdbcDriver </ property >
        
< property  name ="connection.url" > jdbc:hsqldb:hsql://localhost </ property >
        
< property  name ="connection.username" > sa </ property >
        
< property  name ="connection.password" ></ property >
 
        
<!--  JDBC connection pool (use the built-in)  -->
        
< property  name ="connection.pool_size" > 1 </ property >
 
        
<!--  SQL dialect  -->
        
< property  name ="dialect" > org.hibernate.dialect.HSQLDialect </ property >
 
        
<!--  Enable Hibernate's automatic session context management  -->
        
< property  name ="current_session_context_class" > thread </ property >
 
        
<!--  Disable the second-level cache  -->
        
< property  name ="cache.provider_class" > org.hibernate.cache.NoCacheProvider </ property >
 
        
<!--  Echo all executed SQL to stdout  -->
        
< property  name ="show_sql" > true </ property >
 
        
<!--  Drop and re-create the database schema on startup  -->
        
< property  name ="hbm2ddl.auto" > create </ property >
 
        
< mapping  resource ="events/Event.hbm.xml" />
 
    
</ session-factory >
 
</ hibernate-configuration >
 
注意这个XML配置文件使用了不同的DTD。我们配置了Hibernate的SessionFactory,它是针对一个特定数据库的全局性的工厂。如果你有多个数据库,那么使用多个<session-factory>配置,这种情况一般会放在几个不同的配置文件中(为了启动更容易)。
 
前面四个property元素包含了配置一个JDBC连接所必需的信息。名为dialect的property元素指定了Hibernate生成的特定SQL变量。Hibernate对于持久化上下文的自动会话(session)管理轻松自如,稍后你将看到。hbm2ddl.auto选项打开了数据库的自动创建功能——直接作用于数据库。这项当然可以予以关闭(通过移出该选项)或者在一个Ant任务SchemaExport的帮助下重定向输出到文件。最后,我们将持久化类的映射加入。
 
将配置文件拷贝到源程序目录,它将会在classpath的路径内,Hibernate在启动时会自动在classpath中寻找名为hibernate.cfg.xml的配置文件。
 
现在我们要使用Ant来构建,你需要已经安装好Ant,如果没有可以到此下载: http://ant.apache.org/bindownload.cgi ,如何安装Ant超出了本教程的范围,请自行查找资料。也可查阅Ant自带的说明文件。当你装好Ant以后,我们就可以开始创建Ant脚本文件了,它名叫build.xml,将它保存在开发目录即可。
 
一个基本的构建脚本文件看起来像这样:
< project  name ="hibernate-tutorial"  default ="compile" >
 
    
< property  name ="sourcedir"  value ="${basedir}/src" />
    
< property  name ="targetdir"  value ="${basedir}/bin" />
    
< property  name ="librarydir"  value ="${basedir}/lib" />
 
    
< path  id ="libraries" >
        
< fileset  dir ="${librarydir}" >
            
< include  name ="*.jar" />
        
</ fileset >
    
</ path >
 
    
< target  name ="clean" >
        
< delete  dir ="${targetdir}" />
        
< mkdir  dir ="${targetdir}" />
    
</ target >
 
    
< target  name ="compile"  depends ="clean, copy-resources" >
      
< javac  srcdir ="${sourcedir}"
             destdir
="${targetdir}"
             classpathref
="libraries" />
    
</ target >
 
    
< target  name ="copy-resources" >
        
< copy  todir ="${targetdir}" >
            
< fileset  dir ="${sourcedir}" >
                
< exclude  name ="**/*.java" />
            
</ fileset >
        
</ copy >
    
</ target >
 
</ project >
 
这会指示Ant将lib目录内所有以.jar结尾的文件全部放入classpath以备编译使用。它也会拷贝所有非java源程序文件到目标文件夹,例如配置和映射文件。如果你现在运行了Ant脚本,你应该看到如下的输出:
C: hibernateTutorial > ant
Buildfile: build
. xml
 
copy -resources:
     [
copy ] Copying  2   files  to C: hibernateTutorial bin
 
compile:
    [javac] Compiling 
1  source file to C: hibernateTutorial bin
 
BUILD SUCCESSFUL
Total 
time 1  second
 
1.2.5.启动和辅助类
 
现在是时候把一些Event对象保存进数据库了,但首先我们必需完成配置的一些基础代码。我们必须启动Hibernate,这个启动过程包括建立一个新全局的SessionFactory对象,并且将它放于一个程序代码易于访问的地方。一个SessionFactory可以创建一个新的会话,一个会话代表一个单线程的工作单元,SessionFactory是线程安全的全局对象,它只实例化一次。
 
我们将创建一个HibernateUtil 辅助类,它将负责Hibernate的启动以及使SessionFactory易于访问。让我们看一下它的实现代码:
package  util;
 
import  org.hibernate. * ;
import  org.hibernate.cfg. * ;
 
public   class  HibernateUtil {
 
    
private   static   final  SessionFactory sessionFactory;
 
    
static  {
        
try  {
            
//  Create the SessionFactory from hibernate.cfg.xml
            sessionFactory  =   new  Configuration().configure().buildSessionFactory();
        } 
catch  (Throwable ex) {
            
//  Make sure you log the exception, as it might be swallowed
            System.err.println( " Initial SessionFactory creation failed. "   +  ex);
            
throw   new  ExceptionInInitializerError(ex);
        }
    }
 
    
public   static  SessionFactory getSessionFactory() {
        
return  sessionFactory;
    }
 
}
 
这个类不仅仅在它初始化时(当类加载时由JVM只调用一次)创建一个全局的SessionFactory对象,但同时也隐藏了一个事实,那就是它使用了静态的单例(singleton)。在应用服务器也可能通过JNDI查找SessionFactory。
 
如果你在配置文件中给SessionFactory设值了name值,Hibernate就会在它创建以后将该值绑定到JNDI。如果你不想这样,那么你可以使用JMX部署,或者让兼容JMX的容器实例化HibernateService并将之绑定到JNDI。这些高级特性会在Hibernate的参考文档中进行讨论。
 
将HibernateUtil.java放在开发目录中和evnets相邻的另一个目录中;
.
+ lib
 
< Hibernate and third-party libraries >
+ src
 
+ events
    Event
. java
    Event
. hbm . xml
 
+ util
    HibernateUtil
. java
 hibernate
. cfg . xml
+ data
build
. xml
再次编译,应该没有什么错误。最后我们需要配置一个日志系统,Hibernate使用common logging,具体使用Log4j还是JDK1.4 logging由你选择。大多数开发者会选择Log4j,从Hibernate安装包内将logj.properties(在etc/目录内)复制到src目录,和hibernate.cfg.xml放在一起。有兴趣可以看一下log4j的配置,你也可以进行修改增加更多的输出内容。默认情况下,只有Hibernate启动信息会显示在控制台中。
 
本教程的基础部分已经结束,接下来我们将使用Hibernate做些更实际的工作。
 
1.2.6.加载和存储对象
最后,我们使用Hibernate来查询和保存对象。我们创建一个拥有main()方法的EventManager类。
package  events;
import  org.hibernate.Session;
 
import  java.util.Date;
 
import  util.HibernateUtil;
 
public   class  EventManager {
 
    
public   static   void  main(String[] args) {
        EventManager mgr 
=   new  EventManager();
 
        
if  (args[ 0 ].equals( " store " )) {
            mgr.createAndStoreEvent(
" My Event " new  Date());
        }
 
        HibernateUtil.getSessionFactory().close();
    }
 
    
private   void  createAndStoreEvent(String title, Date theDate) {
 
        Session session 
=  HibernateUtil.getSessionFactory().getCurrentSession();
 
        session.beginTransaction();
 
        Event theEvent 
=   new  Event();
        theEvent.setTitle(title);
        theEvent.setDate(theDate);
 
        session.save(theEvent);
 
        session.getTransaction().commit();
    }
 
}
 
我们创建了一个Event对象,并将他传给Hibernate,Hibernate会自动生成SQL语句并执行插入操作,以使数据保存进数据库,在我们运行它之前,让我们看看Session和Transaction的处理代码。
 
一个会话(Session)是一个单一的处理。现在我们继续化繁为简,假设在一个Hibernate会话和一个数据库事务之间是一对一的关系,为了将我们的代码置于一个实际的事务之中(这里使用的是JDBC,也可以使用JTA),我们使用了Hibenate会话提供的事务的API。
 
sessionFactory.getCurrentSession()是什么意思呢?首先,我们可以在任何时间任何地方调用它任意次,一旦你取得SessionFactory对象(这么容易拿到得感谢HibenateUtilJ),getCurrentSession()方法总是返回“当前的”工作,是否还记得在hibernate.cfg.xml中将配置项设为“线程”(thread)?因此,当前的工作是绑定在执行程序的Java线程上的。然而,并不全是这样的,你还必须考虑范围(scope),工作何时开始和结束。
 
一个会话开始于它被首次请求,当首次调用getCurrentSession()以后,会话就被Hibernate绑定到了当前线程,当事务一旦结束,要么提交要么回滚,Hibernate自动取消会话和线程的绑定并将之关闭,如果你再次调用getCurrentSession(),你将得到一个新的会话并且可以开始一个新的工作,这种线程绑定编程模型,是目前使用Hibernate最流行的方式,因为它允许代码进行灵活的分层(事务处理代码可以从数据访问代码中分离出来,我们会在本教程的稍后讨论)。
 
在一个工作的范围内,Hibernate会话应被用来执行一次或数次数据库操作么?上面的例子使用一个会话处理一个操作,这是纯属巧合。这个例子并不足以复杂以演示其他的情况。Hibernate会话的范围是很灵活的,绝对不要为每个数据库操作都使用一个新的会话。所有,即使在今后的例子中再看到几次,也要认为每个操作一个会话是反模式的,不可取的。在本教程的后面部分会使用一个真实的web应用进行示例。
 
可参看第11章:事务和并发,了解更多的事务处理和限定方面的内容,在上面的例子中我们也忽略了任何的错误处理和事务回滚操作。
 
要让我们第一个例子运行,我们需要在Ant脚本中加入一个回调的Target。
 
< target  name ="run"  depends ="compile" >
    
< java  fork ="true"  classname ="events.EventManager"  classpathref ="libraries" >
        
< classpath  path ="${targetdir}" />
        
< arg  value ="${action}" />
    
</ java >
</ target >
 
参数action的值是在执行Ant脚本的时候在命令行进行指定:
C:/hibernateTutorial/>ant run -Daction=store
你应该可以看到,在编译以后,Hibernate启动,并且根据你的配置不同,会有很多日志输出。在最后你会发现这样一行:
[ java ]  Hibernate:  insert   into  EVENTS (EVENT_DATE, title, EVENT_ID)  values  (?, ?, ?)
 
这就是Hibernate执行的Insert语句,问号代表JDBC绑定参数,要看到绑定参数的值,或者减少日志的输出内容,检查log4j.properties配置。
 
现在,我们要将数据库的内容显示出来,那么,我们为main方法添加一个选项。
 
if  (args[ 0 ].equals( " store " )) {
    mgr.createAndStoreEvent(
" My Event " new  Date());
}
else   if  (args[ 0 ].equals( " list " )) {
    List events 
=  mgr.listEvents();
    
for  ( int  i  =   0 ; i  <  events.size(); i ++ ) {
        Event theEvent 
=  (Event) events.get(i);
        System.out.println(
" Event:  "   +  theEvent.getTitle()  +
                           
"  Time:  "   +  theEvent.getDate());
    }
}
 
我们也增加一个listEvents()方法:
private  List listEvents() {
 
    Session session 
=  HibernateUtil.getSessionFactory().getCurrentSession();
 
    session.beginTransaction();
 
    List result 
=  session.createQuery( " from Event " ).list();
 
    session.getTransaction().commit();
 
    
return  result;
}
 
我们这里使用的是HQL(Hibernate Query Language)查询去从数据库加载Event对象,Hibernate会生成相应的SQL,然后从数据库查询出数据,当然,可以使用HQL创建更复杂的查询。
 
现在,我们到了运行并测试的时候了,请按照下面的步骤:
运行ant run -Daction=store向数据库插入一些数据,当然之前通过hbm2dll创建了数据库表。
现在注释掉hibernate.cfg.xml中的配置项以禁止hbm2dll,通常你仅仅在连续的单元测试中才需要将它打开,因为当你再次执行hbm2dll时会消除你存储的所有信息,create会被转换成“当SessionFactory创建的时候,丢弃全部数据库表,然后重建它们”。
如果你现在以参数-Daction=list执行Ant,你可以看到截至目前你存储进数据库的内容,当然你现在还可以调用store操作去增加更多的数据。
 
注意:很多Hibernate初学者在此栽了跟头,并且我们看到大量关于“找不到数据库表”的错误信息,然而如果你按照上面所述的步骤,将不会遇到这个问题,因为hbm2ddl在首次运行是创建了数据库,并且随后的应用程序重启会使用这个数据库,如果你修改了数据库设计,你就需要重新打开hbm2ddl开关。 
 
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值