Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
432 views
in Technique[技术] by (71.8m points)

java - 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? 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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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. Using Spring's XML config is my preferred option for Hibernate 4.

So you've set up a Spring project and got everything working. 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.

My new database-servlet.xml looks like this:

<?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. 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). The main reason that people use Hibernate in Spring is because Spring will manage all your transactions for you. 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. 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.

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

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. You don't have to specify the database type as HSQL is the default, but I always like to be clear. The setup.sql can be located anywhere within the classpath (WEB-INF directory usually). You can specify as many SQL scripts as you like. 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. This example class is provided by Spring, but doesn't have its own thread pool. A popular alternative is the Apache Commons org.apache.commons.dbcp.BasicDataSource, but there are many others. I'll explain each of the properties below:

  • driverClassName: The path to your JDBC driver. This is a database specific JAR that should be available on your classpath. Ensure that you have the most up to date version. If you are using an Oracle database, you'll need a OracleDriver. If you have a MySQL database, you'll need a MySQLDriver. See if you can find the driver you need here but a quick google should give you the correct driver.

  • url: The URL to your database. Usually this will be something like jdbc:oracle:thin:pathoyourdatabase or jdbc: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.

  • username: The username to use when authenticating with the database.

  • password: The password to use when authenticating with the database.

The next thing, is to set up the SessionFactory. This is the thing that Hibernate uses to create and manage your transactions, and actually talks to the database. 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. If you changed the Id of the dataSource, set it here.

  • packagesToScan: The packages to scan to find your JPA annotated objects. These are the objects that the session factory needs to manage, will generally be POJO's and annotated with @Entity. For more information on how to set up object relationships in Hibernate see here.

  • annotatedClasses (not shown): You can also provide a list of classes for Hibernate to scan if they are not all in the same package. You should use either packagesToScan or annotatedClasses but not both. 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. The main ones you will be using are as follows:
    • hibernate.hbm2ddl.auto: One of the hottest Hibernate questions details this property. 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).
    • hibernate.show_sql: Boolean flag, if true Hibernate will print all the SQL it generates to 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).
    • hibernate.format_sql: Boolean flag, will cause Hibernate to pretty print your SQL to 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 can auto-detect which dialect to use based on the JDBC driver that you are using. Since there are about 3 different Oracle dialects and 5 different MySQL dialects, I'd leave this decision up to Hibernate. For a full list of dialects Hibernate supports see here.

The last 2 beans you need to declare are:

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

The TransactionManager bean is what controls the transactions as well as roll-backs.

Note: You should be autowiring your SessionFactory bean into your DAO's.

Once you've done this. All you have to do is add your new database-servlet.xml to your web.xml file.

<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. You will still need to add annotations to your objects, and add @Transactional to your service layer methods that interact with the DAO.

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.

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. The Hibernate TransactionManager uses an AOP pointcut to inject code before the method gets invoked. This is where the TransactionManager will do the following things (in no particular order):

  • Attempts to ascertain what persistent objects (that it knows about) are in


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...