使用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
如下所示:
您会注意到我导入了tx
和jdbc
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\database
或jdbc: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扫描。 您应该使用
packagesToScan
或annotatedClasses
但不能同时使用两者。 声明如下:
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
将特定于数据库的HibernateException
或SQLExceptions
转换为可由应用程序上下文理解的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。 如果示例有错误,请随时编辑它们。