(一个用 Java 编写的内存数据库)来避免花费篇章对数据库服务器的安装/配置进行解释。
所鼓吹的“标准格式”。特别是 Maven,它的资源对这个有着很好的描述。因为本教程使用的是 web 应用程序,我么将创建和使用 src/main/java、src/main/resources 和 src/main/webapp 目录。
在本教程里我们将使用 Maven,利用其 transitive dependency 管理以及根据 Maven 描述符用 IDE 自动设置项目的能力。
- <project xmlns=""
-
xmlns:xsi=""
-
xsi:schemaLocation=" ">
-
-
<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>
提示
It
is not a requirement to use Maven. If you wish to use something else to
build this tutorial (such as Ant), the layout will remain the same. The
only change is that you will need to manually account for all the
needed dependencies. If you use something like providing transitive dependency management you would still use the dependencies mentioned below. Otherwise, you'd need to grab all
dependencies, both explicit and transitive, and add them to the
project's classpath. If working from the Hibernate distribution bundle,
this would mean hibernate3.jar, all artifacts in the lib/required directory and all files from either the lib/bytecode/cglib or lib/bytecode/javassist directory; additionally you will need both the servlet-api jar and one of the slf4j logging backends.
把这个文件保存为项目根目录下的 pom.xml。
- 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;
-
}
-
}
- <?xml version="1.0"?>
-
<!DOCTYPE hibernate-mapping PUBLIC
-
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
-
"">
-
-
<hibernate-mapping package="org.hibernate.tutorial.domain">
-
[...]
-
</hibernate-mapping>
- <hibernate-mapping package="org.hibernate.tutorial.domain">
-
-
<class name="Event" table="EVENTS">
-
-
</class>
-
-
</hibernate-mapping>
- <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>
最后我们在映射文件里面包含需要持久化属性的声明。默认情况下,类里面的属性都被视为非持久化的:
- <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 使用哪个 getter 和 setter 方法。在此例中,Hibernate 会寻找 getDate()、setDate()、getTitle() 和 setTitle() 方法。
注意
为什么 date 属性的映射含有 column attribute,而 title 却没有?当没有设定 column attribute 的时候,Hibernate 缺省地使用 JavaBean 的属性名作为字段名。对于 title,这样工作得很好。然而,date 在多数的数据库里,是一个保留关键字,所以我们最好把它映射成一个不同的名字。
另一有趣的事情是 title 属性缺少一个 type attribute。我们在映射文件里声明并使用的类型,却不是我们期望的那样,是 Java 数据类型,同时也不是 SQL 数据库的数据类型。这些类型就是所谓的 Hibernate 映射类型(mapping types),它们能把 Java 数据类型转换到 SQL 数据类型,反之亦然。再次重申,如果在映射文件中没有设置 type 属性的话,Hibernate 会自己试着去确定正确的转换类型和它的映射类型。在某些情况下这个自动检测机制(在 Java 类上使用反射机制)不会产生你所期待或需要的缺省值。date 属性就是个很好的例子,Hibernate 无法知道这个属性(java.util.Date 类型的)应该被映射成:SQL date,或 timestamp,还是 time 字段。在此例中,把这个属性映射成 timestamp 转换器,这样我们预留了日期和时间的全部信息。
提示
当处理映射文件时,Hibernate 用反射(reflection)来决定这个映射类型。这需要时间和资源,所以如果你注重启动性能,你应该考虑显性地定义所用的类型。
把这个映射文件保存为 src/main/resources/org/hibernate/tutorial/domain/Event.hbm.xml。
和 。然而,在本教程里我们将使用 Hibernate 内置的连接池。
小心
嵌入的 Hibernate 连接池不用于产品环境。它缺乏连接池里的几个功能。
为了保存 Hibernate 的配置,我们可以使用一个简单的 hibernate.properties 文件,或者一个稍微复杂的 hibernate.cfg.xml,甚至可以完全使用程序来配置 Hibernate。多数用户更喜欢使用 XML 配置文件:
- <?xml version='1.0' encoding='utf-8'?>
-
<!DOCTYPE hibernate-configuration PUBLIC
-
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
-
"">
-
-
<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
注意
请注意,这个配置文件指定了一个不同的 DTD。
注意这个 XML 配置使用了一个不同的 DTD。在这里,我们配置了 Hibernate 的SessionFactory — 一个关联于特定数据库全局的工厂(factory)。如果你要使用多个数据库,就要用多个的 ,通常把它们放在多个配置文件中(为了更容易启动)。
签名 4 个 property 元素包含了 JDBC 连接所必需的配置。方言 property 元素指定了 Hibernate 生成的特定 SQL 语句。
提示
In most cases, Hibernate is able to properly determine which dialect to use. See for more information.
最开始的 4 个 property 元素包含必要的 JDBC 连接信息。方言(dialect)的 property 元素指明 Hibernate 生成的特定 SQL 变量。你很快会看到,Hibernate 对持久化上下文的自动 session 管理就会派上用场。 打开 hbm2ddl.auto 选项将自动生成数据库模式(schema)- 直接加入数据库中。当然这个选项也可以被关闭(通过去除这个配置选项)或者通过 Ant 任务 SchemaExport 的帮助来把数据库 schema 重定向到文件中。最后,在配置中为持久化类加入映射文件。
把这个文件保存为 src/main/resources 目录下的 hibernate.cfg.xml。
获得 Maven。Maen 将读取我们先前创建的 /pom.xml 并知道执行基本的项目任务。首先,让我们运行 compile 目标来确保我们可以编译到目前为止的所有程序:
- [hibernateTutorial]$ mvn compile
-
[INFO] Scanning for projects...
-
[INFO] ------------------------------------------------------------------------
-
[INFO] Building First Hibernate Tutorial
-
[INFO] task-segment: [compile]
-
[INFO] ------------------------------------------------------------------------
-
[INFO] [resources:resources]
-
[INFO] Using default encoding to copy filtered resources.
-
[INFO] [compiler:compile]
-
[INFO] Compiling 1 source file to /home/steve/projects/sandbox/hibernateTutorial/target/classes
-
[INFO] ------------------------------------------------------------------------
-
[INFO] BUILD SUCCESSFUL
-
[INFO] ------------------------------------------------------------------------
-
[INFO] Total time: 2 seconds
-
[INFO] Finished at: Tue Jun 09 12:25:25 CDT 2009
-
[INFO] Final Memory: 5M/547M
-
[INFO] ------------------------------------------------------------------------
- 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;
-
}
-
-
}
- 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();
-
}
-
-
}
A org.hibernate.Session
is designed to represent a single unit of work (a single atomic piece
of work to be performed). For now we will keep things simple and assume a
one-to-one granularity between a Hibernate org.hibernate.Session and a database transaction. To shield our code from the actual underlying transaction system we use the Hibernate org.hibernate.Transaction API. In this particular case we are using JDBC-based transactional semantics, but it could also run with JTA.
for more information about transaction handling and demarcation. The
previous example also skipped any error handling and rollback.
要运行它,我们将使用 Maven exec 插件以及必要的 classpath 设置来进行调用:mvn exec:java -Dexec.mainClass="org.hibernate.tutorial.EventManager" -Dexec.args="store"。
你应该会看到,编译以后,Hibernate 根据你的配置启动,并产生一大堆的输出日志。在日志最后你会看到下面这行:
[java] Hibernate: insert into EVENTS (EVENT_DATE, title, EVENT_ID) values (?, ?, ?)
执行 HQL INSERT 语句的例子如下:
我们想要列出所有已经被存储的 events,就要增加一个条件分支选项到 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;
-
}
Here, we are using a Hibernate Query Language (HQL) query to load all existing Event objects from the database. Hibernate will generate the appropriate SQL, send it to the database and populate Event objects with the data. You can create more complex queries with HQL. See for more information.
现在我们可以再次用 Maven exec plugin - mvn exec:java -Dexec.mainClass="org.hibernate.tutorial.EventManager" -Dexec.args="list" 调用新的功能了。