第二章开启Hibernate项目--使用xml文档配置Hibernate项目

第二章:Hibernate的应用举例

本章使用Eclipse创建一个Maven项目的基础上,提供Hibernate的实际应用参考,在开始本章前,您应当安装了Eclipse,并在Eclipse上配置了Maven插件。按照如下步骤,您可以实现您的第一个Hibernate项目。

如果您没有Maven插件,那么创建一个普通的java项目,之后将提到的jar文件引入java路径即可。

2.1 使用XML形式配置Hibernate

本节将讨论使用XML文档的形式配置Hibernate,在下一小节讨论使用Annotation形式,最后讨论使用JAVAEE形式。

2.1.1 创建域模型(domain model)

域模型是指JAVA Model,即实体类。如下所示:

package org.example.ch02.hello;

 

public class Message {

 

    private Long id;

    private String text;

    private Message nextMessage;

   

    public Long getId() {

       return id;

    }

   

    @SuppressWarnings("unused")

    private void setId(Long id) {

       this.id = id;

    }

    public String getText() {

       return text;

    }

    public void setText(String text) {

       this.text = text;

    }

    public Message getNextMessage() {

       return nextMessage;

    }

    public void setNextMessage(Message nextMessage) {

       this.nextMessage = nextMessage;

    }

   

}

我们创建了Message类,设置了id属性,需要注意的是,根据上一章的理论,id应当是一个代理键(surrogate key),代理键与Message的内容无直接关系,由数据库系统自动生成,因此,将主键id的set方法设置为private。Message类的text属性,表示当前消息, nextMessage属性,表示下一条消息。从结构上看,多个Message不仅可以构成链式结构,而且,同一个Message可以是多个其它Message的直接后继消息,因此,可以构成比链式结构更复杂的结构。

2.1.2 创建Hibernate映射

通常,一个域模型对应一个Hibernate映射文件,域模型与其映射文件(.hbm.xml)放在同一个目录下。下面对Message类和数据库中的表进行映射,映射文件(Message.hbm.xml)描述如下:

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC

        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

 

<hibernate-mapping package="org.hibernate.tutorial.domain">

    <class name="org.example.ch02.hello.Message" table="MESSAGE">

       <id name="id"

       column="MESSAGE_ID">

           <generator class="increment"/>

       </id>

      

       <property name="text" column="MESSAGE_TEXT"/>

       <many-to-one name="nextMessage"

           cascade="all"

           column="NEXT_MESSAGE_ID"

           foreign-key="FK_NEXT_MESSAGE"/>

    </class>

</hibernate-mapping>

映射文件的结构可以从Hibernate帮助文档中找到,这里Message类映射为MESSAGE表,并且分别将id属性映射为MESSAGE_ID字段,text属性映射为MESSAGE_TEXT字段,nextMessage属性比较复杂,使用了many-to-one关系,在MESSAGE表中使用NEXT_MESSAGE_ID字段表示,并创建了FK_NEXT_MESSAGE外键约束。需要注意的是,id指定了生成方式为递增,起始id为0.

2.1.3  Hibernate与数据库间的配置

Hibernate与数据库间的配置主要有三种方式,分别是使用property文件(即.properties),使用xml文件(即.cfg.xml)和使用代码配置,通常使用xml的形式配置。(Hibernate.cfg.xml)描述如下:

<?xml version='1.0' encoding='utf-8'?>

<!DOCTYPE hibernate-configuration PUBLIC

        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"

        "http://www.hibernate.org/dtd/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:data/message</property>

        <property name="connection.username">sa</property>

        <property name="connection.password"></property>

 

        <!-- SQL dialect -->

        <property name="dialect">org.hibernate.dialect.HSQLDialect</property>

 

        <!-- Enable Hibernate's automatic session context management -->

      

       <property name="c3p0.min_size">5</property>

       <property name="c3p0.max_size">20</property>

       <property name="c3p0.timeout">300</property>

       <property name="c3p0.max_statements">50</property>

       <property name="c3p0.idle_test_period">3000</property>

 

        <!-- Echo all executed SQL to stdout -->

        <property name="show_sql">true</property>

       <property name="format_sql">true</property>

 

        <!-- Drop and re-create the database schema on startup -->

        <property name="hbm2ddl.auto">update</property>

 

        <mapping resource="org/example/ch02/hello/Message.hbm.xml"/>

 

    </session-factory>

</hibernate-configuration>

 

文档开始配置数据库相关的信息,包括数据库驱动程序,数据库访问地址,数据库用户名和密码等,hsqldb是内嵌数据库,因此在无需单独安装和配置数据库的前提下,即可运行程序。dialect(方言)是指Hibernate产生哪种类型的sql与配置数据库交互,Hibernate是独立于其它数据库,由于数据库厂商间存在sql访问方式的差异,因此,需要选择特定的方言支配具体厂商和版本的数据库。

之后配置了c3p0数据库连接池。Hibernate自身没有提供对数据库连接池的应用版本的实现,因此,Hibernate常使用第三方的数据库连接池。c3p0是Hibernate最常使用的数据库连接池。实际上,如果不配置数据库连接池,那么Hibernate将提供一个简单的单线程的连接方式(

        <!-- JDBC connection pool (use the built-in) -->

       <!--  <property name="connection.pool_size">1</property> -->

),这种方式仅在测试阶段使用,不能够满足实际应用的需求。

连接池配置中:min_size是连接池中最小线程数目,max_size是最大线程数目。当数据库连接请求到来时,如果当前有空闲的线程,就直接利用空闲线程处理请求,如果当前无空闲线程,需要创建新线程处理当前请求,如果当前使用的线程数已经达到max_size,那么连接池将会拒绝新请求。创建和销毁线程的代价比较大,因此,能够重用线程是最好的选择(这个在java多线程中很常见),timeout指定了一个线程过期的时间(按分钟计算,当一个连接任务释放后,执行线程将被回收,暂时缓存在线程池内,当超过timeout时间后仍未被使用,并且线程池内的线程数目大于min_size时,这个线程将被释放)。max_statements表示最多缓存的statements的数目,创建statements的代价比较大,因此缓存部分statements有利于性能的优化。idle_test_period表示一个连接在指定时间内不被使用将变为自动无效。连接池如下图所示:

第二章开启Hibernate项目--使用xml文档配置Hibernate项目

配置文件之后配置了Hibernate生成sql语句的输出形式,这些sql语句对性能调节和错误定位有重要作用。

hbm2dll.auto是Hibernate的工具,配置其用于生成相应的数据表。

最后指明了映射文件的位置。

2.1.4 Hibernate的SessionFactory

一般认为一个SessionFactory对应于一个数据库,SessionFactory是线程安全的,可以被多线程共享。创建一个SessionFactory的代价较大,因此,常见的情况是在应用程序开始时创建SessionFactory,其生命周期经历整个应用程序运行过程。一般情况下,一个应用程序需要控制多个数据库时需要创建多个SessionFactory。

package org.example.ch02.Control;

 

import org.hibernate.SessionFactory;

import org.hibernate.cfg.Configuration;

 

public class HibernateUtil {

 

    private static SessionFactory sessionFactory;

   

    static

    {

       sessionFactory = new Configuration().configure().buildSessionFactory();

    }

 

    public static SessionFactory getSessionFactory() {

       return sessionFactory;

    }

   

   

    public static void shutDown()

    {

       sessionFactory.close();

    }

   

}

Configuration在创建时,会寻找根目录下的hibernate.properties文件,分析Hibernate的配置属性,如果没有配置这个文件,则调用configure方法寻找Hibernate.cfg.xml配置文件,可以通过参数传入配置文件的位置,默认是在根目录下。如果找不到Hibernate.cfg.xml Hibernate将报错。使用buildSessionFactory方法创建SessionFactory。

现在可以理解,Hibernate通过Configuration的configure方法分析Hibernate.cfg.xml文件,获取数据库的连接信息、连接池的配置信息、映射文件的位置,之后分析处理对象和数据库的映射关系。

2.5 Hibernate的存储与查询

测试代码如下:

package org.example.ch02.hello;

 

import java.util.List;

 

import org.example.ch02.Control.HibernateUtil;

import org.hibernate.Session;

import org.junit.Test;

 

public class MessageTest {

    @Test

    public void hibernateTest()

    {

       Message message = new Message();

       message.setText("hello world");

       Message message2 = new Message();

       message2.setText("good day!");

       message.setNextMessage(message2);

      

       Session session = HibernateUtil.getSessionFactory().openSession();

       session.beginTransaction().begin();

       session.save(message);

      

       session.getTransaction().commit();

       session.close();

      

       Session session2 = HibernateUtil.getSessionFactory().openSession();

       session2.beginTransaction().begin();

       @SuppressWarnings("unchecked")

       List<Message> ms = session2.createQuery("from Message m").list();

       for(Message m:ms)

       {

           System.out.println(m.getText());

       }     

       session2.getTransaction().commit();

       session2.close();

   

    }

   

}

通过SessionFactory的openSession方法,可以创建并获取一个Session。Session不是线程安全的,一般一个请求过程对应一个Session。Session的创建和销毁代价较小。通过Session可以获得Transaction。上例中先存储了两个对象,之后使用查询方法查询出相应的对象。

 

这里涉及到一个叫方法链(method chaining)的概念:方法链是一种编程模式,通过在set或者save方法(普通的set和save方法返回值是void)返回传入对象,因此可以创建一个方法调用的链式结构。例如session2.createQuery("from Message m").list(); 这种方法在Java中并不提倡,因为其在调试等方面存在缺陷。

2.5 运行库配置

使用maven描述文件(pom.xml)描述如下:

<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.example.hibernate.ch02</groupId>

  <artifactId>HelloHibernate</artifactId>

  <version>0.0.1-SNAPSHOT</version>

  <dependencies>

    <dependency>

        <groupId>log4j</groupId>

        <artifactId>log4j</artifactId>

        <version>1.2.14</version>

        <type>jar</type>

        <scope>compile</scope>

    </dependency>

    <dependency>

        <groupId>org.slf4j</groupId>

        <artifactId>slf4j-log4j12</artifactId>

        <version>1.5.8</version>

        <type>jar</type>

        <scope>compile</scope>

    </dependency>

    <dependency>

        <groupId>junit</groupId>

        <artifactId>junit</artifactId>

        <version>4.10</version>

        <type>jar</type>

        <scope>compile</scope>

    </dependency>

        <dependency>

        <groupId>hsqldb</groupId>

        <artifactId>hsqldb</artifactId>

        <version>1.8.0.10</version>

        <type>jar</type>

        <scope>compile</scope>

    </dependency>

    <dependency>

        <groupId>javassist</groupId>

        <artifactId>javassist</artifactId>

        <version>3.12.1.GA</version>

        <type>jar</type>

        <scope>compile</scope>

    </dependency>

 

    <dependency>

        <groupId>org.hibernate</groupId>

        <artifactId>hibernate-annotations</artifactId>

        <version>3.5.6-Final</version>

        <type>jar</type>

        <scope>compile</scope>

    </dependency>

    <dependency>

        <groupId>org.hibernate</groupId>

        <artifactId>hibernate-commons-annotations</artifactId>

        <version>3.2.0.Final</version>

        <type>jar</type>

        <scope>compile</scope>

    </dependency>

   

    <dependency>

        <groupId>org.hibernate</groupId>

        <artifactId>hibernate-c3p0</artifactId>

        <version>3.6.10.Final</version>

        <type>jar</type>

        <scope>compile</scope>

    </dependency>

  </dependencies>

</project>

在配置文件中使用了log4j和slf4j,用于Hibernate的日志输出,javassisit为Hibernate选择字节码类库,hsqldb是内嵌的数据库,hibernate的commons-annotation、annotation是hibernate需要使用的类库,c3p0是连接池。

之后是日志配置(log4j.properties):

log4j.appender.stdout=org.apache.log4j.ConsoleAppender

log4j.appender.stdout.Target=System.out

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

 

 

log4j.rootLogger=warn, stdout

 

log4j.logger.org.hibernate.tool.hbm2ddl=debug

 

log4j.logger.org.hibernate=INFO

log4j.logger.org.hibernate.type=INFO

如果您没有maven,那么可以将上述的相应库下载后添加到java路径下。

2.6 执行

输出结果:

12:15:09,421  INFO Environment:570 - Hibernate 3.5.6-Final

12:15:09,437  INFO Environment:603 - hibernate.properties not found

12:15:09,437  INFO Environment:781 - Bytecode provider name : javassist

12:15:09,453  INFO Environment:662 - using JDK 1.4 java.sql.Timestamp handling

12:15:09,765  INFO Configuration:1518 - configuring from resource: /hibernate.cfg.xml

12:15:09,765  INFO Configuration:1495 - Configuration resource: /hibernate.cfg.xml

12:15:14,437  INFO Configuration:655 - Reading mappings from resource : org/example/ch02/hello/Message.hbm.xml

12:15:17,531  INFO HbmBinder:348 - Mapping class: org.example.ch02.hello.Message -> MESSAGE

12:15:17,687  INFO Configuration:1633 - Configured SessionFactory: null

12:15:17,703  INFO ConnectionProviderFactory:173 - Initializing connection provider: org.hibernate.connection.C3P0ConnectionProvider

12:15:17,718  INFO C3P0ConnectionProvider:103 - C3P0 using driver: org.hsqldb.jdbcDriver at URL: jdbc:hsqldb:data/message

12:15:17,718  INFO C3P0ConnectionProvider:104 - Connection properties: {user=sa, password=****}

12:15:19,750 DEBUG SchemaUpdate:203 - create table MESSAGE (MESSAGE_ID bigint not null, MESSAGE_TEXT varchar(255), NEXT_MESSAGE_ID bigint, primary key (MESSAGE_ID))

12:15:19,750 DEBUG SchemaUpdate:203 - alter table MESSAGE add constraint FK_NEXT_MESSAGE foreign key (NEXT_MESSAGE_ID) references MESSAGE

12:15:19,750  INFO SchemaUpdate:217 - schema update complete

Hibernate:

    select

        max(MESSAGE_ID)

    from

        MESSAGE

Hibernate:

    insert

    into

        MESSAGE

        (MESSAGE_TEXT, NEXT_MESSAGE_ID, MESSAGE_ID)

    values

        (?, ?, ?)

Hibernate:

    insert

    into

        MESSAGE

        (MESSAGE_TEXT, NEXT_MESSAGE_ID, MESSAGE_ID)

    values

        (?, ?, ?)

Hibernate:

    select

        message0_.MESSAGE_ID as MESSAGE1_0_,

        message0_.MESSAGE_TEXT as MESSAGE2_0_,

        message0_.NEXT_MESSAGE_ID as NEXT3_0_

    from

        MESSAGE message0_

hello world

good day

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值