使用Spring 3进行基于注释的事务管理和对象映射的最小Hibernate 4 XML配置?

时间:2014-06-26 04:59:39

标签: java spring hibernate jpa

假设我已经有了一个有效的Spring项目,那么使用Spring 3 XML配置添加Hibernate 4所需的最小配置量是多少?我想使用基于注释的事务管理,并使用注释映射我的对象。

注意:这是一个自我回答的Q& A风格问题,旨在为常见问题提供规范答案。我打算随着时间的推移扩展这个问题,以便与Hibernate保持同步。

1 个答案:

答案 0 :(得分:5)

我发现使用Hibernate和Spring的XML配置非常直观,但如果你以前从未将它添加到项目中,那么正常工作可能会很痛苦。使用Spring的XML配置是Hibernate 4的首选选项。

所以你已经建立了一个Spring项目,并且一切正常。您现在想要添加Hibernate。

我总是喜欢在一个单独的XML文件中配置Hibernate,这个文件在你的WEB-INF目录中被称为database-servlet.xml,但是只要它在类路径上,这个名字并不重要。

我的新database-servlet.xml看起来像这样:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">

</beans>

您会注意到我导入了txjdbc Spring命名空间。这是因为我们将在此配置文件中大量使用它们。

您要做的第一件事是启用基于注释的事务管理(@Transactional)。人们在Spring中使用Hibernate的主要原因是Spring会为您管理所有事务。将以下行添加到配置文件中:

<tx:annotation-driven />

我们需要创建一个数据源。数据源基本上是Hibernate用来持久保存对象的数据库。通常,一个事务管理器将具有一个数据源。如果您希望Hibernate与多个数据源通信,那么您就拥有多个事务管理器。

数据源的类型取决于您希望它完成的任务。您可以指定现有数据库,也可以创建一个新的内存中HSQL / Derby / H2数据库,该数据库预先打包了Spring。就个人而言,当我部署项目进行物理测试时,我有一个Hibernate连接的现有数据库,但我使用内存数据库进行单元/集成测试。

我将首先介绍如何创建内存数据库。

<jdbc:embedded-database id="dataSource" type="HSQL">
    <jdbc:script location="classpath:/setup.sql" />
    .
    .
    .
    <!-- As many scripts can run as you like -->
</jdbc:embedded-database>

上面的配置将创建一个嵌入式(内存中)HSQL数据库作为bean,运行脚本setup.sql,然后使dataSource bean可用于应用程序上下文。您不必指定数据库type,因为HSQL是默认值,但我总是希望明确。 setup.sql可以位于类路径中的任何位置(通常是WEB-INF目录)。您可以根据需要指定任意数量的SQL脚本。您还可以设置是否应在创建或销毁数据库时运行它们。

此数据库将随您的应用程序一起生存和死亡。 请勿在生产项目中使用嵌入式数据库,停电一次,所有数据都已消失。

要将数据源连接到现有数据库,配置略有不同。

<bean id="dataSource" 
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="" />
    <property name="url" value="" />
    <property name="username" value="" />
    <property name="password" value="" />
</bean>

这个bean的类可以是实现(我认为)javax.sql.DataSource的任何东西,所以你可以自己编写。此示例类由Spring提供,但没有自己的线程池。一个流行的替代方案是Apache Commons org.apache.commons.dbcp.BasicDataSource,但还有很多其他的。我将解释以下每个属性:

  • driverClassName :JDBC驱动程序的路径。这是一个特定于数据库的 JAR,应该可以在类路径中使用。确保您拥有最新版本。如果您使用的是Oracle数据库,则需要OracleDriver。如果你有一个MySQL数据库,那么你需要一个MySQLDriver。看看您是否能找到所需的驱动程序here,但快速谷歌应该会为您提供正确的驱动程序。

  • url :数据库的URL。通常这会是jdbc\:oracle\:thin\:\path\to\your\databasejdbc:mysql://path/to/your/database。如果你四处寻找你正在使用的数据库的默认位置,你应该能够找到它应该是什么。如果您收到包含HibernateException消息的org.hibernate.HibernateException: Connection cannot be null when 'hibernate.dialect' not set并且您正在遵循本指南,那么您的网址错误的可能性为90%,您的数据库有5%的可能性未启动并且有5%的可能性是您的用户名/密码错误。

  • 用户名:使用数据库进行身份验证时使用的用户名。

  • 密码:使用数据库进行身份验证时使用的密码。

接下来,就是设置SessionFactory。这是Hibernate用来创建和管理事务,并实际与数据库进行对话的事情。它有很多配置选项,我将在下面解释。

<bean id="sessionFactory"
    class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="packagesToScan" value="au.com.project />
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.use_sql_comments">true</prop>
            <prop key="hibernate.hbm2ddl.auto">validate</prop>
        </props>
    </property>
</bean>
  • dataSource :您的数据源bean。如果您更改了dataSource的Id,请在此处进行设置。

  • packagesToScan :要扫描的包以查找JPA注释对象。这些是会话工厂需要管理的对象,通常是POJO并用@Entity注释。有关如何在Hibernate see here中设置对象关系的更多信息。

  • annotatedClasses (未显示):如果Hibernate不在同一个包中,您还可以提供一个类列表供Hibernate扫描。您应该使用packagesToScanannotatedClasses,但不能同时使用两者。声明如下:

<property name="annotatedClasses">
    <list>
        <value>foo.bar.package.model.Person</value>
        <value>foo.bar.package.model.Thing</value>
    </list>
</property>
  • hibernateProperties :这些都是无数的documented here。您将使用的主要内容如下:
    • hibernate.hbm2ddl.auto :最热门的Hibernate问题之一详述了这个属性。 See it for more info。我通常使用validate,并使用SQL脚本(对于内存)设置我的数据库,或者事先创建数据库(现有数据库)。
    • hibernate.show_sql :布尔标志,如果为true Hibernate会将它生成的所有SQL打印到stdout。您还可以通过在日志管理器中设置log4j.logger.org.hibernate.type=TRACE log4j.logger.org.hibernate.SQL=DEBUG来配置记录器,以显示绑定到查询的值(我使用log4j)。
    • hibernate.format_sql :布尔标志,将导致Hibernate将SQL打印到stdout。
    • hibernate.dialect (未显示,有充分理由):很多旧的教程向您展示了如何设置用于与数据库通信的Hibernate方言。 Hibernate 可以根据您使用的JDBC驱动程序自动检测要使用的方言。由于有大约3种不同的Oracle方言和5种不同的MySQL方言,我将这个决定留给了Hibernate。有关方言的完整列表,Hibernate支持see here

您需要声明的最后两个bean是:

<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"
    id="PersistenceExceptionTranslator" />

<bean id="transactionManager" 
    class="org.springframework.orm.hibernate4.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

PersistenceExceptionTranslator将特定于数据库的HibernateExceptionSQLExceptions转换为可由应用程序上下文理解的Spring异常。

TransactionManager bean控制事务和回滚。

注意:您应该将SessionFactory bean自动装入DAO中。

一旦你完成了这个。您所要做的就是将新的database-servlet.xml添加到您的web.xml文件中。

<context-param>
<param-name>contextConfigLocation</param-name>
    <param-value>
        /WEB-INF/database-servlet.xml
        .
        .
        .
    </param-value>
</context-param>

这应该是实际让Hibernate工作所需的全部内容。您仍需要向对象添加注释,并将@Transactional添加到与DAO交互的服务层方法中。

方便提示:如果你正在使用像Ivy或maven这样的依赖管理器,那么你可以使用所有的Spring&amp; Hibernate javadocs,您实际上可以通过将鼠标悬停在属性上来在STS XML编辑器中查看它们。

这一切在实践中如何运作

在您的服务类中,当您使用@Transactional注释方法然后从其他地方调用该方法时,会发生一些事情。 Hibernate TransactionManager使用AOP切入点在调用方法之前注入代码。这是TransactionManager将执行以下操作的地方(无特定顺序):

  • 尝试确定哪些持久对象(它知道的)在内存中。

  • 检查SessionFactory是否有现有的交易会话,如果不存在,请使用SessionFactory创建一个新的,具体取决于注释中的参数。< / p>

从这一点开始,事务管理器会记录您对发现的任何持久对象所做的所有更改,以及通过当前会话运行的所有查询。它这样做是为了在抛出异常的情况下,简单地回滚自调用该方法以来发生的所有事情。

SessionFactory bean负责创建,维护,关闭和刷新TransactionManager要求创建的所有数据库会话。这就是为什么我们将SessionFactory自动装入DAO并通过它运行所有查询的原因。

新Hibernate用户提出的最大问题之一是#34;我的更改何时会被提交?&#34;当您考虑TransactionManager如何与SesisonFactory一起使用时,答案才有意义。退出使用@Transactional注释的服务方法时,将刷新并提交您的数据库更改。这样做的原因是,交易应该代表一个单一的单位&#39;不间断的工作。如果设备出现问题,则假设设备发生故障并且所有更改都应该回滚。因此,当您退出最初调用的服务方法时,SessionFactory将刷新并清除会话。

这并不是说它在您的交易正在进行时也不会刷新并清除会话。例如,如果我调用服务方法添加5个对象的集合并返回数据库中对象的总数,SessionFactory将意识到查询(SELECT COUNT(*))需要更新状态到准确,因此在运行计数查询之前刷新5个对象的添加。执行可能看起来像这样:

//Service
@Override
@Transactional
public long saveAndCount(List<Foo> listOfFoo){
    for(Foo foo : listOfFoo){
        //Doesn't get instantly saved to the database.
        fooDAO.saveOrUpdate(foo);
    }
    /*
     * Before the actual SELECT COUNT(*) query was run, the SessionFactory would
     * flush the save operation of the 5 Foo objects.
     */
    return fooDAO.count();
}

要清楚,DAO根本没有会话管理代码。它会有sessionFactory.getCurrentSession().buildCriteria(Foo.class);之类的东西。没有操纵Session对象的实例,也没有调用flush()clear()。这就是在Spring中使用Hibernate的美妙之处。

<子>声明: 我不知道这些例子是否适用于STANDALONE HIBERNATE

我与Hibernate或Hibernate开发团队没有任何关系。我提供了这些示例,因此我可以参考指出我何时回答有关Hibernate标记的问题。这些示例和讨论是基于我自己的观点以及如何使用Hibernate开发应用程序。这些例子绝不是全面的。我基于我过去使用过Hibernate的常见情况。

如果您在尝试实施这些示例时遇到问题,请不要发表评论并希望我解决您的问题。学习Hibernate的部分原因是学习和学习 其API的API。如果示例有错误,请随时编辑。