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

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

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

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

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

我总是喜欢在一个单独的XML文件中配置Hibernate,这个文件叫做WEB-INF目录中的database-servlet.xml ,但只要它在类路径上,名称就没关系了。

我的新database-servlet.xml如下所示:

    

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

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

  

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

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

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

   . . .   

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

该数据库将随您的应用程序一起生存和死亡。 不要在生产项目中使用嵌入式数据库 ,停电一次,所有数据都不见了。

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

       

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

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

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

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

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

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

     true validate    
  • dataSource :您的数据源bean。 如果您更改了dataSource的Id,请在此处进行设置。

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

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

   foo.bar.package.model.Person foo.bar.package.model.Thing   
  • hibernateProperties :这里有无数的精心记录 。 您将使用的主要内容如下:
    • hibernate.hbm2ddl.auto :最热门的Hibernate问题之一详述了这个属性。 查看更多信息 。 我通常使用validate,并使用SQL脚本(对于内存)设置我的数据库,或者事先创建数据库(现有数据库)。
    • hibernate.show_sql :Boolean flag,如果为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支持的完整方言列表, 请参阅此处 。

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

     

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

TransactionManager bean控制事务和回滚。

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

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

  contextConfigLocation  /WEB-INF/database-servlet.xml . . .   

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

方便提示:如果你正在使用像Ivy或maven这样的依赖管理器,并且你引入了所有的Spring和Hibernate javadoc,你可以通过将鼠标hover在属性上来实际在STS XML编辑器中查看它们。

这一切在实践中如何运作

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

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

  • 检查SessionFactory以查找现有事务会话,并根据注释中的参数使用SessionFactory创建一个新的事务会话(如果不存在)。

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

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

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

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

 //Service @Override @Transactional public long saveAndCount(List 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。 如果示例有错误,请随时编辑它们。