翻译 Hibernate 3.3.2 参考文档(一)

来源:互联网 发布:什么图案画数据对比表 编辑:程序博客网 时间:2024/06/08 18:12

     在翻译之前,我首先声明一下,我翻译这个文档主要是为了让自己对Hibernate的各种配置有更深刻的理解。之所以贴出来,一是想和大家一起分享一下,二来也算是激励一下自己。在翻译的时候,我会去参考网上的一些文档。如果有什么错误的话,还请大家指出来。大家相互学习共同进步。

_________________________________________________________________________________________________________

前言

      在今天的企业环境中,在面向对象的软件编程和关系型数据库之间的转化是十分繁琐和费时的。Hibernate是一项用于java语言的对象/关系映射工具(ORM)。对象关系映射(ORM)是指将数据表现方式从对象模型映射到基于SQL模式的关系型数据库的技术。

        Hibernate不仅负责将Java类映射到数据库表中(以及将Java数据类型转化为SQL数据类型),而且还提供了数据查询和检索设施。使用Hibernate可以明显的缩短开发时间,而且可以避免使用SQL和JDBC手工处理数据。
Hibernate的目标是从95%的常见数据持久化相关的编程任务中减轻开发人员的负担。在那些数据库中只使用存储过程来实现业务逻辑的以数据为中心的应用程序的中Hibernate可能不是解决方案,Hibernate最大的作用是作为面向对象的域模型和业务逻辑之间的Java中间层。Hibernate可以帮助你去除或者封装供应商提供的特定的SQL代码,而且能将所有任务的结果集从表格形式转化为图形对象的形式。

1.1. 第一部分 第一个Hibernate应用程序

        对于这个例子,我们将会新建一个小型的数据库应用程序,用来保存那些我们想要参加的事件(events)和这些事件(events)的主办方的信息。(避免混淆,在后文中直接使用events)。

注意:你可以使用任何你喜欢的数据库,在这里为了避免描述数据库服务器的安装步骤,我们将会使用HSQL数据库。

1.1.1. 安装

        首先,我们需要安装和配置我们的开发环境。我们将会使用“标准目录”来开发,因为“标准目录”被很多构建工具所倡导,特别是Maven,它有一些描述这些目录的很好的资源。由于本教程是一个web应用程序,我们将会创建和使用“src/main/java”“src/main/resources”和“src/main/webapp”目录。

        在这个教程里面我们会使用Maven,利用它传递依赖管理能力以及许多IDE可以通过描述文件自动建立项目的能力。(taking advantage of its transitive dependency management capabilities有些不知道如何翻译)

<project xmlns="http://maven.apache.org/POM/4.0.0"         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <groupId>org.hibernate.tutorials</groupId>    <artifactId>hibernate-tutorial</artifactId>    <version>1.0.0-SNAPSHOT</version>    <name>First Hibernate Tutorial</name>    <build>         <!-- we dont want the version to be part of the generated war file name -->         <finalName>${artifactId}</finalName>    </build>    <dependencies>        <dependency>            <groupId>org.hibernate</groupId>            <artifactId>hibernate-core</artifactId>        </dependency>        <!-- Because this is a web app, we also have a dependency on the servlet api. -->        <dependency>            <groupId>javax.servlet</groupId>            <artifactId>servlet-api</artifactId>        </dependency>        <!-- Hibernate uses slf4j for logging, for our purposes here use the simple backend -->        <dependency>            <groupId>org.slf4j</groupId>            <artifactId>slf4j-simple</artifactId>        </dependency>        <!-- Hibernate gives you a choice of bytecode providers between cglib and javassist -->        <dependency>            <groupId>javassist</groupId>            <artifactId>javassist</artifactId>        </dependency>    </dependencies></project>

提示:并非一定要使用Maven。如果你想使用其他的工具来建立这个教程(例如Ant),生成的目录也是一样的。(因为没有使用过Ant和Maven,所以下面一些关于Ant的设置暂时翻译不了)

将这个文件命名为pom.xml然后放在项目的根目录下。

1.1.2.第一个类

接下来,我们创建一个event的类。这是一个只有几个属性的JavaBean类。

package org.hibernate.tutorial.domain;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;    }}


        这个类里面属性的get方法和set方法使用了标准的JavaBean命名约束,并且所有域(field)的访问级别都设为private。虽然这样设计是值得提倡的,但是并不是必须的。Hibernate也可以直接访问域值,使用访问方法的好处是重构时的健壮性。

        特定的event,id属性有唯一标识符的值。如果我们想要使用Hibernate提供的所有特性,那么所有的持久化实体类(persistent entity classes)(以及一些不太重要的依赖类)都需要这样的标识符属性。实际是大部分的应用程序,特别是web应用程序,需要通过标识符来区分对象,所以你应该认为这是一种特性,而不是一种限制。然而,我们通常不会去操作一个对象的标识符,因此,标识符的set方法应该定义为private。当对象被保存的时候只有Hibernate会为它分配标识符。Hibernate可以直接访问public、private和protected的访问方法,以及public、private和protected的域。选择哪种方式由你决定,你可以选择合适你的应用程序设计的方法。

       对于所有持久化类来说,无参构造方法是必须的。因为Hibernate必须要使用Java反射机制来为你创建对象。构造器可以是私有的,但是当要生成运行时代理或是想要在没有字节码(bytecode instrumentation)的情况下进行高效的数据检索,构造函数的访问级别必须为package或public。

       将这个文件保存到src/main/java/org/hibernate/tutorial/domain目录下。

1.1.3.映射文件

        Hibernate需要知道怎样去加载(load)和存储(store)持久类的对象。这正是Hibernate映射文件发挥作用的地方。映射文件会告诉Hibernate应该访问数据库里面的哪个表(table)以及应该使用表里面的哪些字段(column)。

一个映射文件的基本结构看起来是这样的:

<?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 package="org.hibernate.tutorial.domain">[...]</hibernate-mapping>

        Hibernate的DTD是很复杂的。你可以使用它在编辑器或是IDE里面自动完成XML文件的元素和属性的映射。在你的文本编辑器打开DTD文件,是查看所有元素和属性并且查看默认设置以及评论最简单的方法。Hibernate不会自动到网上去下载DTD文件,但是他会首先到应用程序的classpath路径下面去查找DTD文件。DTD文件已经包含在了hibernate-core.jar里面了(它也包含在hibernate3.jar,同时也在Hibernate发布包的src/目录下)。

重要:为了缩短代码,在后面的例子里面我们会省略DTD文件的声明,当然它不是可有可无的。

       在两个hibernate-mapping标签之间,要包括一个实体类的元素。所有的持久化实体类(再次声明,这里也包含那些依赖类,那些不是次要的实体类)需要映射到SQL数据库的表上。

<hibernate-mapping package="org.hibernate.tutorial.domain">    <class name="Event" table="EVENTS">    </class></hibernate-mapping>

       到目前为止,我们已经告诉Hibernate如何将Event类的对象持久化到数据库的EVENTS表中。每一个实例对象就代表数据库EVENTS表中的一行记录。现在我们可以继续将唯一标识符映射到表格的主键上。正如我们不关心如何处理标识符那样,我们可以配置Hibernate的标识符产生策略来生成一个代理主键字段:

<hibernate-mapping package="org.hibernate.tutorial.domain">    <class name="Event" table="EVENTS">        <id name="id" column="EVENT_ID">            <generator class="native"/>        </id>    </class></hibernate-mapping>


       id元素是标识符属性的声明。name="id"声明了JavaBean的映射属性的名称,告诉Hibernate使用gatId()方法和setId()方法来访问这个属性。column属性告诉Hibernate使用那一列来作为EVENTS表的主键。

       嵌套的generator元素指定标识符的生成策略(也就是标识符的值是如何产生的?)。在这里我们选择native,它会根据已配置的数据库方言(dialect)自动选择最佳的标识符生成策略。Hibernate支持数据库生成的全局唯一的标识符以及应用程序分配的标识符。如何生成标识符的值也是Hibernate多个可以扩展的点之一,你可以插入你自己的策略。

提示:如果就可移植性而言的话,native不是最佳的策略,后面会讨论到。

最后,我们需要告诉Hibernate关于实体类中剩余的属性。在默认情况下,类里面的所有属性都被认为是非持久化的。

<hibernate-mapping package="org.hibernate.tutorial.domain">    <class name="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使用哪个get方法和set方法来访问持久化类对象中的属性。在上面的配置文件中,Hibernate会使用getDate()和setDate()来访问date属性,使用getTitle()和setTitle()方法来访问title属性。

注意:上述配置代码中,为什么data属性配置<property>里面有column属性,但是title的属性配置<property>里面却没有呢?没有column属性,Hibernate默认会使用<property>里面的name来作为column的值。对于title配置就是这样,因为对于date在多部分数据库中都是保留关键字,所以你需要把它映射到一个不同的名字上。

       title映射中还缺少了一个type的属性。在映射文件中声明和使用的类型不是Java的数据类型也不是SQL数据库的类型。这些类型叫做Hibernate映射类型,转换器可以把Java数据类型转化为SQL数据类型,反之亦然。再次声明,如果type属性没有声明,Hibernate自身会去尝试去确定正确地转换和映射属性的类型。但是在某些情况下,Hibernate使用Java类的反射机制自动确定的类型可能不是你所期待的或者需要的。上面的date属性就是这种情况。Hibernate不知道将java.util.Date应该映射为SQL date,timestamp,还是time类型的字段。如果把属性映射成一个timestamp类型的转换器就可以保持完整的日期和时间信息了。

提示:Hibernate在处理映射文件确定映射类型的时候会使用Java的反射机制。这个过程需要花费时间和资源,所以如果启动的性能很重要的话,你应该定义明确的使用类型。

将这个映射文件保存为src/main/resources/org/hibernate/tutorial/domain/Event.hbm.xml

1.1.4.Hibernate配置

现在,你已经有了持久化类和它的映射文件了。现在应该去配置Hibernate了,首先让我们安装和配置HSQL数据库并运行为服务器模式(server mode)。

注意:我们这样做,数据之间仍然存在运行(We do this do that the data remains between runs)。

我们将利用Maven的exec插件来启动HSQL数据库服务器,通过运行:

mvn exec:java -Dexec.mainClass="org.hsqldb.Server" -Dexec.args="-database.0 file:target/data/tutorial" 

       你会看到它启动然后绑定到一个TCP/IP的socket上。这就是稍后我们的应用程序要连接的地方。如果你想在本教程的期间启动一个全新的数据库,那就关掉HSQL数据库,删除target/data目录下面的所有文件,然后再重新启动HSQL数据库。

       Hibernate将代表你的应用程序去连接数据库,所以它需要知道如何获取连接。在本教程中,我们将会使用一个独立的连接池,Hibernate内置支持两个第三方的开源JDBC连接池:c3p0 和 proxool。但是,对于本教程,我们将使用Hibernate内置的连接池。

小心:Hibernate内置的连接池是不能用于生产中使用的,它缺少正常连接池的很多特性。

 对于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">update</property>        <mapping resource="org/hibernate/tutorial/domain/Event.hbm.xml"/>    </session-factory></hibernate-configuration>

注意:注意这个配置文件指定了一个不同的DTD。

        你可以配置Hibernate的SessionFactory。SessionFactory是为某一特定的数据库负责的全局性工厂。如果你有多个数据库,为了更方便启动,你应该在多个配置文件中使用多个<session-factory>配置。最开始的四个property元素包含了JDBC连接的必要信息。方言(dialect)的property元素指定了Hibernate生成特定的SQL语句。

提示:在大部分情况下,Hibernate能正确地决定使用哪种方言。

        在这种情况下,Hibernate对持久化上下文的自动session管理十分有用。打开hbm2ddl.auto选项将会直接在数据库中自动生成数据库的图表。当然这个选项也是可以关闭的,通过删除这个配置项,或者通过Ant任务SchemaExport的帮助,来把数据库的图表重定向到一个文件中。最后,把持久化类的映射文件加入到配置中。

将这个文件命名为hibernate.cfg.xml,保存到src/main/resources目录下。

1.1.5.使用Maven构建

1.1.6.启动和辅助类

      现在我们可以来加载和保存一些Event对象了,不过首先你必须先完成一些基础的代码。你在启动Hibernate之前必须先创建一个全局的SessionFactory对象然后将它保存在某处以便在程序代码中可以访问。SessionFactory用于获取Session类的实例对象。一个Session对象代表一个单独的线程操作单元。SessionFactory一旦实例化就是一个线程安全的全局变量。

     我们会创建一个HibernateUtil帮助类来更方便地启动和访问SessionFactory。

package org.hibernate.tutorial.util;import org.hibernate.SessionFactory;import org.hibernate.cfg.Configuration;public class HibernateUtil {    private static final SessionFactory sessionFactory = buildSessionFactory();    private static SessionFactory buildSessionFactory() {        try {            // Create the SessionFactory from hibernate.cfg.xml            return 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;    }}

将上述代码保存到src/main/java/org/hibernate/tutorial/util/HibernateUtil.java目录下。

     这个工具类不仅在他的静态初始化方法中生成立全局的SessionFactory对象的引用,而且还隐藏了SessionFactory对象是单例的事实。我们可以在服务器的JNDI中或者其他地方查找SessionFactory的引用。

      如果你在配置文件中给SessionFactory对象定义了一个name属下,那么当创建SessionFactory对象后,Hibernate根据name属性来试着将它绑定到JNDI。另外,还有一个更好的选择,那就是使用JMX部署并且让具有JMX能力的容器初始化并且将HibernateService绑定到JNDI

      现在你需要配置一个日志系统。Hibernate使用通用的日志接口并且提供了两个选择:log4j和JDK 1.4日志。大部分开发人员会更喜欢使用Log4j:从Hibernate发布包中ect/目录下将log4j.properties文件复制到你的src目录下,与hibernate.cfg.xml文件放在一起。如果你想要更加详细的输出信息的话,可以修改 log4j.properties文件的配置。在默认情况下,只有Hibernate的启动信息会显示到标准输出上。
这个教程的基本配置已经完成了,你现在可以准备用Hibernate去做一些真正的工作了。

1.1.7.加载并存储对象

现在让我们开始用Hibernate做一些事情,让我们先写一个有main方法的EventManager类。

package org.hibernate.tutorial;import org.hibernate.Session;import java.util.*;import org.hibernate.tutorial.domain.Event;import org.hibernate.tutorial.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();    }}

     在createAndStoreEvent()方法里面我们创建了一个新的Event对象并且把它传递给Hibernate。当我们把Event对象交给Hibernate的时候,Hibernate需要关心SQL和向数据库执行插入操作。

        Session类作为一个单一的操作单元,现在让我们暂时简单一点,假设HibernateSession和数据库事务是一对一对应的关系。为了让我们的代码从实际的底层的事务系统中脱离出来,我们使用HibernateTransaction API。在这种特殊情况下,我们使用基于JDBC的事务原子性,但是它也可以和JTA一起运行。

       sessionFactory.getCurrentSession()到底做了什么?首先,只要你持有SessionFactory就可以在任何时候任何地方调用它。getCurrentSession()方法总是返回当前的工作“单元”。还记得我们在src/main/resources/hibernate.cfg.xml中把这个配置项改为“thread”吗?因此,当前的操作单元会被绑定到当前正在运行的应用程序的Java线程上。

重要:Hibernate提供了三种跟踪当前会话的方法。(Hibernate offers three methods of current session tracking. The "thread" based method is not intended for production use; it is merely useful for prototyping and tutorials such as this one. Current session tracking is discussed in more detail later on

     当前线程第一次调用getCurrentSession()的时候开始一个Session,然后Hibernate会把它绑定到当前线程上。当事务结束的时候,无论是通过提交(commit)还是回滚(rollback),Hibernate会自动把Session从当前线程剥离(不再绑定),然后你可以关闭Session。如果你再次调用getCurrentSession(),你会得到一个新的Session,并且开始一个新的操作单元。

      与操作单元相关的话题,Hibernate Session是否应该用来执行一次或多次数据库操作?对于上述例子中使用一个Session来执行一次操作。这纯粹是巧合,这个例子不是很复杂,不能展示出其他方式。Hibernate Session的使用是很灵活的,但是在你的应用程序中绝对不能为每一次数据库操作都新建一个Session。
      你应该看到Hibernate启动,并且根据你的配置,输出打量日志信息,接近尾声的时候,会显示下面这行:

[java] Hibernate: insert into EVENTS (EVENT_DATE, title, EVENT_ID) values (?, ?, ?)

这是Hibernate执行了插入操作。

要列出要保存的event的一个主要方法:

 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;    }


这里,我们通常使用Hibernate 查询语言(HQL)查询语句来加载数据库在所有存在的event对象。Hibernate会生成相应的SQL语句,发送到数据库并查询到event对象数据。你也可以创建更加复杂的HQL查询语句。