TA的每日心情 | 开心 2021-3-12 23:18 |
---|
签到天数: 2 天 [LV.1]初来乍到
|
创建一个命令行为基础的简单的java应用程序,利用Hibernate持久化java对象。
一、准备工作
1. 安装Java SDK 5.0 or 6.0
2. 下载Hibernate3(http://www.hibernate.org)
3. 安装mysql数据库
4. 将Hibernate的jar包拷贝到工作目录c:worklib下,mysql的驱动程序mysql-connector-java-3.1.10-bin.jar也拷入这个目录,hibernate必须的jar包如下:
antlr.jar
cglib.jar
asm.jar
asm-attrs.jars
commons-collections.jar
commons-logging.jar
hibernate3.jar
jta.jar
dom4j.jar
log4j.jar
5.编译用的批处理文件h.bat:
set classpath=.;c:worklibhibernate3.jar;c:worklibmysql-connector-java-3.1.10-bin.jar;c:worklibcommons-logging-1.0.4.jar;c:worklibdom4j-1.6.1.jar;c:worklibcommons-collections-2.1.1.jar;c:worklibc3p0-0.9.1.jar;c:worklibasm.jar;c:worklibcglib-2.1.3.jar;c:worklibjta.jar;c:worklibantlr-2.7.6.jar
二、要持久化的java对象 创建第一个JavaBean类:代码如下:- 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来创建实例。 三、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文件中。 在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后缀是一个习惯命名 四、配置Hibernate
Hibernate是应用程序链接数据库的外层,所以它需要一些连接信息。连接通过JDBC连接池完成,这是需要进行配置的。Hibernate发布包中包含了几个开源的JDBC连接池,但是在本教程中使用内置(built-in)的连接池。当使用不同的连接池时,需要将连接池的驱动程序拷贝到类路径中。
可以使用hibernate.properties文件,或者稍微复杂些的hibernate.cg.xml文件,甚至完全使用编程的方法来进行Hibernate的配置,大多数用户使用XML配置文件: <?xml version="1.0" encoding="GBK"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<!--上面四行对所有的hibernate连接配置文件都相同 -->
<!-- hibernate- configuration是连接配置文件的根元素 -->
<hibernate-configuration>
<session-factory>
<!-- 指定连接数据库所用的驱动 -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- 指定连接数据库的url,hibernate连接的数据库名 -->
<property name="connection.url">jdbc:mysql://localhost/hbs</property>
<!-- 指定连接数据库的用户名 -->
<property name="connection.username">root</property>
<!-- 指定连接数据库的密码 -->
<property name="connection.password"></property>
<!-- 指定连接池的大小-->
<!-- C3P0 connection pool -->
<property name="hibernate.c3p0.max_size">20</property>
<property name="hibernate.c3p0.min_size">1</property>
<property name="hibernate.c3p0.timeout">5000</property>
<property name="hibernate.c3p0.max_statements">100</property>
<property name="hibernate.c3p0.idle_test_period">3000</property>
<property name="hibernate.c3p0.acquire_increment">2</property>
<property name="hibernate.c3p0.validate">true</property>
<!-- 指定数据库方言-->
<property name="dialect">org.hibernate.dialect.MySQLDialect</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> <!-- 根据需要自动创建数据库表-->
<property name="hbm2ddl.auto">create</property>
<!-- 罗列所有的映射文静-->
<mapping resource="events/Event.hbm.xml"/>
</session-factory>
</hibernate-configuration> 首先,这个XML配置文件使用一个不同于映射配置文件的DTD,Hibernate的SessionFactory元素用于配置一个针对于特定数据库的全局工厂类。如果使用多个数据库时,为了方便启动,一般使用多个配置文件,或者包含多个<session-factory>。 属性hbm2ddl启用自动生成数据库schemas-直接存入数据库。最后,我们将持久化类的映射文件(一个或者多个)添加到配置文件中。 五、启动
在加载和存储一些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。这里不做深入的讨论。 六、加载和存储对象
我们终于可以使用Hibernate来加载和存储对象了。我们编写一个带main方法的EventManager类,内容如下:- package events;
- import org.hibernate.Session;
- import java.util.Date;
- import java.util.List;
- import util.HibernateUtil;
- public class EventManager {
- public static void main(String[] args) {
- EventManager mgr = new EventManager();
- if (args[0].equals("store")) {
- mgr.createAndStoreEvent("My Event1", 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());
- }
- }
- HibernateUtil.getSessionFactory().close();
- }
- private List listEvents() {
- Session session = HibernateUtil.getSessionFactory().getCurrentSession();
- session.beginTransaction();
- List result = session.createQuery("from Event").list();
- session.getTransaction().commit();
- return result;
- }
- 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是一个不推荐的模式。在本教程的后面将展示一个实际的应用程序。
七:编译和运行
c:work>h.bat c:work>set classpath=.;c:worklibhibernate3.jar;c:worklibmysql-connector-java-3.
1.10-bin.jar;c:worklibcommons-logging-1.0.4.jar;c:worklibdom4j-1.6.1.jar;c:work
libcommons-collections-2.1.1.jar;c:worklibc3p0-0.9.1.jar;c:worklibasm.jar;c:
kklibcglib-2.1.3.jar;c:worklibjta.jar;c:worklibantlr-2.7.6.jar c:work>javac -d . *.java c:work>java events.EventManager store 八:结果
mysql> use hbs;
Database changed
mysql> show tables;
+----------------------+
| Tables_in_hbs |
+----------------------+
| events |
+----------------------+
1 row in set (0.00 sec) mysql> select * from events;
+-----------------+-----------------------------------+-----------------+
| EVENT_ID | EVENT_DATE | title |
+-----------------+-----------------------------------+-----------------+
| 1 | 2007-10-20 09:26:14 | My Event |
+-----------------+-----------------------------------+-----------------+
1 row in set (0.02 sec)
源码下载:http://www.hnzz3z.com:8103/zz3zcwb/work1.zip |
|