序:笔者马上要进行Hibernate&Spring的培训,培训之前进行一下预习,本文为权当预习笔记作为记载。
Hibernate简介
本文是Hibernate Getting Started的第一部分:I want to learn Hibernate for java!中的Tutorial的翻译,加之笔者的实践,如有错误之处,敬请指出!
1. 前提条件:
Apache Ant 1.6.5 or higher(
http://ant.apache.org),设置ANT_HOME环境变量,以便ant命令可以直接运行,如果使用的IDE(笔者用的是Eclipse Europa)集成了Ant,此条件可以不考虑。
servlet.jar or servlet-api.jar(
http://tomcat.apache.org),在Tomcat的lib包中含有这些库文件,5.x包含的是servlet.jar,6.x包含的是servlet-api.jar,主要使用其中的一些Servlet API。
其中,Hibernate3只下载core版本即可使用本文,关于其他的文件,例如:
Annotations、EntityManager、Extensions、Search、Shards、Validator等内容可以在使用的时候进行下载。
笔者下载的Hibernate版本为3.2,下载后的文件为hibernate-3.2.5.ga.zip,将其解压到本地目录中,本文中以HB_HOME作为Hibernate的主目录。
本文所涉及到的代码及文件包含在HB_HOME/doc/reference/tutorial/中。
首先,我们创建一个命令行为基础的简单的java应用程序,我们使用HSQL DB,当然,也可以使用其他的Database软件,这里为了方便,使用HSQL DB。创建样例Project的主目录为HibernateSample(笔者是根据Tutorial的Step来设置的工程名,首先是HibernateEvent工程),工程的源代码目录为src,目标目录为bin,库文件目录为lib。
将Hibernate的jar包拷贝到HibernateSample的lib目录下,Hibernate必须的jar包如下:
.
+lib
antlr.jar
cglib.jar
asm.jar
asm-attrs.jars
commons-collections.jar
commons-logging.jar
hibernate3.jar
jta.jar
dom4j.jar
log4j.jar
根据Hibernate版本的不同,也可能有不同的必须jar包,可以参考HB_HOME\lib下的reademe.txt来查看哪些是必须的,哪些是非必须的。其中hibernate3.jar是Hibernate自身的jar包。log4j.jar虽然不是必须的,但是在很多开发中都将使用,所以也包含了进来。
笔者在使用Tutorial的时候,是将所有的HB_HOME下的jar文件都拷贝到HibernateSample\lib目录中,然后再拷贝以下文件:hibernate3.jar(位于HB_HOME目录下),servlet.jar(Tomcat\common\lib目录下)。
创建第一个JavaBean类:events.Event,代码如下:
package events;
import java.util.*;
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;
}
}
这就是一个简单的JavaBean,包含一些private属性和public方法,其中对于Hibernate有以下特点:
Hibernate可以直接存取私有属性。
id属性包含一个唯一的标志符。所有的持久化类对象都有一个唯一的标记符。事实上,很多应用程序都是通过标志来识别对象的。由于我们不会手动的管理id的值,所以setId方法被设置为private。
所有的持久化类都需要一个不带参数的构造函数,Hibernate需要通过Java Reflection来创建实例。
2. Hibernate映射:Hibernate需要知道如何加载和存储持久化的类,这就是Hibernate映射需要完成的工作。映射文件告诉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工具进行查看DTD文件,该文件中包含了一些注释,可以帮助理解映射文件的内容。注意,Hibernate并不从网络中加载DTD文件,而是首先从classpath中寻找,DTD文件包含在hibernate3.jar文件中,也包含在HH_HOME\src目录中。
在hibernate-mappings元素中,包含一个class元素,所有的持久化实体类需要一个到数据库表的映射。
<hibernate-mapping>
<class name="events.Event" table="EVENTS">
</class>
</hibernate-mapping>
这段配置告诉Hibernate如何存储和加载类Event的对象到EVENTS表,每一个对象对应着表中的一行。
现在,我们继续来研究表的主键与唯一标志属性的映射,由于我们不关心这个标记符的处理,所以我们使用代理的主键列。
<hibernate-mapping>
<class name="events.Event" table="EVENTS">
<id name="id" column="EVENT_ID">
<generator class="native"/>
</id>
</class>
</hibernate-mapping>
id元素是标志属性的声明,name=”id”是Java属性的声明。Hibernate使用getter和setter方法来存取属性。column属性告诉Hibernate在EVENTS表中的哪一列对应着主键。嵌套的generator元素指定了标志的生成策略,这里我们使用native策略(根据配置的数据库选择最好的策略)。
最后,我们将类中的持久化属性添加到映射文件中,默认情况下,类的所有属性都不是持久化的。
<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和settter方法。在上面的配置文件中,Hibernate会查找getDate()/setDate()和getTitle()/setTtile()方法。
如果property没有包含column属性,那么Hibernate默认使用property的名字作为数据库表的列的名字,对于title来说没有问题。但是在大多数数据库中,date是一个保留字,所以最好将其映射到其他的名字。
另外一个比较有趣的话题是,title还缺少一个type属性(和date相比)。也许你认为在映射文件中的声明的类型是java数据类型,那就错了。是SQL数据库类型?也不对。这些types是所谓的Hibernate映射类型,转化器可以将其从java数据类型转换为SQL数据类型,反之亦然。让type属性不存在的时候,Hibernate试图确定正确的转换以及映射的类型。在某些情况下,这个自动的检测(对java类使用Reflection)可能没有你期望或者需要的默认类型。date属性就属于这种类型。Hibernate不知道java.util.Date属性应该映射到SQL的date,timestamp还是time列上,我们通过使用timestamp转换来获取完整的date和time信息。
这个映射文件应该以Event.hbm.xml作为文件名保存。和java类文件在同一个目录。映射文件名字是任意的,但是hbm.xml后缀是一个习惯命名。现在的目录结构应该是如下的结构:
.
+lib
<Hibernate and third-party libraries>
+src
+events
Event.java
Event.hbm.xml
现在我们已经完成了一个持久化的java类以及一个映射文件。
接下来要做的就是配置Hibernate。在这之前,我们先进行一下HSQL DB的配置。HSQL DB是一个基于java的SQL DBMS,可以从其官方网站进行下载。实际上,我们只需要一个hsqldb.jar文件,将其放在HibernateSample\lib目录下。
在HibernateSample目录下创建一个data目录,HSQL DB将数据保存在这个目录中。现在打开一个命令行提示符(Windows下),进入HibernateSample\data目录下,执行如下命令:
Java –classpath ../lib/hsqldb.jar org.hsqldb.Server
此时可以看到如下信息:
G:\eclipse\workspace\HibernateEvent\data>java -classpath ../lib/hsqldb.jar org.h
sqldb.Server
[Server@83cc67]: [Thread[main,5,main]]: checkRunning(false) entered
[Server@83cc67]: [Thread[main,5,main]]: checkRunning(false) exited
[Server@83cc67]: Startup sequence initiated from main() method
[Server@83cc67]: Loaded properties from [G:\eclipse\workspace\HibernateEvent\dat
a\server.properties]
[Server@83cc67]: Initiating startup sequence...
[Server@83cc67]: Server socket opened successfully in 219 ms.
[Server@83cc67]: Database [index=0, id=0, db=file:test, alias=] opened sucessful
ly in 921 ms.
[Server@83cc67]: Startup sequence completed in 1156 ms.
[Server@83cc67]: 2007-10-13 12:36:03.937 HSQLDB server 1.8.0 is online
[Server@83cc67]: To close normally, connect and execute SHUTDOWN SQL
[Server@83cc67]: From command line, use [Ctrl]+[C] to abort abruptly
并在data目录中生成如下文件:test.lck,test.log和test.properties
其中test.properties中记录了一些#HSQL Database Engine的信息,例如版本等。
test.log中包含了创建用户和密码的信息。CREATE USER SA PASSWORD "" ADMIN
如果在本教程中需要使用纯净的数据库,那么通过按下CTRL+C,结束HSQL DB,然后删除data目录下的所有文件,重新启动HSQL DB。
Hibernate是应用程序链接数据库的外层,所以它需要一些连接信息。连接通过JDBC’连接池完成,这是需要进行配置的。Hibernate发布包中包含了几个开源的JDBC连接池,但是在本教程中使用内置(built-in)的连接池。当使用不同的连接池时,需要将连接池的驱动程序拷贝到类路径中。
可以使用hibernate.properties文件,或者稍微复杂些的hibernate.cg.xml文件,甚至完全使用编程的方法来进行Hibernate的配置,大多数用户使用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指定了Hibernate生成的特定的SQL变量。Hibernate的自动持久化会话管理上下文(automatic session management for persistence contexts)。属性hbm2ddl启用自动生成数据库schemas-直接存入数据库。当然也可以禁用这个属性,或者通过使用Ant的SchemaExport任务将其重定向到一个文件中。最后,我们将持久化类的映射文件(一个或者多个)添加到配置文件中。
将配置文件拷贝到src目录下,这样它就会自动的包含classpath的根目录中,在启动时,Hibernate自动查找文件名为hibernate.cfg.xml的文件在classpath的根目录中。
使用Ant构建,搭建好Ant的运行时环境后,编写build.xml文件,将其存放在HibernateSample目录下,内容如下:
<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将HibernateSample\lib目录下的所有.jar文件添加到编译的classpath中,并将所有非java源文件拷贝到目标目录中(bin)。例如Hibernate配置文件和映射文件,然后执行Ant命令,结果如下:(笔者是在Eclipse中执行的Ant命令)
Buildfile: G:\eclipse\workspace\HibernateEvent\build.xml
clean:
[delete] Deleting directory G:\eclipse\workspace\HibernateEvent\bin
[mkdir] Created dir: G:\eclipse\workspace\HibernateEvent\bin
copy-resources:
[copy] Copying 2 files to G:\eclipse\workspace\HibernateEvent\bin
compile:
[javac] Compiling 1 source file to G:\eclipse\workspace\HibernateEvent\bin
BUILD SUCCESSFUL
Total time: 2 seconds
到目前位置,目录结构如下:
HibernateEvent
-src
-events
Event.java
Event.hbm.xml
hibernate.cfg.xml
+data
-lib
readme.txt
...jars
build.xml
3. 启动
在加载和存储一些Event对象之前,先完成基础设施(util)代码。我们得启动Hibernate。这里说的启动包括创建一个全局的SessionFactory对象,然后将其保存在程序代码可以引用的地方。SessionFactory可以打开新的Session,Session代表一个单线程的工作单元,SessionFactory是一个线程安全的全局对象,只初始化一次。
我们创建一个叫做HibernateUtil的辅助类,这个类用于方便的启动和调用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;
}
}
这个类不仅静态初始化了一个全局的SessionFactory(在类加载时,JVM只调用该方法一次),并且隐藏了它使用静态单例的事实。它也可能在应用程序服务器的JNDI中查找SessionFactory。
如果在配置文件中为SessionFactory指定一个名字,创建完这个对象后Hibernate会试着将其绑定到JNDI。为了避免这样的代码,可以使用JMX部署,让JMX初始化并绑定HibernateService到JNDI。这里不做深入的讨论。
此时工程目录结构如下:
HibernateEvent
-src
-events
Event.java
Event.hbm.xml
-util
HibernateUtil.java
hibernate.cfg.xml
-data
test.lck
test.log
test.properties
-lib
readme.txt
...jars
build.xml
此时也应该没有编译错误。
最后,我们配置一下日志系统。Hibernate使用通用的日志,你可以在log4j和JDK1.4的日志机制中进行选择。大多数开发者喜欢使用Log4j:将Hibernate发布包中(HB_HOME\etc目录)的log4j.properties文件拷贝到HibernateSample目录中,和hibernate.cgf.xml同级。一般情况下,只有Hibernate启动信息在标准输出中显示,可以根据需要进行修改调试信息输出的多少。
接下来,我们开始使用Hibernate做一些实际的工作了。
4. 加载和存储对象
我们终于可以使用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语句,将其INSERT到数据库,在执行之前,我们先看一下Session和Transaction代码:
Session是一个单独的工作单元。简而言之,Hibernate的Session和数据库的Transaction是一对一的关系。为了隔离我们的代码与实际的底层事务系统(本例中是JDBC,也可能是JTA Java Transaction API),我们使用在Hibernate中使用Session来完成Transaction API。
sessionFactory.getCurrentSession()完成以下工作:
可以在任意位置调用任意次这个方法,一旦你获取了SessionFactory对象,getCurrentSession()方法总是返回当前的工作单元。还记得我们在hibernate.cfg.xml中对这个配置的设置吗?
<!-- Enable Hibernate's automatic session context management -->
<property name="current_session_context_class">thread</property>
因此,当前的工作单元与当前的Java线程绑定在一起的。然后,你还需要考虑范围,工作单元的起始和结束。
Session当第一次需要的时候就开始了,当第一次调用getCurrentSession()的时候创建了Session,借下来被Hibernate绑定到当前线程中。当事务结束时(通过commit或者rollback),Hibernate自动将其从当前线程中接触绑定,并将其关闭。当你再次调用getCurrentSession()方法时,就获取了一个新的Session,从而开始一个新的工作单元。线程绑定编程模型是使用Hibernate的最常用方式,因为它允许代码的灵活层次化(事务分界代码可以与数据存取代码相分离,在本教程后面会提及到)。
对于一个工作单元,Hibernate的Session是否可以用来执行一个或者多个数据库操作?上面的例子中使用Session进行了一个操作。这只是一个巧合,上面的例子比较简单而已。Session的范围很灵活,但是在应用程序中对每个database操作使用一个新的Session是绝对不应该的。所以虽然你在本教程的许多例子中看到这种情况,但是session-per-operation是一个不推荐的模式。在本教程的后面将展示一个实际的应用程序。
Chapter11,Transactions And Concurrency(事务与并发)提供了关于事务处理的更多信息。同样,在上例中忽略了错误处理和rollback。
为了运行第一个例子,需要在Ant中添加一个可以调用的target:
<target name="run" depends="compile">
<java fork="true" classname="events.EventManager" classpathref="libraries">
<classpath path="${targetdir}"/>
<arg value="${action}"/>
</java>
</target>
参数action的值实在调用target时在命令行上进行设置的,例如:
C:\hibernateTutorial\>ant run –Daction=store
编译完成后,Hibernate启动,根据日志的配置,输出日志信息,在最后将看到如下日志:
Buildfile: G:\eclipse\workspace\HibernateEvent\build.xml
clean:
[delete] Deleting directory G:\eclipse\workspace\HibernateEvent\bin
[mkdir] Created dir: G:\eclipse\workspace\HibernateEvent\bin
copy-resources:
[copy] Copying 3 files to G:\eclipse\workspace\HibernateEvent\bin
[copy] Copied 2 empty directories to 1 empty directory under G:\eclipse\workspace\HibernateEvent\bin
compile:
[javac] Compiling 3 source files to G:\eclipse\workspace\HibernateEvent\bin
run:
[java] 14:38:40,937 INFO Environment:514 - Hibernate 3.2.5
[java] 14:38:40,953 INFO Environment:547 - hibernate.properties not found
[java] 14:38:40,953 INFO Environment:681 - Bytecode provider name : cglib
[java] 14:38:40,953 INFO Environment:598 - using JDK 1.4 java.sql.Timestamp handling
[java] 14:38:41,031 INFO Configuration:1426 - configuring from resource: /hibernate.cfg.xml
[java] 14:38:41,031 INFO Configuration:1403 - Configuration resource: /hibernate.cfg.xml
[java] 14:38:41,359 INFO Configuration:553 - Reading mappings from resource : events/Event.hbm.xml
[java] 14:38:41,515 INFO HbmBinder:300 - Mapping class: events.Event -> EVENTS
[java] 14:38:41,546 INFO Configuration:1541 - Configured SessionFactory: null
[java] 14:38:41,625 INFO DriverManagerConnectionProvider:41 - Using Hibernate built-in connection pool (not for production use!)
[java] 14:38:41,625 INFO DriverManagerConnectionProvider:42 - Hibernate connection pool size: 1
[java] 14:38:41,625 INFO DriverManagerConnectionProvider:45 - autocommit mode: false
[java] 14:38:41,656 INFO DriverManagerConnectionProvider:80 - using driver: org.hsqldb.jdbcDriver at URL: jdbc:hsqldb:hsql://localhost
[java] 14:38:41,656 INFO DriverManagerConnectionProvider:86 - connection properties: {user=sa, password=****}
[java] 14:38:41,796 INFO SettingsFactory:89 - RDBMS: HSQL Database Engine, version: 1.8.0
[java] 14:38:41,796 INFO SettingsFactory:90 - JDBC driver: HSQL Database Engine Driver, version: 1.8.0
[java] 14:38:41,828 INFO Dialect:152 - Using dialect: org.hibernate.dialect.HSQLDialect
[java] 14:38:41,843 INFO TransactionFactoryFactory:31 - Using default transaction strategy (direct JDBC transactions)
[java] 14:38:41,843 INFO TransactionManagerLookupFactory:33 - No TransactionManagerLookup configured (in JTA environment, use of read-write or transactional second-level cache is not recommended)
[java] 14:38:41,843 INFO SettingsFactory:143 - Automatic flush during beforeCompletion(): disabled
[java] 14:38:41,843 INFO SettingsFactory:147 - Automatic session close at end of transaction: disabled
[java] 14:38:41,843 INFO SettingsFactory:154 - JDBC batch size: 15
[java] 14:38:41,843 INFO SettingsFactory:157 - JDBC batch updates for versioned data: disabled
[java] 14:38:41,843 INFO SettingsFactory:162 - Scrollable result sets: enabled
[java] 14:38:41,843 INFO SettingsFactory:170 - JDBC3 getGeneratedKeys(): disabled
[java] 14:38:41,843 INFO SettingsFactory:178 - Connection release mode: auto
[java] 14:38:41,843 INFO SettingsFactory:205 - Default batch fetch size: 1
[java] 14:38:41,859 INFO SettingsFactory:209 - Generate SQL with comments: disabled
[java] 14:38:41,859 INFO SettingsFactory:213 - Order SQL updates by primary key: disabled
[java] 14:38:41,859 INFO SettingsFactory:217 - Order SQL inserts for batching: disabled
[java] 14:38:41,859 INFO SettingsFactory:386 - Query translator: org.hibernate.hql.ast.ASTQueryTranslatorFactory
[java] 14:38:41,859 INFO ASTQueryTranslatorFactory:24 - Using ASTQueryTranslatorFactory
[java] 14:38:41,859 INFO SettingsFactory:225 - Query language substitutions: {}
[java] 14:38:41,859 INFO SettingsFactory:230 - JPA-QL strict compliance: disabled
[java] 14:38:41,859 INFO SettingsFactory:235 - Second-level cache: enabled
[java] 14:38:41,859 INFO SettingsFactory:239 - Query cache: disabled
[java] 14:38:41,859 INFO SettingsFactory:373 - Cache provider: org.hibernate.cache.NoCacheProvider
[java] 14:38:41,859 INFO SettingsFactory:254 - Optimize cache for minimal puts: disabled
[java] 14:38:41,859 INFO SettingsFactory:263 - Structured second-level cache entries: disabled
[java] 14:38:41,875 INFO SettingsFactory:283 - Echoing all SQL to stdout
[java] 14:38:41,875 INFO SettingsFactory:290 - Statistics: disabled
[java] 14:38:41,875 INFO SettingsFactory:294 - Deleted entity synthetic identifier rollback: disabled
[java] 14:38:41,875 INFO SettingsFactory:309 - Default entity-mode: pojo
[java] 14:38:41,875 INFO SettingsFactory:313 - Named query checking : enabled
[java] 14:38:41,906 INFO SessionFactoryImpl:161 - building session factory
[java] 14:38:42,296 INFO SessionFactoryObjectFactory:82 - Not binding factory to JNDI, no JNDI name configured
[java] 14:38:42,312 INFO SchemaExport:154 - Running hbm2ddl schema export
[java] 14:38:42,312 DEBUG SchemaExport:170 - import file not found: /import.sql
[java] 14:38:42,312 INFO SchemaExport:179 - exporting generated schema to database
[java] 14:38:42,312 DEBUG SchemaExport:303 - drop table EVENTS if exists
[java] 14:38:42,359 DEBUG SchemaExport:303 - create table EVENTS (EVENT_ID bigint generated by default as identity (start with 1), EVENT_DATE timestamp, title varchar(255), primary key (EVENT_ID))
[java] 14:38:42,359 INFO SchemaExport:196 - schema export complete
[java] Hibernate: insert into EVENTS (EVENT_ID, EVENT_DATE, title) values (null, ?, ?)
[java] Hibernate: call identity()
[java] 14:38:42,640 INFO SessionFactoryImpl:769 - closing
[java] 14:38:42,640 INFO DriverManagerConnectionProvider:147 - cleaning up connection pool: jdbc:hsqldb:hsql://localhost
BUILD SUCCESSFUL
Total time: 4 seconds
这就是Hibernate执行的INSERT语句,问好代表JDBC绑定的参数。可以通过log4j.properties来查看绑定的参数的值,或者减少调试日志的数量。
现在,我们来查看保存的events,在EventManager的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,发送到数据库,返回Event对象。当然,你可以创建更复杂的HQL。
现在,通过以下步骤来执行和测试所有的功能:
运行ant run –Daction=store来存储数据,当然,先通过dbm2ddl生成数据库schema。
现在禁用hbm2ddl。将该属性从hibernate.cfg.xml中注释掉。通常只是在持续单元测试中将其打开,但是每次运行hbm2ddl都会将你store的所有内容删除。配置文件中的create设置的意思就是“丢弃schema中所有的表,然后重新建立这些表,当SessionFactory创建的时候”。
此时,如果你调用Ant,使用-Daction=list,就可以查看目前位置存储的所有的events了。
Buildfile: G:\eclipse\workspace\HibernateEvent\build.xml
clean:
[delete] Deleting directory G:\eclipse\workspace\HibernateEvent\bin
[mkdir] Created dir: G:\eclipse\workspace\HibernateEvent\bin
copy-resources:
[copy] Copying 3 files to G:\eclipse\workspace\HibernateEvent\bin
[copy] Copied 2 empty directories to 1 empty directory under G:\eclipse\workspace\HibernateEvent\bin
compile:
[javac] Compiling 3 source files to G:\eclipse\workspace\HibernateEvent\bin
run:
……
[java] Hibernate: select event0_.EVENT_ID as EVENT1_0_, event0_.EVENT_DATE as EVENT2_0_, event0_.title as title0_ from EVENTS event0_
[java] Event: My Event Time: 2007-10-13 14:40:42.843
[java] 14:41:42,859 INFO SessionFactoryImpl:769 - closing
[java] 14:41:42,859 INFO DriverManagerConnectionProvider:147 - cleaning up connection pool: jdbc:hsqldb:hsql://localhost
BUILD SUCCESSFUL
Total time: 4 seconds
注意:许多Hibernate初学者会在这里发生问题。经常出现Table not found错误消息。因为hbm2ddl在第一次运行的时候创建database schema,随后的重启应用程序会使用这个schema。如果修改了映射和/或者数据库schema,就必须重新启用hbm2ddl。
第一部分到此结束。
下一部分,将介绍Mapping Associations,关联映射。
发表于 @ 2008年04月10日 01:29:00|编辑