简体   繁体   English

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

[英]Minimal Hibernate 4 XML configuration with Spring 3 for annotation based transaction management and object mapping?

Assuming that I've already got a working Spring project what is the minimal amount of configuration needed to add in Hibernate 4 with Spring 3 XML configuration? 假设我已经有一个正常的Spring项目,那么在带有Spring 3 XML配置的Hibernate 4中添加所需的最少配置是多少? I want to use annotation based transaction management and have my objects mapped using annotations. 我想使用基于注释的事务管理,并使用注释映射我的对象。

Note: This is a self-answer Q&A style question intended to give a canonical answer to a common problem. 注意:这是一个自助式问答式问题,旨在为常见问题提供规范的答案。 I intend to expand this question over time to keep up to date with Hibernate. 我打算随着时间的推移扩展这个问题,以便与Hibernate保持同步。

I find using Hibernate with Spring's XML config fairly intuitive, but if you've never added it to a project before, it can be a pain to get working correctly. 我发现在Spring的XML配置中使用Hibernate相当直观,但是如果您以前从未将其添加到项目中,那么正确工作可能会很痛苦。 Using Spring's XML config is my preferred option for Hibernate 4. 对于Hibernate 4,我首选使用Spring的XML配置。

So you've set up a Spring project and got everything working. 因此,您已经建立了一个Spring项目并使所有工作正常进行。 You now want to add Hibernate. 您现在要添加休眠。

I always like to configure Hibernate in a separate XML file called something like database-servlet.xml inside your WEB-INF directory, but the name doesn't really matter as long as its on the classpath. 我一直喜欢在WEB-INF目录中的一个单独的XML文件(如database-servlet.xml配置Hibernate,但是名称实际上并不重要,只要它在类路径上即可。

My new database-servlet.xml looks like this: 我的新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>

You'll notice I imported the tx and jdbc Spring namespaces. 您会注意到我导入了txjdbc Spring名称空间。 This is because we are going to use them quite heavily in this config file. 这是因为我们将在此配置文件中大量使用它们。

First thing you want to do is enable annotation based transaction management ( @Transactional ). 您要做的第一件事是启用基于注释的事务管理( @Transactional )。 The main reason that people use Hibernate in Spring is because Spring will manage all your transactions for you. 人们在Spring中使用Hibernate的主要原因是因为Spring将为您管理所有事务。 Add the following line to your configuration file: 将以下行添加到您的配置文件:

<tx:annotation-driven />

We need to create a data source. 我们需要创建一个数据源。 The data source is basically the database that Hibernate is going to use to persist your objects. 数据源基本上是Hibernate用来持久化对象的数据库。 Generally one transaction manager will have one data source. 通常,一个事务管理器将具有一个数据源。 If you want Hibernate to talk to multiple data sources then you have multiple transaction managers. 如果您希望Hibernate与多个数据源对话,那么您将有多个事务管理器。

The type of data source will depend on what you want it to accomplish. 数据源的类型将取决于您想要完成的工作。 You can specify an existing database, or you can create a new in-memory HSQL/Derby/H2 database that comes prepackaged with Spring. 您可以指定一个现有数据库,也可以创建一个预包装在Spring中的新内存HSQL / Derby / H2数据库。 Personally I have an existing database that Hibernate connects to when I deploy my project for physical testing, but I use an in-memory database for unit/integration testing. 就个人而言,当我部署项目进行物理测试时,我有一个现有的Hibernate连接到的数据库,但是我使用内存数据库进行单元/集成测试。

I'll show how to create an in-memory database first. 我将首先展示如何创建一个内存数据库。

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

The above config, will create an embedded (in-memory) HSQL database as a bean, run the script setup.sql and then make the dataSource bean available to the application context. 上面的配置将创建一个嵌入式(内存中的)HSQL数据库作为Bean,运行脚本setup.sql ,然后使dataSource Bean可用于应用程序上下文。 You don't have to specify the database type as HSQL is the default, but I always like to be clear. 您不必指定数据库type因为默认是HSQL,但是我总是想清楚一点。 The setup.sql can be located anywhere within the classpath (WEB-INF directory usually). setup.sql可以位于类路径内的任何位置(通常为WEB-INF目录)。 You can specify as many SQL scripts as you like. 您可以根据需要指定任意数量的SQL脚本。 You can also set if they should be run on creation or destruction of the database. 您还可以设置是否在创建或销毁数据库时运行它们。

This database will live and die with your application. 该数据库将与您的应用程序一起生存和消失。 DO NOT USE AN EMBEDDED DATABASE ON A PRODUCTION PROJECT , one power outage, and all your data is gone. 请勿在生产项目上使用嵌入式数据库 ,一次停电,所有数据都将丢失。

To connect a data source to an existing database the configuration is slightly different. 要将数据源连接到现有数据库,配置略有不同。

<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>

The class of this bean can be anything that implements (I think) javax.sql.DataSource so you could write your own. 这个bean的类可以是任何实现(我认为) javax.sql.DataSource因此您可以编写自己的类。 This example class is provided by Spring, but doesn't have its own thread pool. 该示例类由Spring提供,但没有自己的线程池。 A popular alternative is the Apache Commons org.apache.commons.dbcp.BasicDataSource , but there are many others. 一个流行的替代方法是Apache Commons org.apache.commons.dbcp.BasicDataSource ,但还有许多其他选择。 I'll explain each of the properties below: 我将在下面解释每个属性:

  • driverClassName : The path to your JDBC driver. driverClassName :JDBC驱动程序的路径。 This is a database specific JAR that should be available on your classpath. 这是特定数据库的 JAR,应该在您的类路径中可用。 Ensure that you have the most up to date version. 确保您拥有最新版本。 If you are using an Oracle database, you'll need a OracleDriver. 如果您使用的是Oracle数据库,则需要一个OracleDriver。 If you have a MySQL database, you'll need a MySQLDriver. 如果您有MySQL数据库,则需要一个MySQLDriver。 See if you can find the driver you need here but a quick google should give you the correct driver. 看看是否可以在这里找到所需的驱动程序但是快速的Google应该会为您提供正确的驱动程序。

  • url : The URL to your database. url :数据库的URL。 Usually this will be something like jdbc\\:oracle\\:thin\\:\\path\\to\\your\\database or jdbc:mysql://path/to/your/database . 通常,这将类似于jdbc\\:oracle\\:thin\\:\\path\\to\\your\\databasejdbc:mysql://path/to/your/database If you google around for the default location of the database you are using, you should be able to find out what this should be. 如果您四处搜寻使用的数据库的默认位置,则应该能够找出应该使用的数据库的默认位置。 If you are getting a HibernateException with the message org.hibernate.HibernateException: Connection cannot be null when 'hibernate.dialect' not set and you are following this guide, there is a 90% chance that your URL is wrong, a 5% chance that your database isn't started and a 5% chance that your username/password is wrong. 如果您收到一条消息为org.hibernate.HibernateException: Connection cannot be null when 'hibernate.dialect' not setHibernateException 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 : The username to use when authenticating with the database. username :向数据库进行身份验证时使用的用户名。

  • password : The password to use when authenticating with the database. password :与数据库进行身份验证时使用的密码。

The next thing, is to set up the SessionFactory . 接下来,是设置SessionFactory This is the thing that Hibernate uses to create and manage your transactions, and actually talks to the database. Hibernate就是用来创建和管理事务,并实际上与数据库进行对话的。 It has quite a few configuration options that I will try to explain below. 它具有许多配置选项,我将在下面尝试解释。

<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 : Your data source bean. dataSource :您的数据源bean。 If you changed the Id of the dataSource, set it here. 如果您更改了数据源的ID,请在此处进行设置。

  • packagesToScan : The packages to scan to find your JPA annotated objects. packagesToScan :要扫描以查找您的JPA注释对象的软件包。 These are the objects that the session factory needs to manage, will generally be POJO's and annotated with @Entity . 这些是会话工厂需要管理的对象,通常是POJO的,并带有@Entity注释。 For more information on how to set up object relationships in Hibernate see here . 有关如何在Hibernate中设置对象关系的更多信息, 请参见此处

  • annotatedClasses (not shown): You can also provide a list of classes for Hibernate to scan if they are not all in the same package. annotatedClasses (未显示):您还可以提供一个类列表,供Hibernate扫描,如果它们不在同一包中,则可以进行扫描。 You should use either packagesToScan or annotatedClasses but not both. 您应该使用packagesToScanannotatedClasses但不能同时使用。 The declaration looks like this: 声明看起来像这样:

<property name="annotatedClasses">
    <list>
        <value>foo.bar.package.model.Person</value>
        <value>foo.bar.package.model.Thing</value>
    </list>
</property>
  • hibernateProperties : There are a myriad of these all lovingly documented here . hibernateProperties :这里有无数的文献记载 The main ones you will be using are as follows: 您将使用的主要方法如下:
    • hibernate.hbm2ddl.auto : One of the hottest Hibernate questions details this property. hibernate.hbm2ddl.auto :最热门的Hibernate问题之一详细介绍了此属性。 See it for more info . 有关更多信息,请参见它 I generally use validate, and set up my database using either SQL scripts (for an in-memory), or create the database beforehand (existing database). 我通常使用validate,并使用SQL脚本(用于内存)或预先创建数据库(现有数据库)来设置数据库。
    • hibernate.show_sql : Boolean flag, if true Hibernate will print all the SQL it generates to stdout . hibernate.show_sql :布尔型标志,如果为true,则Hibernate会将其生成的所有SQL打印到stdout You can also configure your logger to show you the values that are being bound to the queries by setting log4j.logger.org.hibernate.type=TRACE log4j.logger.org.hibernate.SQL=DEBUG in your log manager (I use log4j). 您还可以通过在日志管理器中设置log4j.logger.org.hibernate.type=TRACE log4j.logger.org.hibernate.SQL=DEBUG来配置记录器以显示绑定到查询的值(我使用log4j )。
    • hibernate.format_sql : Boolean flag, will cause Hibernate to pretty print your SQL to stdout. hibernate.format_sql :布尔标志,将使Hibernate将SQL漂亮地打印到stdout。
    • hibernate.dialect (Not shown, for good reason): A lot of old tutorials out there show you how to set the Hibernate dialect that it will use to communicate to your database. hibernate.dialect (出于充分的原因而未显示):那里的许多旧教程都向您展示了如何设置将用于与数据库通信的Hibernate方言。 Hibernate can auto-detect which dialect to use based on the JDBC driver that you are using. Hibernate 可以基于正在使用的JDBC驱动程序自动检测要使用的方言。 Since there are about 3 different Oracle dialects and 5 different MySQL dialects, I'd leave this decision up to Hibernate. 由于大约有3种不同的Oracle方言和5种不同的MySQL方言,因此我将决定权交给Hibernate。 For a full list of dialects Hibernate supports see here . 有关Hibernate支持的方言的完整列表, 请参见此处

The last 2 beans you need to declare are: 您需要声明的最后两个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>

The PersistenceExceptionTranslator translates database specific HibernateException or SQLExceptions into Spring exceptions that can be understood by the application context. PersistenceExceptionTranslator将数据库特定的HibernateExceptionSQLExceptions转换为应用程序上下文可以理解的Spring异常。

The TransactionManager bean is what controls the transactions as well as roll-backs. TransactionManager bean是控制事务以及回滚的对象。

Note: You should be autowiring your SessionFactory bean into your DAO's. 注意:您应该将SessionFactory bean自动装配到DAO中。

Once you've done this. 完成此操作后。 All you have to do is add your new database-servlet.xml to your web.xml file. 您所要做的就是将新的database-servlet.xml添加到web.xml文件中。

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

This should be all you need to actually get Hibernate working. 这应该是使Hibernate实际工作所需的全部。 You will still need to add annotations to your objects, and add @Transactional to your service layer methods that interact with the DAO. 您仍然需要在对象中添加注释,并在与DAO交互的服务层方法中添加@Transactional

Handy hint: If you are using a dependency manager like Ivy or maven, and you pull in all the Spring & Hibernate javadocs, you can actually view them in the STS XML editor by hovering over properties. 方便的提示:如果您使用的是Ivy或maven之类的依赖项管理器,并且引入了所有Spring&Hibernate javadocs,则可以通过将鼠标悬停在属性上来实际上在STS XML编辑器中查看它们。

How all this works in practice 所有这些在实践中如何运作

In your service class, when you annotate a method with @Transactional and then call that method from elsewhere a few things happen. 在服务类中,当使用@Transactional注释方法,然后从其他位置调用该方法时,会发生一些事情。 The Hibernate TransactionManager uses an AOP pointcut to inject code before the method gets invoked. Hibernate TransactionManager在调用方法之前使用AOP切入点插入代码。 This is where the TransactionManager will do the following things (in no particular order): 在这里, TransactionManager将执行以下操作(不按特定顺序):

  • Attempts to ascertain what persistent objects (that it knows about) are in memory. 尝试确定哪些持久性对象(它知道)在内存中。

  • Check the SessionFactory for an existing transactional session, and use the SessionFactory to create a new one if one doesn't exist, depending on your parameters inside the annotation. 检查SessionFactory是否存在现有的事务会话,如果不存在,则使用SessionFactory创建一个新的会话,具体取决于注释中的参数。

From this point the transaction manager records all the changes that you make to any persistent objects that it discovered, as well as any queries run through the current session. 从这一点开始,事务管理器记录您对发现的任何持久对象所做的所有更改,以及在当前会话中运行的所有查询。 It does this so that if an exception is thrown, its a simple matter to roll back everything that has occurred since the method was invoked. 这样做的目的是,如果引发异常,则回滚自调用该方法以来发生的所有事情都很简单。

The SessionFactory bean is responsible for creating, maintaining, closing and flushing all the database sessions that the TransactionManager asks it to create. SessionFactory bean负责创建,维护,关闭和刷新TransactionManager要求其创建的所有数据库会话。 That's why we autowire the SessionFactory into DAO's and make run all queries through it. 这就是为什么我们将SessionFactory自动连接到DAO并通过它运行所有查询的原因。

One of the biggest questions that new Hibernate users ask is "When do my changes get committed?" 新的Hibernate用户提出的最大问题之一是“我的更改何时提交?” and the answer makes sense when you think how the TransactionManager works with the SesisonFactory . 当您认为TransactionManager如何与SesisonFactory工作时,答案就很有意义。 Your database changes will be flushed and committed when you exit the service method that was annotated with @Transactional . 当您退出使用@Transactional注释的服务方法时,将刷新并提交数据库更改。 The reason for this is, that a transaction is supposed to represent a single 'unit' of unbroken work. 这样做的原因是,交易应该代表不间断工作的单个“单元”。 If something goes wrong with the unit, then it is assumed that the unit failed and all changes should be rolled back. 如果设备出现问题,则认为设备发生故障,所有更改都应回滚。 So the SessionFactory will flush and clear the session when you exit the service method that you called originally. 因此,当您退出最初调用的服务方法时, SessionFactory将刷新并清除会话。

That's not to say that it won't also flush and clear the session while your transaction is going on. 这并不是说在您进行交易时,它也不会刷新并清除会话。 For example, if I call a service method to add a collection of 5 objects and return the total count of objects in the database, the SessionFactory would realise that the query ( SELECT COUNT(*) ) requires an updated state to be accurate, and so would flush the addition of the 5 objects before running the count query. 例如,如果我调用一个服务方法以添加5个对象的集合并返回数据库中对象的总数,则SessionFactory将意识到查询( SELECT COUNT(*) )需要一个更新的状态才能准确,并且因此会在运行count查询之前刷新5个对象的添加。 The execution could look something like this: 执行可能看起来像这样:

//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();
}

To be clear, the DAO would have no session management code at all. 需要明确的是,DAO根本没有会话管理代码。 It would have something along the lines of sessionFactory.getCurrentSession().buildCriteria(Foo.class); 它将具有类似于sessionFactory.getCurrentSession().buildCriteria(Foo.class); and that's it. 就是这样。 No manipulating instances of a Session object, no calls to flush() or clear() . 没有操纵Session对象的实例,没有对flush()clear()调用。 That's the beauty of using Hibernate with Spring. 这就是在Spring中使用Hibernate的好处。

DISCLAIMER: I DO NOT KNOW IF THESE EXAMPLES WILL WORK WITH STANDALONE HIBERNATE 免责声明:我不知道这些示例是否适用于斯坦纳德·希尔伯奈特

I am not in any way affiliated with Hibernate or the Hibernate dev team. 我与Hibernate或Hibernate开发团队没有任何关系。 I'm providing these examples so I have a reference to point to when I'm answering questions on the Hibernate tag. 我提供这些示例,以便在回答Hibernate标签上的问题时提供参考。 These examples and discussions are my based on my own opinion as well as how I develop my applications using Hibernate. 这些示例和讨论是基于我自己的观点以及我如何使用Hibernate开发应用程序的。 These examples are in no way comprehensive. 这些例子绝不是全面的。 I'm basing them on the common situations I've used Hibernate for in the past. 我将基于过去使用Hibernate的常见情况为基础。

If you encounter issues trying to implement these examples, do not comment and expect me to fix your problem. 如果您在尝试实现这些示例时遇到问题,请不要发表评论,并希望我能解决您的问题。 Part of learning Hibernate is learning the in's and out 's of its API. 学习Hibernate的一部分是学习其API的内容。 If there is a mistake with the examples, please feel free to edit them. 如果示例有误,请随时对其进行编辑。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM