天天看点

【Spring】Spring Framework Reference Documentation中文版16

20. Object Relational Mapping (ORM) Data Access

对象关系映射的数据访问

20.1 Introduction to ORM with Spring

介绍spring的ORM

The Spring Framework supports integration with Hibernate, Java Persistence API (JPA) and Java Data Objects (JDO) for resource management, data access object (DAO) implementations, and transaction strategies. For example, for Hibernate there is first-class support with several convenient IoC features that address many typical Hibernate integration issues. You can configure all of the supported features for O/R (object relational) mapping tools through Dependency Injection. They can participate in Spring’s resource and transaction management, and they comply with Spring’s generic transaction and DAO exception hierarchies. The recommended integration style is to code DAOs against plain Hibernate, JPA, and JDO APIs. The older style of using Spring’s DAO templates is no longer recommended; however, coverage of this style can be found in the Section 39.1, “Classic ORM usage” in the appendices.

spring框架支持和Hibernate的集成、JPA和Java数据Object对于资源的管理,数据访问object的实现和事务策略。例如,对于Hibernate首先支持一些方便的IOC特性对于许多类型的Hibernate集成问题。你可以配置所有对于OR的支持映射工具通过依赖注入。他们可以参与到spring的资源、事务管理和配合spring的通常事务、DAO异常的结构。推荐的集成风格对于DAO是普通的Hibernate、JPA和JDO的API。以前的方式是使用spring的DAO模板但是现在已经不推荐了,然而,这样的说明也可以在39.1节中找到,“经典的ORM的使用”,在附录中。

Spring adds significant enhancements to the ORM layer of your choice when you create data access applications. You can leverage as much of the integration support as you wish, and you should compare this integration effort with the cost and risk of building a similar infrastructure in-house. You can use much of the ORM support as you would a library, regardless of technology, because everything is designed as a set of reusable JavaBeans. ORM in a Spring IoC container facilitates configuration and deployment. Thus most examples in this section show configuration inside a Spring container.

spring添加了有效的加强层对于ORM层对于你的选择当你创建数据访问应用时。你可以利用大部分的集成根据你的需要,并且你应当比较这些集成对于消耗的影响和建造一个相似的内部结构的影响。你可以使用ORM支持作为一个库而忽略技术,因为所有的事情被设计作为一系列重用的JavaBean。ORM是一个spring的IOC容器便于配置和开发。在这一节中的大部分例子展示了如果在spring的容器中配置。

Benefits of using the Spring Framework to create your ORM DAOs include:

使用spring框架来创建你的ORM DAO的好处包括:

    Easier testing. Spring’s IoC approach makes it easy to swap the implementations and configuration locations of Hibernate SessionFactory instances, JDBC DataSource instances, transaction managers, and mapped object implementations (if needed). This in turn makes it much easier to test each piece of persistence-related code in isolation.

易于测试。spring的ioc方式使得可以简单的实现和配置Hibernate的SessionFactory的实例的位置,JDBC数据源实例,事务管理和映射object的实现(如果需要)。这是为了方便测试每一部分相关的持久化关系代码。

    Common data access exceptions. Spring can wrap exceptions from your ORM tool, converting them from proprietary (potentially checked) exceptions to a common runtime DataAccessException hierarchy. This feature allows you to handle most persistence exceptions, which are non-recoverable, only in the appropriate layers, without annoying boilerplate catches, throws, and exception declarations. You can still trap and handle exceptions as necessary. Remember that JDBC exceptions (including DB-specific dialects) are also converted to the same hierarchy, meaning that you can perform some operations with JDBC within a consistent programming model.

通用的数据访问异常。spring可以处理来自ORM工具的异常,将他们转换为普通的运行时DataAccessException结构。这个特性允许你来处理大部分持久化异常,有些是不可以恢复,在一些相关的层,不需要复杂的异常捕获、抛出和异常声明。你依然可以包装和处理异常如果是有必要的。记住JDBC异常(包括特定的数据库方言)依然可以被转换为相同的结构,意味着你可以执行一些操作通过一致的编程模型来操作JDBC。

    General resource management. Spring application contexts can handle the location and configuration of Hibernate SessionFactory instances, JPA EntityManagerFactory instances, JDBC DataSource instances, and other related resources. This makes these values easy to manage and change. Spring offers efficient, easy, and safe handling of persistence resources. For example, related code that uses Hibernate generally needs to use the same Hibernate Session to ensure efficiency and proper transaction handling. Spring makes it easy to create and bind a Session to the current thread transparently, by exposing a current Session through the Hibernate SessionFactory. Thus Spring solves many chronic problems of typical Hibernate usage, for any local or JTA transaction environment.

通常的资源管理。spring应用上下文可以处理Hibernate的SessionFactory实例的位置和配置、JPA的EntityManagerFactory实例、JDBC的数据源实例和其他相应资源。这使得这些值可以简单的管理和改变。spring提供了有效、简单和安全的处理持久化资源的方式。例如,相应的代码使用Hibernate需要系统的Hibernate的Session来保证有效和适当的事务处理。spring使得可以简单的创建和绑定一个Session显式对于当前的线程,暴露一个当前的Session通过Hibernate的SessionFactory。spring解决很多慢性的Hibernate使用中的问题,对于本地或JTA的事务环境。

    Integrated transaction management. You can wrap your ORM code with a declarative, aspect-oriented programming (AOP) style method interceptor either through the @Transactional annotation or by explicitly configuring the transaction AOP advice in an XML configuration file. In both cases, transaction semantics and exception handling (rollback, and so on) are handled for you. As discussed below, in Resource and transaction management, you can also swap various transaction managers, without affecting your ORM-related code. For example, you can swap between local transactions and JTA, with the same full services (such as declarative transactions) available in both scenarios. Additionally, JDBC-related code can fully integrate transactionally with the code you use to do ORM. This is useful for data access that is not suitable for ORM, such as batch processing and BLOB streaming, which still need to share common transactions with ORM operations.

集成事务管理。你可以包裹你的ORM代码通过声明、面向切面的方式方法拦截器通过@Transactional或在xml配置文件中明确配置AOP的advice。在两个例子中,事务语义和异常处理(回滚等等)可以被你来处理。就像下面套路的,在资源和事务管理中,你也可以交换不同的事务管理而不需要影响你的ORM相关代码。例如,你可以交换本地事务和JTA,使用相同的服务(例如声明式事务)在两种不同的情况。此外,JDBC相关的代码可以集成事务在你使用ORM的时候。这对于数据的访问是有用的但是不适合ORM,例如一个批处理和BLOB的流,依旧可以通过ORM操作在相同的事务中共享。

[Tip]

提示

For more comprehensive ORM support, including support for alternative database technologies such as MongoDB, you might want to check out the Spring Data suite of projects. If you are a JPA user, the Getting Started Accessing Data with JPA guide from https://spring.io provides a great introduction.

对于更复杂的ORM支持,包括不同数据库技术的支持如MongoDB,你可以查看spring的Data相关工程。如果你是一个JPA的用户,可以查看来自https://spring.io提供的指南关于开始数据访问通过JPA指南。

20.2 General ORM integration considerations

通常的ORM集成考虑

This section highlights considerations that apply to all ORM technologies. The Section 20.3, “Hibernate” section provides more details and also show these features and configurations in a concrete context.

这个章节重点考虑的有关ORM技术的应用。20.3节“Hibernate”章节提供了更多的细节并且展示了这些特性和配置在一个混合的上下文中。

The major goal of Spring’s ORM integration is clear application layering, with any data access and transaction technology, and for loose coupling of application objects. No more business service dependencies on the data access or transaction strategy, no more hard-coded resource lookups, no more hard-to-replace singletons, no more custom service registries. One simple and consistent approach to wiring up application objects, keeping them as reusable and free from container dependencies as possible. All the individual data access features are usable on their own but integrate nicely with Spring’s application context concept, providing XML-based configuration and cross-referencing of plain JavaBean instances that need not be Spring-aware. In a typical Spring application, many important objects are JavaBeans: data access templates, data access objects, transaction managers, business services that use the data access objects and transaction managers, web view resolvers, web controllers that use the business services,and so on.

spring的ORM集成的主要目标是干净的应用层,通过数据访问和事务技术,并且适用于松耦合应用的object。不需要业务服务依赖对于数据访问或事务策略,不需要硬编码资源查找,不需要硬替换单例,不需要自定义服务注册。一个简单的一致的方式来处理应用object,保证他们可以重用并且可以不依赖容器的依赖。所有独立的数据访问特性可以重用他们本身但是很好的集成spring的应用上下文概念、提供基于xml的配置和跨引用的普通JavaBean实例不需要Spring-aware。在一个普通的spring的应用中,许多重要的object是JavaBean:数据访问模板、数据访问object、事务管理、业务服务并使用数据访问object、web页面解析、web控制需要使用业务服务等。

20.2.1 Resource and transaction management

资源和事务管理

Typical business applications are cluttered with repetitive resource management code. Many projects try to invent their own solutions, sometimes sacrificing proper handling of failures for programming convenience. Spring advocates simple solutions for proper resource handling, namely IoC through templating in the case of JDBC and applying AOP interceptors for the ORM technologies.

通常业务应用混杂着重复的资源代码。许多项目试图介绍他们的解决方案,有时牺牲适当的错误处理为了编程的方便。spring拥护简单的解决访问对于适当的资源处理、命名IOC通过模板由于JDBC并且应用AOP拦截器对于ORM技术。

The infrastructure provides proper resource handling and appropriate conversion of specific API exceptions to an unchecked infrastructure exception hierarchy. Spring introduces a DAO exception hierarchy, applicable to any data access strategy. For direct JDBC, the JdbcTemplate class mentioned in a previous section provides connection handling and proper conversion of SQLException to the DataAccessException hierarchy, including translation of database-specific SQL error codes to meaningful exception classes. For ORM technologies, see the next section for how to get the same exception translation benefits.

基础设施提供了适当的资源处理和对于特定API异常的适当转换对于一个非检查的基础设施异常结构。spring引入了一个DAO异常结构,应用任何数据访问的策略。对于直接的JDBC,之前章节中提到的JdbcTemplate类提供了连接处理和对于SQLException的适当转换到DataAccessException的结构,包括特定数据库SQL错误代码的翻译对于有意义的异常类。对于ORM技术,可以看下一节对于获得相同异常翻译的好处。

When it comes to transaction management, the JdbcTemplate class hooks in to the Spring transaction support and supports both JTA and JDBC transactions, through respective Spring transaction managers. For the supported ORM technologies Spring offers Hibernate, JPA and JDO support through the Hibernate, JPA, and JDO transaction managers as well as JTA support. For details on transaction support, see the Chapter 17, Transaction Management chapter.

当处理事务管理,JdbcTemplate类在spring中事务支持钩子并且支持JTA和JDBC事务,通过相应的spring的事务管理。对于支持ORM技术spring提供了Hibernate、JPA和JDO支持通过Hibernate、JPA和JDO事务管理就像JTA支持。事务支持的详细细节,见17章,事务管理章节。

20.2.2 Exception translation

异常翻译

When you use Hibernate, JPA, or JDO in a DAO, you must decide how to handle the persistence technology’s native exception classes. The DAO throws a subclass of a HibernateException, PersistenceException or JDOException depending on the technology. These exceptions are all run-time exceptions and do not have to be declared or caught. You may also have to deal with IllegalArgumentException and IllegalStateException. This means that callers can only treat exceptions as generally fatal, unless they want to depend on the persistence technology’s own exception structure. Catching specific causes such as an optimistic locking failure is not possible without tying the caller to the implementation strategy. This trade off might be acceptable to applications that are strongly ORM-based and/or do not need any special exception treatment. However, Spring enables exception translation to be applied transparently through the @Repository annotation:

当你使用Hibernate、JPA后JDO在DAO中时,你必须决定如何处理持久化技术的本地异常类。DAO依赖这些技术,抛出HibernateException、PersistenceException或JDOException的子类。这些异常都是运行时异常并且不需要声明和捕获。你也可以处理IllegalArgumentException和IllegalStateException。这意味着调用者可以处理异常作为普通的错误,除非他们希望依赖持久化技术自己的异常结构。处理特定的引用例如乐观锁失败是不需要调用实现策略的。这些权衡可能适合于应用依赖于基于ORM并且不需要任何特定的异常处理。然而,spring允许异常翻译来应用通过@Repository注解:

@Repository

public class ProductDaoImpl implements ProductDao {

    // class body here...

}

<beans>

    <!-- Exception translation bean post processor -->

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

    <bean id="myProductDao" class="product.ProductDaoImpl"/>

</beans>

The postprocessor automatically looks for all exception translators (implementations of the PersistenceExceptionTranslator interface) and advises all beans marked with the @Repository annotation so that the discovered translators can intercept and apply the appropriate translation on the thrown exceptions.

处理器自动查找所有的异常翻译(实现了PersistenceExceptionTranslator接口)并且通知所有的bean使用@Repository注解因此发现翻译器可以拦截并应用适当的翻译通过抛出异常。

In summary: you can implement DAOs based on the plain persistence technology’s API and annotations, while still benefiting from Spring-managed transactions, dependency injection, and transparent exception conversion (if desired) to Spring’s custom exception hierarchies.

总结一下:你可以实现DAO基于普通的持久化API和注解,依然可以从spring管理的事务、依赖注入和显示异常转换中获益(如果值得)对于spring的自定义异常结构。

20.3 Hibernate

We will start with a coverage of Hibernate 5 in a Spring environment, using it to demonstrate the approach that Spring takes towards integrating O/R mappers. This section will cover many issues in detail and show different variations of DAO implementations and transaction demarcation. Most of these patterns can be directly translated to all other supported ORM tools. The following sections in this chapter will then cover the other ORM technologies, showing briefer examples there.

我们将会开始覆盖Hibernate5在spring的环境中,使用描述的方式使得spring来基层OR映射。这一节将包括许多问题的细节并展示不同的DAO实现和翻译描述。这些方式的大部分可以直接通过所有其他的ORM工具来支持。这节中下面的内容将包括ORM技术,展示简单的例子。

[Note]

注意

As of Spring 4.0, Spring requires Hibernate 3.6 or later. Note that the Hibernate team stopped supporting Hibernate 3 years ago and even phased out support for Hibernate 4.x in late 2015. We therefore recommend Hibernate 5.1 and higher from a 2016+ perspective.

由于spring4.0,spring要求Hibernate最低版本是3.6及以上。注意Hibernate小组停止支持Hibernate3在很多年之前并且Hibernate4.x最多支持到2015年。我们建议Hibernate5.1或更高的2016之后的版本。

20.3.1 SessionFactory setup in a Spring container

在spring容器中设置SessionFactory

To avoid tying application objects to hard-coded resource lookups, you can define resources such as a JDBC DataSource or a Hibernate SessionFactory as beans in the Spring container. Application objects that need to access resources receive references to such predefined instances through bean references, as illustrated in the DAO definition in the next section.

为了避免硬编码资源查找的方式,你可以定义资源类似于一个JDBC数据源或一个Hibernate的SessionFactory作为一个bean在spring的容器中。应用object需要访问资源接收应用对于这样提前定义的实例通过bean的引用,作为DAO的定义在下一节中描述。

The following excerpt from an XML application context definition shows how to set up a JDBC DataSource and a Hibernate SessionFactory on top of it:

下面的内容来自xml的应用上下文展示了如何设置一个JDBC数据源和Hibernate的SessionFactory:

<beans>

    <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">

        <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>

        <property name="url" value="jdbc:hsqldb:hsql://localhost:9001"/>

        <property name="username" value="sa"/>

        <property name="password" value=""/>

    </bean>

    <bean id="mySessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">

        <property name="dataSource" ref="myDataSource"/>

        <property name="mappingResources">

            <list>

                <value>product.hbm.xml</value>

            </list>

        </property>

        <property name="hibernateProperties">

            <value>

                hibernate.dialect=org.hibernate.dialect.HSQLDialect

            </value>

        </property>

    </bean>

</beans>

Switching from a local Jakarta Commons DBCP BasicDataSource to a JNDI-located DataSource (usually managed by an application server) is just a matter of configuration:

将一个本地的Jakarta Commons DBCP BasicDataSource切换为基于JNDI的数据源(通常通过一个应用服务器来管理)只需要一个配置:

<beans>

    <jee:jndi-lookup id="myDataSource" jndi-name="java:comp/env/jdbc/myds"/>

</beans>

You can also access a JNDI-located SessionFactory, using Spring’s JndiObjectFactoryBean / <jee:jndi-lookup> to retrieve and expose it. However, that is typically not common outside of an EJB context.

你也可以访问基于JNDI的SessionFactory,使用spring的JndiObjectFactoryBean / <jee:jndi-lookup>来接收并展示他。然而,通常不会在EJB上下文的外面。

20.3.2 Implementing DAOs based on plain Hibernate API

基于普通的Hibernate API实现DAO

Hibernate has a feature called contextual sessions, wherein Hibernate itself manages one current Session per transaction. This is roughly equivalent to Spring’s synchronization of one Hibernate Session per transaction. A corresponding DAO implementation resembles the following example, based on the plain Hibernate API:

Hibernate有一个特性名字为上下文会话,在Hibernate中管理每个事务中的当前会话。这和spring对于一个Hibernate会话的每个事务进行同步差不多。一个相应的DAO实现类似于下面的例子,基于普通的Hibernate的API:

public class ProductDaoImpl implements ProductDao {

    private SessionFactory sessionFactory;

    public void setSessionFactory(SessionFactory sessionFactory) {

        this.sessionFactory = sessionFactory;

    }

    public Collection loadProductsByCategory(String category) {

        return this.sessionFactory.getCurrentSession()

                .createQuery("from test.Product product where product.category=?")

                .setParameter(0, category)

                .list();

    }

}

This style is similar to that of the Hibernate reference documentation and examples, except for holding the SessionFactory in an instance variable. We strongly recommend such an instance-based setup over the old-school static HibernateUtil class from Hibernate’s CaveatEmptor sample application. (In general, do not keep any resources in static variables unless absolutely necessary.)

这种风格和Hibernate应用文档和例子相似,处理在一个实例变量中持有一个SessionFactory。我们强烈推荐这样基于实例的设置而不是老式的静态HibernateUtil类来自Hibernate的CaveatEmptor案例中。(通常,不需要持有任何资源在静态变量除非是必要的。)

The above DAO follows the dependency injection pattern: it fits nicely into a Spring IoC container, just as it would if coded against Spring’s HibernateTemplate. Of course, such a DAO can also be set up in plain Java (for example, in unit tests). Simply instantiate it and call setSessionFactory(..) with the desired factory reference. As a Spring bean definition, the DAO would resemble the following:

上面的DAO遵循依赖注入的模式:他很好的适合了spring的IOC容器,就像他可以适合spring的HibernateTemplate。当然,这样的DAO也可以被设置通过普通的Java方式(例如在单元测试中)。简单的实例化他并调用setSessionFactory传递预期的工程引用。作为一个spring的bean定义,DAO可以声明如下:

<beans>

    <bean id="myProductDao" class="product.ProductDaoImpl">

        <property name="sessionFactory" ref="mySessionFactory"/>

    </bean>

</beans>

The main advantage of this DAO style is that it depends on Hibernate API only; no import of any Spring class is required. This is of course appealing from a non-invasiveness perspective, and will no doubt feel more natural to Hibernate developers.

这种DAO风格主要的优点是他只依赖Hibernate的API,不需要导入spring的类。这样当然没有任何侵入性,并且会觉得很自然对于Hibernate的开发者。

However, the DAO throws plain HibernateException (which is unchecked, so does not have to be declared or caught), which means that callers can only treat exceptions as generally fatal - unless they want to depend on Hibernate’s own exception hierarchy. Catching specific causes such as an optimistic locking failure is not possible without tying the caller to the implementation strategy. This trade off might be acceptable to applications that are strongly Hibernate-based and/or do not need any special exception treatment.

然而,DAO抛出普通的HibernateException(是非检查的,引起不需要声明或捕获),意味着调用者只需要处理通常的错误————除非他们希望依赖Hibernate自身的异常结构。捕获特定的原因例如乐观锁失败是不可能的除非调用实现策略。这种方式适合应用是基于Hibernate不需要特定的异常处理。

Fortunately, Spring’s LocalSessionFactoryBean supports Hibernate’s SessionFactory.getCurrentSession() method for any Spring transaction strategy, returning the current Spring-managed transactional Session even with HibernateTransactionManager. Of course, the standard behavior of that method remains the return of the current Session associated with the ongoing JTA transaction, if any. This behavior applies regardless of whether you are using Spring’s JtaTransactionManager, EJB container managed transactions (CMTs), or JTA.

幸运的是,spring的LocalSessionFactoryBean支持Hibernate的SessionFactory.getCurrentSession()方法对于任何spring的事务策略,返回当前spring管理的事务会话甚至通过HibernateTransactionManager。当然,这个方法标准的行为保留返回当前的会话连接不间断的JTA事务,如何可以的话。这种行为不管你使用spring的JtaTransactionManager、EJB容器管理事务或JTA。

In summary: you can implement DAOs based on the plain Hibernate API, while still being able to participate in Spring-managed transactions.

总结:你可以基于普通的Hibernate的API引入DAO,依然可以参与spring管理的事务。

20.3.3 Declarative transaction demarcation

声明事务的划分

We recommend that you use Spring’s declarative transaction support, which enables you to replace explicit transaction demarcation API calls in your Java code with an AOP transaction interceptor. This transaction interceptor can be configured in a Spring container using either Java annotations or XML.This declarative transaction capability allows you to keep business services free of repetitive transaction demarcation code and to focus on adding business logic, which is the real value of your application.

我们建议你使用spring的声明式事务支持,允许你替代显式的事务划分API调用在你的Java代码中通过AOP的事务拦截器。这个是我拦截器可以被配置在spring的容器中使用Java注解或XML。这个声明事务允许你保持业务的自由有关声明式事务隔离代码并且不强制添加业务逻辑,对于你的应用。

[Note]

注意

Prior to continuing, you are strongly encouraged to read Section 17.5, “Declarative transaction management” if you have not done so.

之前的继续,我们强烈建议你阅读17.5节,“声明式事务管理”如果你没有读过的话。

Furthermore, transaction semantics like propagation behavior and isolation level can be changed in a configuration file and do not affect the business service implementations.

此外,事务语义就像传播行为和隔离级别可以在配置文件中改变并且不会影响到业务服务的实现:

The following example shows how you can configure an AOP transaction interceptor, using XML, for a simple service class:

下面的例子展示如何配置一个AOP事务拦截器,使用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:aop="http://www.springframework.org/schema/aop"

    xmlns:tx="http://www.springframework.org/schema/tx"

    xsi:schemaLocation="

        http://www.springframework.org/schema/beans

        http://www.springframework.org/schema/beans/spring-beans.xsd

        http://www.springframework.org/schema/tx

        http://www.springframework.org/schema/tx/spring-tx.xsd

        http://www.springframework.org/schema/aop

        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- SessionFactory, DataSource, etc. omitted -->

    <bean id="transactionManager"

            class="org.springframework.orm.hibernate5.HibernateTransactionManager">

        <property name="sessionFactory" ref="sessionFactory"/>

    </bean>

    <aop:config>

        <aop:pointcut id="productServiceMethods"

                expression="execution(* product.ProductService.*(..))"/>

        <aop:advisor advice-ref="txAdvice" pointcut-ref="productServiceMethods"/>

    </aop:config>

    <tx:advice id="txAdvice" transaction-manager="myTxManager">

        <tx:attributes>

            <tx:method name="increasePrice*" propagation="REQUIRED"/>

            <tx:method name="someOtherBusinessMethod" propagation="REQUIRES_NEW"/>

            <tx:method name="*" propagation="SUPPORTS" read-only="true"/>

        </tx:attributes>

    </tx:advice>

    <bean id="myProductService" class="product.SimpleProductService">

        <property name="productDao" ref="myProductDao"/>

    </bean>

</beans>

This is the service class that is advised:

这个服务类是这样的:

public class ProductServiceImpl implements ProductService {

    private ProductDao productDao;

    public void setProductDao(ProductDao productDao) {

        this.productDao = productDao;

    }

    // notice the absence of transaction demarcation code in this method

    // Spring's declarative transaction infrastructure will be demarcating

    // transactions on your behalf

    public void increasePriceOfAllProductsInCategory(final String category) {

        List productsToChange = this.productDao.loadProductsByCategory(category);

        // ...

    }

}

We also show an attribute-support based configuration, in the following example. You annotate the service layer with @Transactional annotations and instruct the Spring container to find these annotations and provide transactional semantics for these annotated methods.

我们也展示了支持属性的配置,在下面的例子中。你声明一个服务层通过@Transactional注解并指定spring容器来查找这些注解并提供事务语义对于这些声明方法。

public class ProductServiceImpl implements ProductService {

    private ProductDao productDao;

    public void setProductDao(ProductDao productDao) {

        this.productDao = productDao;

    }

    @Transactional

    public void increasePriceOfAllProductsInCategory(final String category) {

        List productsToChange = this.productDao.loadProductsByCategory(category);

        // ...

    }

    @Transactional(readOnly = true)

    public List<Product> findAllProducts() {

        return this.productDao.findAllProducts();

    }

}

As you can see from the following configuration example, the configuration is much simplified, compared to the XML example above, while still providing the same functionality driven by the annotations in the service layer code. All you need to provide is the TransactionManager implementation and a "<tx:annotation-driven/>" entry.

就像你可以看到的下面的配置案例,配置是很简单的,对于上面的xml,但是依然提供了相同的功能对于服务层代码中的注解。所有你需要提供的是一个TransactionManager和一个"<tx:annotation-driven/>"配置。

<?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:aop="http://www.springframework.org/schema/aop"

    xmlns:tx="http://www.springframework.org/schema/tx"

    xsi:schemaLocation="

        http://www.springframework.org/schema/beans

        http://www.springframework.org/schema/beans/spring-beans.xsd

        http://www.springframework.org/schema/tx

        http://www.springframework.org/schema/tx/spring-tx.xsd

        http://www.springframework.org/schema/aop

        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- SessionFactory, DataSource, etc. omitted -->

    <bean id="transactionManager"

            class="org.springframework.orm.hibernate5.HibernateTransactionManager">

        <property name="sessionFactory" ref="sessionFactory"/>

    </bean>

    <tx:annotation-driven/>

    <bean id="myProductService" class="product.SimpleProductService">

        <property name="productDao" ref="myProductDao"/>

    </bean>

</beans>

20.3.4 Programmatic transaction demarcation

编程事务划分

You can demarcate transactions in a higher level of the application, on top of such lower-level data access services spanning any number of operations. Nor do restrictions exist on the implementation of the surrounding business service; it just needs a Spring PlatformTransactionManager. Again, the latter can come from anywhere, but preferably as a bean reference through a setTransactionManager(..) method, just as the productDAO should be set by a setProductDao(..) method. The following snippets show a transaction manager and a business service definition in a Spring application context, and an example for a business method implementation:

你可以分离事务以一种高级别对于应用来说,这样低级别的数据访问服务跨越任意的操作。不限制实现业务服务:只需要一个spring的PlatformTransactionManager。再次,后序可以来自任何但是最好作为一个bean的引用通过setTransactionManager方法,就像productDAO应该被设置通过setProductDao方法一样。下面的片段展示了一个事务管理和一个业务服务定义在spring的应用上下文并且是一个对于业务方法实现的例子:

<beans>

    <bean id="myTxManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">

        <property name="sessionFactory" ref="mySessionFactory"/>

    </bean>

    <bean id="myProductService" class="product.ProductServiceImpl">

        <property name="transactionManager" ref="myTxManager"/>

        <property name="productDao" ref="myProductDao"/>

    </bean>

</beans>

public class ProductServiceImpl implements ProductService {

    private TransactionTemplate transactionTemplate;

    private ProductDao productDao;

    public void setTransactionManager(PlatformTransactionManager transactionManager) {

        this.transactionTemplate = new TransactionTemplate(transactionManager);

    }

    public void setProductDao(ProductDao productDao) {

        this.productDao = productDao;

    }

    public void increasePriceOfAllProductsInCategory(final String category) {

        this.transactionTemplate.execute(new TransactionCallbackWithoutResult() {

            public void doInTransactionWithoutResult(TransactionStatus status) {

                List productsToChange = this.productDao.loadProductsByCategory(category);

                // do the price increase...

            }

        });

    }

}

Spring’s TransactionInterceptor allows any checked application exception to be thrown with the callback code, while TransactionTemplate is restricted to unchecked exceptions within the callback. TransactionTemplate triggers a rollback in case of an unchecked application exception, or if the transaction is marked rollback-only by the application (via TransactionStatus). TransactionInterceptor behaves the same way by default but allows configurable rollback policies per method.

spring的TransactionInterceptor允许任何检查应用异常被抛出通过回调代码,当TransactionTemplate被限制为非限制异常对于回调来说。TransactionTemplate引发回滚在一个非检查的应用异常中或如果事务被标记为只回滚通过应用(通过TransactionStatus)。TransactionInterceptor会有相同的行为在默认的情况下但是允许对于每个方法配置回滚策略。

20.3.5 Transaction management strategies

事务管理策略

Both TransactionTemplate and TransactionInterceptor delegate the actual transaction handling to a PlatformTransactionManager instance, which can be a HibernateTransactionManager (for a single Hibernate SessionFactory, using a ThreadLocal Session under the hood) or a JtaTransactionManager (delegating to the JTA subsystem of the container) for Hibernate applications. You can even use a custom PlatformTransactionManager implementation. Switching from native Hibernate transaction management to JTA, such as when facing distributed transaction requirements for certain deployments of your application, is just a matter of configuration. Simply replace the Hibernate transaction manager with Spring’s JTA transaction implementation. Both transaction demarcation and data access code will work without changes, because they just use the generic transaction management APIs.

TransactionTemplate和TransactionInterceptor委托实际的事务处理对于PlatformTransactionManager实例,可以作为一个HibernateTransactionManager(对于一个简单的Hibernate的SessionFactory使用一个线程本地的会话在引擎后面)或一个JtaTransactionManager(委托给容器的JTA子系统)对于Hibernate应用。你甚至可以使用一个自定义的PlatformTransactionManager实现。切换本地Hibernate事务管理到JTA,就像处理分布式事务需求对于特定的应用开发,就是一个配置问题。简单的替换Hibernate事务管理通过JTA事务实现。事务隔离和数据访问代码将不需要任何改变,因为只是使用了通常的事务管理API。

For distributed transactions across multiple Hibernate session factories, simply combine JtaTransactionManager as a transaction strategy with multiple LocalSessionFactoryBean definitions. Each DAO then gets one specific SessionFactory reference passed into its corresponding bean property. If all underlying JDBC data sources are transactional container ones, a business service can demarcate transactions across any number of DAOs and any number of session factories without special regard, as long as it is using JtaTransactionManager as the strategy.

对于分布式事务跨越多个Hibernate的会话工厂,简单的合并JtaTransactionManager作为一个事务策略对于多个LocalSessionFactoryBean定义。每个DAO获得一个特定的SessionFactory引用传递给相应的bean属性。如果每个JDBC数据资源下是事务容器,一个业务服务可以区分事务在任意数量的DAO中并且会话工程的数量不需要有特定的考虑,可以使用JtaTransactionManager作为策略。

<beans>

    <jee:jndi-lookup id="dataSource1" jndi-name="java:comp/env/jdbc/myds1"/>

    <jee:jndi-lookup id="dataSource2" jndi-name="java:comp/env/jdbc/myds2"/>

    <bean id="mySessionFactory1"

            class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">

        <property name="dataSource" ref="myDataSource1"/>

        <property name="mappingResources">

            <list>

                <value>product.hbm.xml</value>

            </list>

        </property>

        <property name="hibernateProperties">

            <value>

                hibernate.dialect=org.hibernate.dialect.MySQLDialect

                hibernate.show_sql=true

            </value>

        </property>

    </bean>

    <bean id="mySessionFactory2"

            class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">

        <property name="dataSource" ref="myDataSource2"/>

        <property name="mappingResources">

            <list>

                <value>inventory.hbm.xml</value>

            </list>

        </property>

        <property name="hibernateProperties">

            <value>

                hibernate.dialect=org.hibernate.dialect.OracleDialect

            </value>

        </property>

    </bean>

    <bean id="myTxManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>

    <bean id="myProductDao" class="product.ProductDaoImpl">

        <property name="sessionFactory" ref="mySessionFactory1"/>

    </bean>

    <bean id="myInventoryDao" class="product.InventoryDaoImpl">

        <property name="sessionFactory" ref="mySessionFactory2"/>

    </bean>

    <bean id="myProductService" class="product.ProductServiceImpl">

        <property name="productDao" ref="myProductDao"/>

        <property name="inventoryDao" ref="myInventoryDao"/>

    </bean>

    <aop:config>

        <aop:pointcut id="productServiceMethods"

                expression="execution(* product.ProductService.*(..))"/>

        <aop:advisor advice-ref="txAdvice" pointcut-ref="productServiceMethods"/>

    </aop:config>

    <tx:advice id="txAdvice" transaction-manager="myTxManager">

        <tx:attributes>

            <tx:method name="increasePrice*" propagation="REQUIRED"/>

            <tx:method name="someOtherBusinessMethod" propagation="REQUIRES_NEW"/>

            <tx:method name="*" propagation="SUPPORTS" read-only="true"/>

        </tx:attributes>

    </tx:advice>

</beans>

Both HibernateTransactionManager and JtaTransactionManager allow for proper JVM-level cache handling with Hibernate, without container-specific transaction manager lookup or a JCA connector (if you are not using EJB to initiate transactions).

HibernateTransactionManager和JtaTransactionManager运行适当的JVM级别的缓存处理通过Hibernate,不需要特定容器的事务管理查找或JCA连接器(如果你不使用EJB来开始事务)。

HibernateTransactionManager can export the Hibernate JDBC Connection to plain JDBC access code, for a specific DataSource. This capability allows for high-level transaction demarcation with mixed Hibernate and JDBC data access completely without JTA, if you are accessing only one database. HibernateTransactionManager automatically exposes the Hibernate transaction as a JDBC transaction if you have set up the passed-in SessionFactory with a DataSource through the dataSource property of the LocalSessionFactoryBean class. Alternatively, you can specify explicitly the DataSource for which the transactions are supposed to be exposed through the dataSource property of the HibernateTransactionManager class.

HibernateTransactionManager可以导出普通的JDBC跨代码,对于一个特定的数据源。这允许一个高级别的事务隔离通过混合的Hibernate和JDBC数据访问完全不依赖JTA,如果你只访问一个数据库。HibernateTransactionManager自动暴露Hibernate事务作为一个JDBC事务如果你设置传递一个数据源给SessionFactory通过LocalSessionFactoryBean类的属性。作为替代,你可以明确指定数据源对于事务被支持用于暴露通过HibernateTransactionManager类的dataSource属性。

20.3.6 Comparing container-managed and locally defined resources

比较容器管理和本地定义资源

You can switch between a container-managed JNDI SessionFactory and a locally defined one, without having to change a single line of application code. Whether to keep resource definitions in the container or locally within the application is mainly a matter of the transaction strategy that you use. Compared to a Spring-defined local SessionFactory, a manually registered JNDI SessionFactory does not provide any benefits. Deploying a SessionFactory through Hibernate’s JCA connector provides the added value of participating in the Java EE server’s management infrastructure, but does not add actual value beyond that.

你可以切换容器管理JNDI的SessionFactory和本地定义不需要改变一行应用代码。保持资源定义在容器中或本地在应用中主要问题是你使用的事务策略。对比spring定义的本地SessionFactory,一个手动注册JNDI的SessionFactory没有提供任何益处。通过Hibernate的JCA连接部署一个SessionFactory提供添加值在JavaEE服务器的管理中,但是不能添加实际值。

Spring’s transaction support is not bound to a container. Configured with any strategy other than JTA, transaction support also works in a stand-alone or test environment. Especially in the typical case of single-database transactions, Spring’s single-resource local transaction support is a lightweight and powerful alternative to JTA. When you use local EJB stateless session beans to drive transactions, you depend both on an EJB container and JTA, even if you access only a single database, and only use stateless session beans to provide declarative transactions through container-managed transactions. Also, direct use of JTA programmatically requires a Java EE environment as well. JTA does not involve only container dependencies in terms of JTA itself and of JNDI DataSource instances. For non-Spring, JTA-driven Hibernate transactions, you have to use the Hibernate JCA connector, or extra Hibernate transaction code with the TransactionManagerLookup configured for proper JVM-level caching.

spring的事务支持没有绑定到容器。和JTA不同可以配置任何策略,也可以独立使用或在测试环境中工作。尤其是在典型的单数据事务中,spring的单资源本地事务支持是轻量级的并且是功能强大的对于JTA来说。当你使用本地的EJB无状态会话bean来驱动事务,你依赖EJB容器和JTA即使你只是访问当个数据库并且使用了无状态会话bean来提供声明式事务通过容器管理的事务。并且直接编程使用JTA也需要一个JavaEE环境。JTA不会只依赖容器和JNDI的数据源实例。对于非spring,基于JTA的Hibernate事务,你需要使用Hibernate的JCA连接器或额外的Hibernate事务代码通过TransactionManagerLookup的配置对于JVM级别的缓存。

Spring-driven transactions can work as well with a locally defined Hibernate SessionFactory as they do with a local JDBC DataSource if they are accessing a single database. Thus you only have to use Spring’s JTA transaction strategy when you have distributed transaction requirements. A JCA connector requires container-specific deployment steps, and obviously JCA support in the first place. This configuration requires more work than deploying a simple web application with local resource definitions and Spring-driven transactions. Also, you often need the Enterprise Edition of your container if you are using, for example, WebLogic Express, which does not provide JCA. A Spring application with local resources and transactions spanning one single database works in any Java EE web container (without JTA, JCA, or EJB) such as Tomcat, Resin, or even plain Jetty. Additionally, you can easily reuse such a middle tier in desktop applications or test suites.

基于spring的事务可以工作和本地定义的Hibernate的SeesionFactory使用本地的JDBC数据源如果他们创建单个数据库。你只需要使用spring的JTA事务策略当你有分布式事务的需求时。一个JCA容器需要特定的容器部署和明确的JCA支持作为第一位。这种配置要求需要步数一个简单的web应用使用本地资源定义和基于spring的事务。并且你需要一个企业级版本的容器如果你使用的话,例如,WebLogic Express,不需要提供JCA。一个spring应用使用本地资源和事务跨越单个数据库工作在JavaEE的web容器中(不需要JTA、JCA或EJB)例如Tomcat、Resin或甚至是Jetty。此外,你可以简单的重用这样的中间件来连接桌面应用或测试套件。

All things considered, if you do not use EJBs, stick with local SessionFactory setup and Spring’s HibernateTransactionManager or JtaTransactionManager. You get all of the benefits, including proper transactional JVM-level caching and distributed transactions, without the inconvenience of container deployment. JNDI registration of a Hibernate SessionFactory through the JCA connector only adds value when used in conjunction with EJBs.

所有的事情都被考虑了,如果你不使用EJB,将本地SessionFactory设置和spring的HibernateTransactionManager或JtaTransactionManager连接在一起。你获得了很多好处,包括适当的事务JVM级别缓存和分布式事务,不需要依赖容器的部署。Hibernate的SessionFactory的JNDI注册通过JCA容器只需要添加值通过EJB使用。

20.3.7 Spurious application server warnings with Hibernate

使用Hibernate伪造应用服务器警告

In some JTA environments with very strict XADataSource implementations — currently only some WebLogic Server and WebSphere versions — when Hibernate is configured without regard to the JTA PlatformTransactionManager object for that environment, it is possible for spurious warning or exceptions to show up in the application server log. These warnings or exceptions indicate that the connection being accessed is no longer valid, or JDBC access is no longer valid, possibly because the transaction is no longer active. As an example, here is an actual exception from WebLogic:

在一些JTA环境中有非常严格的XADataSource实现————当时只有一些WebLogic服务器和WebSphere的部分版本————当Hibernate被配置不需要JTA的PlatformTransactionManager对于这些环境,他可以伪造警告或异常出现在应用服务器的日志中。这些警告或异常的连接访问将不在有效、或JDBC访问将不在有效,有时因为事务不会被激活。例如,有一个实际的异常来自WebLogic。

java.sql.SQLException: The transaction is no longer active - status: 'Committed'. No further JDBC access is allowed within this transaction.

You resolve this warning by simply making Hibernate aware of the JTA PlatformTransactionManager instance, to which it will synchronize (along with Spring). You have two options for doing this:

你解决异常通常是简单的标记的Hibernate了解PlatformTransactionManager实例,将会同步(和spring)。你有两个选项对于这件事:

    If in your application context you are already directly obtaining the JTA PlatformTransactionManager object (presumably from JNDI through JndiObjectFactoryBean or <jee:jndi-lookup>) and feeding it, for example, to Spring’s JtaTransactionManager, then the easiest way is to specify a reference to the bean defining this JTA PlatformTransactionManager instance as the value of the jtaTransactionManager property for LocalSessionFactoryBean. Spring then makes the object available to Hibernate.

如果在你的应用上下文你已经直接获得了JTA的PlatformTransactionManager的object(推测来自JNDI通过JndiObjectFactoryBean或<jee:jndi-lookup>)和处理他们,例如,对于spring的JtaTransactionManager,最简单的方法就是定义一个bean的引用定义在JTA的PlatformTransactionManager实例作为LocalSessionFactoryBean的jtaTransactionManager属性值。spring 使得object对于Hibernate可用。

    More likely you do not already have the JTA PlatformTransactionManager instance, because Spring’s JtaTransactionManager can find it itself. Thus you need to configure Hibernate to look up JTA PlatformTransactionManager directly. You do this by configuring an application server- specific TransactionManagerLookup class in the Hibernate configuration, as described in the Hibernate manual.

就像你没有JTA的PlatformTransactionManager实例,因为spring的JtaTransactionManager可以发现本身。你需要配置Hibernate来直接查找JTA的PlatformTransactionManager。你通过配置应用服务器指定TransactionManagerLookup在Hibernate的配置中,这种做法描述在Hibernate的手册中。

The remainder of this section describes the sequence of events that occur with and without Hibernate’s awareness of the JTA PlatformTransactionManager.

这节剩余的内容描述事件发生的顺序不需要Hibernate了解JTA的PlatformTransactionManager。

When Hibernate is not configured with any awareness of the JTA PlatformTransactionManager, the following events occur when a JTA transaction commits:

当Hibernate没有配置任何JTA的PlatformTransactionManager时,下面的事件当JTA事务提交时发生:

    The JTA transaction commits.

JTA事务提交。

    Spring’s JtaTransactionManager is synchronized to the JTA transaction, so it is called back through an afterCompletion callback by the JTA transaction manager.

spring的JtaTransactionManager是同步的对于JTA事务,因此可以被回调通过JTA的事务管理器的afterCompletion回调。

    Among other activities, this synchronization can trigger a callback by Spring to Hibernate, through Hibernate’s afterTransactionCompletion callback (used to clear the Hibernate cache), followed by an explicit close() call on the Hibernate Session, which causes Hibernate to attempt to close() the JDBC Connection.

在其他活动中,这些同步可以截获spring对于Hibernate的回调,通过Hibernate的afterTransactionCompletion回调(使用清理Hibernate的缓存),或许明确使用close方法调用对于Hibernate的session,可以使得Hibernate调用JDBC连接的close方法。

    In some environments, this Connection.close() call then triggers the warning or error, as the application server no longer considers the Connection usable at all, because the transaction has already been committed.

在一些环境中,这个Connection.close方法会引起警告或错误,作为应用服务器不需要考虑连接的重用,因为事务已经被提交了。

When Hibernate is configured with awareness of the JTA PlatformTransactionManager, the following events occur when a JTA transaction commits:

当Hibernate被配置了解了JTA的PlatformTransactionManager,下面的事件当JTA事务提交时会发生:

    the JTA transaction is ready to commit.

JTA事务准备提交。

    Spring’s JtaTransactionManager is synchronized to the JTA transaction, so the transaction is called back through a beforeCompletion callback by the JTA transaction manager.

spring的JtaTransactionManager是同步的对于JTA事务,因此事务可以被回调通过JTA的事务管理器的beforeCompletion回调。

    Spring is aware that Hibernate itself is synchronized to the JTA transaction, and behaves differently than in the previous scenario. Assuming the Hibernate Session needs to be closed at all, Spring will close it now.

spring连接Hibernate本身是同步的对于JTA事务,并且和之前场景中的行为是不同的。假设Hibernate的Session需要关闭,spring将会在现在关闭。

    The JTA transaction commits.

JTA事务提交。

    Hibernate is synchronized to the JTA transaction, so the transaction is called back through an afterCompletion callback by the JTA transaction manager, and can properly clear its cache.

Hibernate是同步的对于JTA事务,因此事务被回调通过JTA事务管理器的afterCompletion回调方法,并且可以清除他的缓存。

20.4 JDO

Spring supports the standard JDO 2.0 and 2.1 APIs as data access strategy, following the same style as the Hibernate support. The corresponding integration classes reside in the org.springframework.orm.jdo package.

spring支持标准的JDO2.0和2.1的API作为数据访问策略,符合相同的风格作为Hibernate的支持。相应的集成类在org.springframework.orm.jdo包中。

20.4.1 PersistenceManagerFactory setup

PersistenceManagerFactory的设置

Spring provides a LocalPersistenceManagerFactoryBean class that allows you to define a local JDO PersistenceManagerFactory within a Spring application context:

spring提供了一个LocalPersistenceManagerFactoryBean类允许你来定义一个本地的JDO的PersistenceManagerFactory通过spring的应用上下文:

<beans>

    <bean id="myPmf" class="org.springframework.orm.jdo.LocalPersistenceManagerFactoryBean">

        <property name="configLocation" value="classpath:kodo.properties"/>

    </bean>

</beans>

Alternatively, you can set up a PersistenceManagerFactory through direct instantiation of a PersistenceManagerFactory implementation class. A JDO PersistenceManagerFactory implementation class follows the JavaBeans pattern, just like a JDBC DataSource implementation class, which is a natural fit for a configuration that uses Spring. This setup style usually supports a Spring-defined JDBC DataSource, passed into the connectionFactory property. For example, for the open source JDO implementation DataNucleus (formerly JPOX) ( http://www.datanucleus.org/), this is the XML configuration of the PersistenceManagerFactory implementation:

作为代替,你可以设置一个PersistenceManagerFactory通过直接的PersistenceManagerFactory实现类。一个JDO的PersistenceManagerFactory实现类符合JavaBean的模型,就像JDBC数据源实现类,是自然符合spring的配置的。这种设置风格通过支持spring定义的JDBC数据源,通过connectionFactory属性来传递。例如,对于开源的JDO实现DataNucleus(以前是JPOX)(http://www.datanucleus.org/),是PersistenceManagerFactory实现的xml配置。

<beans>

 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">

    <property name="driverClassName" value="${jdbc.driverClassName}"/>

    <property name="url" value="${jdbc.url}"/>

    <property name="username" value="${jdbc.username}"/>

    <property name="password" value="${jdbc.password}"/>

 </bean>

 <bean id="myPmf" class="org.datanucleus.jdo.JDOPersistenceManagerFactory" destroy-method="close">

    <property name="connectionFactory" ref="dataSource"/>

    <property name="nontransactionalRead" value="true"/>

 </bean>

</beans>

You can also set up JDO PersistenceManagerFactory in the JNDI environment of a Java EE application server, usually through the JCA connector provided by the particular JDO implementation. Spring’s standard JndiObjectFactoryBean or <jee:jndi-lookup> can be used to retrieve and expose such a PersistenceManagerFactory. However, outside an EJB context, no real benefit exists in holding the PersistenceManagerFactory in JNDI: only choose such a setup for a good reason. See Section 20.3.6, “Comparing container-managed and locally defined resources” for a discussion; the arguments there apply to JDO as well.

你也可以设置JDO的PersistenceManagerFactory在JNDI环境作为JavaEE的应用服务器,JCA连接器通过特定的JDO实现来提供。spring的标准JndiObjectFactoryBean或<jee:jndi-lookup>可以被使用来获得并暴露这样的PersistenceManagerFactory。然而你,在EJB上下文之外,没有实际的好处存在来通过JNDI获取PersistenceManagerFactory:只有选择这样一个设置才是好的。见20.3.6节,“比较容器管理和本地定义资源”中的讨论,参数应用到JDO就可以。

20.4.2 Implementing DAOs based on the plain JDO API

基于普通的JDO的API实现DAO

DAOs can also be written directly against plain JDO API, without any Spring dependencies, by using an injected PersistenceManagerFactory. The following is an example of a corresponding DAO implementation:

DAO可以使用普通的JDO的API来完成,不需要spring的依赖,通过使用注入PersistenceManagerFactory。下面是一个例子有关DAO的实现:

public class ProductDaoImpl implements ProductDao {

    private PersistenceManagerFactory persistenceManagerFactory;

    public void setPersistenceManagerFactory(PersistenceManagerFactory pmf) {

        this.persistenceManagerFactory = pmf;

    }

    public Collection loadProductsByCategory(String category) {

        PersistenceManager pm = this.persistenceManagerFactory.getPersistenceManager();

        try {

            Query query = pm.newQuery(Product.class, "category = pCategory");

            query.declareParameters("String pCategory");

            return query.execute(category);

        }

        finally {

            pm.close();

        }

    }

}

<beans>

    <bean id="myProductDao" class="product.ProductDaoImpl">

        <property name="persistenceManagerFactory" ref="myPmf"/>

    </bean>

</beans>

The main problem with such DAOs is that they always get a new PersistenceManager from the factory. To access a Spring-managed transactional PersistenceManager, define a TransactionAwarePersistenceManagerFactoryProxy (as included in Spring) in front of your target PersistenceManagerFactory, then passing a reference to that proxy into your DAOs as in the following example:

这里主要的问题是这样的DAO会获得一个来自工程的新的PersistenceManager。为了访问spring管理的事务PersistenceManager,定义一个TransactionAwarePersistenceManagerFactoryProxy(包括在spring中)在你的目标PersistenceManagerFactory之前,然后传递一个代理的引用到你的DAO中如下面的例子:

<beans>

    <bean id="myPmfProxy"

            class="org.springframework.orm.jdo.TransactionAwarePersistenceManagerFactoryProxy">

        <property name="targetPersistenceManagerFactory" ref="myPmf"/>

    </bean>

    <bean id="myProductDao" class="product.ProductDaoImpl">

        <property name="persistenceManagerFactory" ref="myPmfProxy"/>

    </bean>

</beans>

Your data access code will receive a transactional PersistenceManager (if any) from the PersistenceManagerFactory.getPersistenceManager() method that it calls. The latter method call goes through the proxy, which first checks for a current transactional PersistenceManager before getting a new one from the factory. Any close() calls on the PersistenceManager are ignored in case of a transactional PersistenceManager.

你的数据访问代码将获得一个事务PersistenceManager(如果可以的话)来自PersistenceManagerFactory.getPersistenceManager方法。后面的方法调用通过代理,首先会检查当前的事务PersistenceManager在获得一个来自工程的新的之前。任何close的调用对于PersistenceManager被忽略由于事务的PersistenceManager。

If your data access code always runs within an active transaction (or at least within active transaction synchronization), it is safe to omit the PersistenceManager.close() call and thus the entire finally block, which you might do to keep your DAO implementations concise:

如果你的数据访问代码通过一个激活事务来运行(或至少激活事务同步),省略PersistenceManager.close()调用是安全的包括整个finally块中,你可以保持DAO实现的简洁:

public class ProductDaoImpl implements ProductDao {

    private PersistenceManagerFactory persistenceManagerFactory;

    public void setPersistenceManagerFactory(PersistenceManagerFactory pmf) {

        this.persistenceManagerFactory = pmf;

    }

    public Collection loadProductsByCategory(String category) {

        PersistenceManager pm = this.persistenceManagerFactory.getPersistenceManager();

        Query query = pm.newQuery(Product.class, "category = pCategory");

        query.declareParameters("String pCategory");

        return query.execute(category);

    }

}

With such DAOs that rely on active transactions, it is recommended that you enforce active transactions through turning off TransactionAwarePersistenceManagerFactoryProxy’s `allowCreate flag:

这样的DAO依赖激活事务,推荐你强制激活事务通过关闭TransactionAwarePersistenceManagerFactoryProxy’s的allowCreate标志:

<beans>

    <bean id="myPmfProxy"

            class="org.springframework.orm.jdo.TransactionAwarePersistenceManagerFactoryProxy">

        <property name="targetPersistenceManagerFactory" ref="myPmf"/>

        <property name="allowCreate" value="false"/>

    </bean>

    <bean id="myProductDao" class="product.ProductDaoImpl">

        <property name="persistenceManagerFactory" ref="myPmfProxy"/>

    </bean>

</beans>

The main advantage of this DAO style is that it depends on JDO API only; no import of any Spring class is required. This is of course appealing from a non-invasiveness perspective, and might feel more natural to JDO developers.

这个DAO风格的主要好处是他只依赖JDO的API,不需要导入任何spring的依赖库。这当然符合没有侵入的风格,并且将更加自然对于JDO的开发者。

However, the DAO throws plain JDOException (which is unchecked, so does not have to be declared or caught), which means that callers can only treat exceptions as fatal, unless you want to depend on JDO’s own exception structure. Catching specific causes such as an optimistic locking failure is not possible without tying the caller to the implementation strategy. This trade off might be acceptable to applications that are strongly JDO-based and/or do not need any special exception treatment.

然而,dao抛出普通的JDOException(是非检查的,不需要你定义或捕获),意味着调用者只需要处理致命这样的异常,除非你希望依赖JDO的异常结构。捕获特定的异常例如一个乐观锁失败是不可能的除非绑定调用者和实现策略。这种权衡对于应用时基于JDO不需要特定的异常处理。

In summary, you can DAOs based on the plain JDO API, and they can still participate in Spring-managed transactions. This strategy might appeal to you if you are already familiar with JDO. However, such DAOs throw plain JDOException, and you would have to convert explicitly to Spring’s DataAccessException (if desired).

总结一下,你可以让DAO基于普通的JDO的API,并且他们可以参与spring管理的事务。这种策略可以使用于你如果你十分熟悉JDO。然而,这样的DAO会抛出普通的JDOException并且你必须将其明确转换为spring的DataAccessException(如果期望这么做)。

20.4.3 Transaction management

事务管理

[Note]

注意

You are strongly encouraged to read Section 17.5, “Declarative transaction management” if you have not done so, to get a more detailed coverage of Spring’s declarative transaction support.

我们建议你阅读17.5节,“声明式事务管理”如果你没有这么做,为了获得更多有关spring的声明式事务管理的支持。

To execute service operations within transactions, you can use Spring’s common declarative transaction facilities. For example:

为了执行带有事务的服务操作,你可以使用spring的通用声明式事务功能。例如:

<?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:aop="http://www.springframework.org/schema/aop"

    xmlns:tx="http://www.springframework.org/schema/tx"

    xsi:schemaLocation="

        http://www.springframework.org/schema/beans

        http://www.springframework.org/schema/beans/spring-beans.xsd

        http://www.springframework.org/schema/tx

        http://www.springframework.org/schema/tx/spring-tx.xsd

        http://www.springframework.org/schema/aop

        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="myTxManager" class="org.springframework.orm.jdo.JdoTransactionManager">

        <property name="persistenceManagerFactory" ref="myPmf"/>

    </bean>

    <bean id="myProductService" class="product.ProductServiceImpl">

        <property name="productDao" ref="myProductDao"/>

    </bean>

    <tx:advice id="txAdvice" transaction-manager="txManager">

        <tx:attributes>

            <tx:method name="increasePrice*" propagation="REQUIRED"/>

            <tx:method name="someOtherBusinessMethod" propagation="REQUIRES_NEW"/>

            <tx:method name="*" propagation="SUPPORTS" read-only="true"/>

        </tx:attributes>

    </tx:advice>

    <aop:config>

        <aop:pointcut id="productServiceMethods"

                expression="execution(* product.ProductService.*(..))"/>

        <aop:advisor advice-ref="txAdvice" pointcut-ref="productServiceMethods"/>

    </aop:config>

</beans>

JDO requires an active transaction to modify a persistent object. The non-transactional flush concept does not exist in JDO, in contrast to Hibernate. For this reason, you need to set up the chosen JDO implementation for a specific environment. Specifically, you need to set it up explicitly for JTA synchronization, to detect an active JTA transaction itself. This is not necessary for local transactions as performed by Spring’s JdoTransactionManager, but it is necessary to participate in JTA transactions, whether driven by Spring’s JtaTransactionManager or by EJB CMT and plain JTA.

JDO要求一个激活事务来修改一个持久化object。非事务的刷新概念不存在于JDO中,而在Hibernate中存在。由于这种原因,你需要设置选择JDO实现对于特定的环境。特定的,你需要明确设置对于JTA的同步,为了检测激活JTA事务本身。作为spring的JdoTransactionManager来说这不是必须的对于本地的事务,不管是基于spring的JtaTransactionManager或EJB CMT和普通的JTA。

JdoTransactionManager is capable of exposing a JDO transaction to JDBC access code that accesses the same JDBC DataSource, provided that the registered JdoDialect supports retrieval of the underlying JDBC Connection. This is the case for JDBC-based JDO 2.0 implementations by default.

JdoTransactionManager可以暴露一个JDO事务对于JDBC访问代码来访问相同的JDBC数据源,提供注册的JdoDialect支持恢复底层的JDBC连接。这是由于基于JDBC的JDO2.0实现是默认的。

20.4.4 JdoDialect

As an advanced feature, both LocalPersistenceManagerFactoryBean and JdoTransactionManager support a custom JdoDialect that can be passed into the jdoDialect bean property. Using a JdoDialect implementation, you can enable advanced features supported by Spring, usually in a vendor-specific manner:

作为高级的特性,LocalPersistenceManagerFactoryBean和JdoTransactionManager支持一个自定义的JdoDialect可以传递通过jdoDialect的bean属性。使用JdoDialect的实现,你可以允许高级的spring的特性支持,通常以特定供应商的方式。

    Applying specific transaction semantics such as custom isolation level or transaction timeout

应用特定事务语义例如自定义隔离级别或事务超时

    Retrieving the transactional JDBC Connection for exposure to JDBC-based DAOs

获得事务的JDBC连接为了暴露给基于JDBC的DAO

    Applying query timeouts, which are automatically calculated from Spring-managed transaction timeouts

应用查询超时,可以自动计算来自spring管理的事务超时

    Eagerly flushing a PersistenceManager, to make transactional changes visible to JDBC-based data access code

强烈刷新PersistenceManager为了使得事务改变对于基于JDBC的数据访问代码可见

    Advanced translation of JDOExceptions to Spring DataAccessExceptions

对于spring的DataAccessExceptions的JDOExceptions的高级翻译

See the JdoDialect javadocs for more details on its operations and how to use them within Spring’s JDO support.

详见JdoDialect的javadocs关于他的操作及如何使用spring的JDO支持。

20.5 JPA

The Spring JPA, available under the org.springframework.orm.jpa package, offers comprehensive support for the Java Persistence API in a similar manner to the integration with Hibernate or JDO, while being aware of the underlying implementation in order to provide additional features.

spring的JPA在org.springframework.orm.jpa包中,提供了复杂的支持对于Java的持久化API在相同的情况通过Hibernate或JDO,可以被实现已知为了提供额外的特性。

20.5.1 Three options for JPA setup in a Spring environment

对于在spring环境中的JPA设置选项

The Spring JPA support offers three ways of setting up the JPA EntityManagerFactory that will be used by the application to obtain an entity manager.

spring的JPA支持提供了三种设置方式对于EntityManagerFactory可以被应用来使用来获得一个实例管理器。

LocalEntityManagerFactoryBean

[Note]

注意

Only use this option in simple deployment environments such as stand-alone applications and integration tests.

只用使用这个选项在简单的部署环境例如独立的应用或集成测试中。

The LocalEntityManagerFactoryBean creates an EntityManagerFactory suitable for simple deployment environments where the application uses only JPA for data access. The factory bean uses the JPA PersistenceProvider autodetection mechanism (according to JPA’s Java SE bootstrapping) and, in most cases, requires you to specify only the persistence unit name:

LocalEntityManagerFactoryBean创建一个EntityManagerFactory使用于简单的部署环境当应用只使用JPA对于数据访问。工厂bean使用JPA的PersistenceProvider自动探测策略(遵循JPA的JavaSE的启动)并且,在大部分情况,要求你来指定持久化单元名字:

<beans>

    <bean id="myEmf" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">

        <property name="persistenceUnitName" value="myPersistenceUnit"/>

    </bean>

</beans>

This form of JPA deployment is the simplest and the most limited. You cannot refer to an existing JDBC DataSource bean definition and no support for global transactions exists. Furthermore, weaving (byte-code transformation) of persistent classes is provider-specific, often requiring a specific JVM agent to specified on startup. This option is sufficient only for stand-alone applications and test environments, for which the JPA specification is designed.

这种JPA部署的形式是最简单的也是最多限制的。你不能引用已有的JDBC数据源的bean定义和不支持全局的事务支持。此外,处理(二进制转换)持久化类是特定,需要一个特殊的JVM代理在启动时指定。这个选项对于独立的应用和测试环境是足够的,特定于对于JPA的设计。

Obtaining an EntityManagerFactory from JNDI

从JNDI中获得一个EntityManagerFactory

[Note]

注意

Use this option when deploying to a Java EE 5 server. Check your server’s documentation on how to deploy a custom JPA provider into your server, allowing for a different provider than the server’s default.

当部署在JavaEE5服务器的时候使用这个选项。检查你的服务器文档关于如何部署一个自定义的JPA的提供者对于你的服务器,允许你使用不同的提供而不只是使用服务器默认的。

Obtaining an EntityManagerFactory from JNDI (for example in a Java EE 5 environment), is simply a matter of changing the XML configuration:

从JNDI中获得一个EntityManagerFactory(例如在JavaEE5的环境中),是简单的只要改变xml的配置:

<beans>

    <jee:jndi-lookup id="myEmf" jndi-name="persistence/myPersistenceUnit"/>

</beans>

This action assumes standard Java EE 5 bootstrapping: the Java EE server autodetects persistence units (in effect, META-INF/persistence.xml files in application jars) and persistence-unit-ref entries in the Java EE deployment descriptor (for example, web.xml) and defines environment naming context locations for those persistence units.

这个行为假设标准的JavaEE5引导:JavaEE服务器自动探测持久化单元(实际上,在应用jar中的META-INF/persistence.xml文件)并且持久化单元引用实体在JavaEE的部署描述符(例如,web.xml)并且定义环境命名上下文位置对于这些持久化单元。

In such a scenario, the entire persistence unit deployment, including the weaving (byte-code transformation) of persistent classes, is up to the Java EE server. The JDBC DataSource is defined through a JNDI location in the META-INF/persistence.xml file; EntityManager transactions are integrated with the server’s JTA subsystem. Spring merely uses the obtained EntityManagerFactory, passing it on to application objects through dependency injection, and managing transactions for the persistence unit, typically through JtaTransactionManager.

在这种场景中,整个持久化单元部署,包括处理(二进制转换)持久化类,对于JavaEE服务器。JDBC数据源被定义通过JNDI的位置在META-INF/persistence.xml文件中;EntityManager事务被集成到服务器的JTA子系统中。spring很少使用获得EntityManagerFactory,可以传递给应用object通过依赖注入并且管理事务对于持久化单元,通常通过JtaTransactionManager。

If multiple persistence units are used in the same application, the bean names of such JNDI-retrieved persistence units should match the persistence unit names that the application uses to refer to them, for example, in @PersistenceUnit and @PersistenceContext annotations.

如果多个持久化单元被使用在相同的应用中,bean的名字例如JNDI的单元应当匹配持久化单元名字当应用引用他们的时候,例如,在@PersistenceUnit和@PersistenceContext注解中。

LocalContainerEntityManagerFactoryBean

[Note]

注意

Use this option for full JPA capabilities in a Spring-based application environment. This includes web containers such as Tomcat as well as stand-alone applications and integration tests with sophisticated persistence requirements.

在基于spring管理的应用环境使用这个选项对于全功能的JPA功能。这包括web容器例如Tomcat和独立的应用和集成测试包含负责的持久化请求。

The LocalContainerEntityManagerFactoryBean gives full control over EntityManagerFactory configuration and is appropriate for environments where fine-grained customization is required. The LocalContainerEntityManagerFactoryBean creates a PersistenceUnitInfo instance based on the persistence.xml file, the supplied dataSourceLookup strategy, and the specified loadTimeWeaver. It is thus possible to work with custom data sources outside of JNDI and to control the weaving process. The following example shows a typical bean definition for a LocalContainerEntityManagerFactoryBean:

LocalContainerEntityManagerFactoryBean给予了全面的控制对于EntityManagerFactory的配置并且适用于细粒度的自定义需求。LocalContainerEntityManagerFactoryBean创建一个PersistenceUnitInfo实例基于persistence.xml文件,提供dataSourceLookup策略并且指定loadTimeWeaver。可以使用自定义数据源工作在JNDI之外并且控制处理程序。下面的例子展示了一个典型的bean定义对于LocalContainerEntityManagerFactoryBean:

<beans>

    <bean id="myEmf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

        <property name="dataSource" ref="someDataSource"/>

        <property name="loadTimeWeaver">

            <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>

        </property>

    </bean>

</beans>

The following example shows a typical persistence.xml file:

下面的例子展示了一个典型的persistence.xml文件

<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">

    <persistence-unit name="myUnit" transaction-type="RESOURCE_LOCAL">

        <mapping-file>META-INF/orm.xml</mapping-file>

        <exclude-unlisted-classes/>

    </persistence-unit>

</persistence>

[Note]

注意

The <exclude-unlisted-classes/> shortcut indicates that no scanning for annotated entity classes is supposed to occur. An explicit 'true' value specified - <exclude-unlisted-classes>true</exclude-unlisted-classes/> - also means no scan. <exclude-unlisted-classes>false</exclude-unlisted-classes/> does trigger a scan; however, it is recommended to simply omit the exclude-unlisted-classes element if you want entity class scanning to occur.

<exclude-unlisted-classes/>简写展示了不在扫描注解实体类是支持的。一个冲突指定为true——<exclude-unlisted-classes>true</exclude-unlisted-classes/>————也意味着不会扫描。<exclude-unlisted-classes>false</exclude-unlisted-classes/>会引起扫描,然而,他不会简单的省略exclude-unlisted-classes元素如果你希望整个类被扫描。

Using the LocalContainerEntityManagerFactoryBean is the most powerful JPA setup option, allowing for flexible local configuration within the application. It supports links to an existing JDBC DataSource, supports both local and global transactions, and so on. However, it also imposes requirements on the runtime environment, such as the availability of a weaving-capable class loader if the persistence provider demands byte-code transformation.

使用LocalContainerEntityManagerFactoryBean是最有力的JPA设置选项,允许方便的本地配置对于应用。他支持一个已有的JDBC数据源,支持本地和全局事务等等。然而,他也需要运行时环境,例如可用的代码包裹类如果持久化提供通过二进制转换。

This option may conflict with the built-in JPA capabilities of a Java EE 5 server. In a full Java EE 5 environment, consider obtaining your EntityManagerFactory from JNDI. Alternatively, specify a custom persistenceXmlLocation on your LocalContainerEntityManagerFactoryBean definition, for example, META-INF/my-persistence.xml, and only include a descriptor with that name in your application jar files. Because the Java EE 5 server only looks for default META-INF/persistence.xml files, it ignores such custom persistence units and hence avoid conflicts with a Spring-driven JPA setup upfront. (This applies to Resin 3.1, for example.)

这个选项可能发生冲突在内置的JavaEE5服务器中内置的JPA功能。在全面的JavaEE5环境中,考虑从JNDI中获得你的EntityManagerFactory。作为替代,定义一个自定义的persistenceXmlLocation在你的LocalContainerEntityManagerFactoryBean定义中,例如,META-INF/my-persistence.xml,并且只包括一个描述符在你应用的jar文件的名字中。因为JavaEE5服务器只会寻找默认的META-INF/persistence.xml文件,他忽略这样的自定义持久化单元因此提前避免冲突和基于spring的JPA设置。(这适用于Resin3.1,作为例子。)

When is load-time weaving required?

什么时候需要加载时处理?

Not all JPA providers require a JVM agent ; Hibernate is an example of one that does not. If your provider does not require an agent or you have other alternatives, such as applying enhancements at build time through a custom compiler or an ant task, the load-time weaver should not be used.

并不是所有的JPA提供了一个JVM代理;Hibernate就是一个例子就是没有提供代理。如果你提供者没有需要一个代理或你有一个其他的选择,例如在构建时的应用增强通过一个自定义的编译器或一个ant认为,加载时处理不应被使用。

The LoadTimeWeaver interface is a Spring-provided class that allows JPA ClassTransformer instances to be plugged in a specific manner, depending whether the environment is a web container or application server. Hooking ClassTransformers through an agent typically is not efficient. The agents work against the entire virtual machine and inspect every class that is loaded, which is usually undesirable in a production server environment.

LoadTimeWeaver接口是spring提供的类允许JPA的ClassTransformer实例组装以特定的行为,依赖于环境是否是一个web容器或应用服务器。挂钩ClassTransformers通过一个代理通常是不够的。代理配合整个虚拟机工作并且检查每个加载的类,通常不会在生产环境中使用。

Spring provides a number of LoadTimeWeaver implementations for various environments, allowing ClassTransformer instances to be applied only per class loader and not per VM.

spring提供了一系列的LoadTimeWeaver实现对于不同的环境,允许ClassTransformer实例被应用于每个类加载器而不是每个虚拟机。

Refer to the section called “Spring configuration” in the AOP chapter for more insight regarding the LoadTimeWeaver implementations and their setup, either generic or customized to various platforms (such as Tomcat, WebLogic, GlassFish, Resin and JBoss).

引用的章节名字为“spring配置”在AOP章节中关于更多检查LoadTimeWeaver的实现和他的启动,不管是通用还是自定义对于不同的平台(例如Tomcat、WebLogic、GlassFish、Resin和JBoss)。

As described in the aforementioned section, you can configure a context-wide LoadTimeWeaver using the @EnableLoadTimeWeaving annotation of context:load-time-weaver XML element. Such a global weaver is picked up by all JPA LocalContainerEntityManagerFactoryBeans automatically. This is the preferred way of setting up a load-time weaver, delivering autodetection of the platform (WebLogic, GlassFish, Tomcat, Resin, JBoss or VM agent) and automatic propagation of the weaver to all weaver-aware beans:

在之前的章节中描述过,你可以配置一个上下文的LoadTimeWeaver使用@EnableLoadTimeWeaving注解来自context:load-time-weaver的xml元素。例如一个全局的处理器来自动获得JPA的LocalContainerEntityManagerFactoryBeans。这是一个推荐的方法来设置加载时处理器,发表平台的自动探测(WebLogic、GlassFish、Tomcat、Resin、JBoss或虚拟机代理)和自动处理器传播对于所有的处理器理解的bean:

<context:load-time-weaver/>

<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

    ...

</bean>

However, if needed, one can manually specify a dedicated weaver through the loadTimeWeaver property:

然而,如果需要,可以手动指定一个专用的处理器通过loadTimeWeaver属性:

<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

    <property name="loadTimeWeaver">

        <bean class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver"/>

    </property>

</bean>

No matter how the LTW is configured, using this technique, JPA applications relying on instrumentation can run in the target platform (ex: Tomcat) without needing an agent. This is important especially when the hosting applications rely on different JPA implementations because the JPA transformers are applied only at class loader level and thus are isolated from each other.

不管如何设置LTW,使用这种技术,JPA应用依赖的功能可以运行在目标平台(例如Tomcat)而不需要一个代理。这是很重要的当处理应用依赖于不同的JPA实现因为JPA转换被应用于类加载器级别和其他是隔离的。

Dealing with multiple persistence units

处理多个持久化单元

For applications that rely on multiple persistence units locations, stored in various JARS in the classpath, for example, Spring offers the PersistenceUnitManager to act as a central repository and to avoid the persistence units discovery process, which can be expensive. The default implementation allows multiple locations to be specified that are parsed and later retrieved through the persistence unit name. (By default, the classpath is searched for META-INF/persistence.xml files.)

对于应用他们依赖多个持久化单元的位置,存储在不同的JARS在classpath中,例如,spring提供了PersistenceUnitManager来处理一个中心的库并且避免持久化单元探测程序,可以被扩展。默认的实现允许多个位置被指定来解析和后续接收持久化单元名。(默认,classpath被查找对于META-INF/persistence.xml文件。)

<bean id="pum" class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">

    <property name="persistenceXmlLocations">

        <list>

            <value>org/springframework/orm/jpa/domain/persistence-multi.xml</value>

            <value>classpath:/my/packagecustom-persistence.xml</value>

            <value>classpath*:META-INF/persistence.xml</value>

        </list>

    </property>

    <property name="dataSources">

        <map>

            <entry key="localDataSource" value-ref="local-db"/>

            <entry key="remoteDataSource" value-ref="remote-db"/>

        </map>

    </property>

    <!-- if no datasource is specified, use this one -->

    <property name="defaultDataSource" ref="remoteDataSource"/>

</bean>

<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

    <property name="persistenceUnitManager" ref="pum"/>

    <property name="persistenceUnitName" value="myCustomUnit"/>

</bean>

The default implementation allows customization of the PersistenceUnitInfo instances, before they are fed to the JPA provider, declaratively through its properties, which affect all hosted units, or programmatically, through the PersistenceUnitPostProcessor, which allows persistence unit selection. If no PersistenceUnitManager is specified, one is created and used internally by LocalContainerEntityManagerFactoryBean.

默认的实现允许PersistenceUnitInfo实例的自定义,之前他们可以被处理通过JPA的提供者,通过属性来定义,影响所有的持有单元,或编程通过PersistenceUnitPostProcessor,允许持久化单元选择。如果没有指定PersistenceUnitManager,可以创建并使用内置的LocalContainerEntityManagerFactoryBean。

20.5.2 Implementing DAOs based on plain JPA

基于普通的JPA实现DAO

[Note]

注意

Although EntityManagerFactory instances are thread-safe, EntityManager instances are not. The injected JPA EntityManager behaves like an EntityManager fetched from an application server’s JNDI environment, as defined by the JPA specification. It delegates all calls to the current transactional EntityManager, if any; otherwise, it falls back to a newly created EntityManager per operation, in effect making its usage thread-safe.

尽管EntityManagerFactory实例是线程安全的,但是EntityManager实例却不是。注入JPA的EntityManager行为类似于一个EntityManager从一个应用服务器的JNDI环境中获取,通过JPA规范来定义。他代表所有调用当前的事务EntityManager,此外,他回调一个新创建的EntityManager在每次的操作中,使得他是线程安全的。

It is possible to write code against the plain JPA without any Spring dependencies, by using an injected EntityManagerFactory or EntityManager. Spring can understand @PersistenceUnit and @PersistenceContext annotations both at field and method level if a PersistenceAnnotationBeanPostProcessor is enabled. A plain JPA DAO implementation using the @PersistenceUnit annotation might look like this:

可以写代码使用普通的JPA而不依赖于spring,通过使用EntityManagerFactory或EntityManager。spring可以理解@PersistenceUnit和@PersistenceContext注解在field和方法级别如果允许PersistenceAnnotationBeanPostProcessor的话。一个普通的JPA的DAO实现使用@PersistenceUnit注解类似于:

public class ProductDaoImpl implements ProductDao {

    private EntityManagerFactory emf;

    @PersistenceUnit

    public void setEntityManagerFactory(EntityManagerFactory emf) {

        this.emf = emf;

    }

    public Collection loadProductsByCategory(String category) {

        EntityManager em = this.emf.createEntityManager();

        try {

            Query query = em.createQuery("from Product as p where p.category = ?1");

            query.setParameter(1, category);

            return query.getResultList();

        }

        finally {

            if (em != null) {

                em.close();

            }

        }

    }

}

The DAO above has no dependency on Spring and still fits nicely into a Spring application context. Moreover, the DAO takes advantage of annotations to require the injection of the default EntityManagerFactory:

上面的DAO没有依赖spring并且依然可以用在spring的应用上下文中。此外,dao使用了注解的优点需要注入默认的EntityManagerFactory:

<beans>

    <!-- bean post-processor for JPA annotations -->

    <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>

    <bean id="myProductDao" class="product.ProductDaoImpl"/>

</beans>

As an alternative to defining a PersistenceAnnotationBeanPostProcessor explicitly, consider using the Spring context:annotation-config XML element in your application context configuration. Doing so automatically registers all Spring standard post-processors for annotation-based configuration, including CommonAnnotationBeanPostProcessor and so on.

作为替代明确定义一个PersistenceAnnotationBeanPostProcessor,考虑使用spring的context:annotation-config的xml元素在你应用上下文的配置中。这样可以自动注册所有spring标准的后处理器对于基于注解的配置,包括CommonAnnotationBeanPostProcessor等等。

<beans>

    <!-- post-processors for all standard config annotations -->

    <context:annotation-config/>

    <bean id="myProductDao" class="product.ProductDaoImpl"/>

</beans>

The main problem with such a DAO is that it always creates a new EntityManager through the factory. You can avoid this by requesting a transactional EntityManager (also called "shared EntityManager" because it is a shared, thread-safe proxy for the actual transactional EntityManager) to be injected instead of the factory:

主要的问题关于dao是会经常创建一个新的EntityManager通过工厂。你可以避免这个问题通过请求一个事务EntityManager(也叫做"shared EntityManager"因为他是共享的、线程安全的代理对于实际的事务EntityManager)可以被注入到工厂中:

public class ProductDaoImpl implements ProductDao {

    @PersistenceContext

    private EntityManager em;

    public Collection loadProductsByCategory(String category) {

        Query query = em.createQuery("from Product as p where p.category = :category");

        query.setParameter("category", category);

        return query.getResultList();

    }

}

The @PersistenceContext annotation has an optional attribute type, which defaults to PersistenceContextType.TRANSACTION. This default is what you need to receive a shared EntityManager proxy. The alternative, PersistenceContextType.EXTENDED, is a completely different affair: This results in a so-called extended EntityManager, which is not thread-safe and hence must not be used in a concurrently accessed component such as a Spring-managed singleton bean. Extended EntityManagers are only supposed to be used in stateful components that, for example, reside in a session, with the lifecycle of the EntityManager not tied to a current transaction but rather being completely up to the application.

@PersistenceContext注解有一个可选属性type,默认值是PersistenceContextType.TRANSACTION。默认是你需要收到一个共享的EntityManager代理。另外一个选项是PersistenceContextType.EXTENDED,一个完全不同的方式:也叫做扩展EntityManager,不是线程安全的,所以必须安全的访问例如作为spring管理的单例bean。扩展EntityManager只支持使用在有状态的组件,例如,在session中跟随EntityManager的生命周期但是没有绑定到当前的事务而是被应用所管理。

Method- and field-level Injection

方法和field级别的注入

Annotations that indicate dependency injections (such as @PersistenceUnit and @PersistenceContext) can be applied on field or methods inside a class, hence the expressions method-level injection and field-level injection. Field-level annotations are concise and easier to use while method-level allows for further processing of the injected dependency. In both cases the member visibility (public, protected, private) does not matter.

注解指示依赖注入(例如@PersistenceUnit和@PersistenceContext)可以被应用于类中的field或方法,因此表达式方法级别注入和field界别的注入。field级别的注入是简明的并且方便使用而方法级别的注入允许对依赖注入进行更进一步的处理。两种方式都是可见的(public、protected、private)都可以。

What about class-level annotations?

类级别的注解如何?

On the Java EE 5 platform, they are used for dependency declaration and not for resource injection.

在JavaEE5平台,他们被使用依赖定义而不是资源注入。

The injected EntityManager is Spring-managed (aware of the ongoing transaction). It is important to note that even though the new DAO implementation uses method level injection of an EntityManager instead of an EntityManagerFactory, no change is required in the application context XML due to annotation usage.

注入EntityManager是spring管理的(包括不明的事务)。注意到这点是重要的通过新的dao实现使用方法级别的注入对于EntityManager代替EntityManagerFactory,对于应用上下文的xml是不需要改变的由于注解的使用。

The main advantage of this DAO style is that it only depends on Java Persistence API; no import of any Spring class is required. Moreover, as the JPA annotations are understood, the injections are applied automatically by the Spring container. This is appealing from a non-invasiveness perspective, and might feel more natural to JPA developers.

这种dao风格主要的优势是只依赖于Java持久化API,没有导入任何spring的类。此外,作为JPA注解是易于理解的,注入由spring的容器自动完成。这符合不侵入的方式,并且可以对于JPA的开发者来说是很自然的。

20.5.3 Transaction Management

事务管理

[Note]

注意

You are strongly encouraged to read Section 17.5, “Declarative transaction management” if you have not done so, to get a more detailed coverage of Spring’s declarative transaction support.

我们强烈建议你阅读17.5节,“声明式事务管理”如果你没有读过的话,为了获得有关spring对于声明式事务管理更多的细节描述。

To execute service operations within transactions, you can use Spring’s common declarative transaction facilities. For example:

为了执行服务操作通过事务,你可以使用spring的通用声明式事务策略。例如:

<?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:aop="http://www.springframework.org/schema/aop"

    xmlns:tx="http://www.springframework.org/schema/tx"

    xsi:schemaLocation="

        http://www.springframework.org/schema/beans

        http://www.springframework.org/schema/beans/spring-beans.xsd

        http://www.springframework.org/schema/tx

        http://www.springframework.org/schema/tx/spring-tx.xsd

        http://www.springframework.org/schema/aop

        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="myTxManager" class="org.springframework.orm.jpa.JpaTransactionManager">

        <property name="entityManagerFactory" ref="myEmf"/>

    </bean>

    <bean id="myProductService" class="product.ProductServiceImpl">

        <property name="productDao" ref="myProductDao"/>

    </bean>

    <aop:config>

        <aop:pointcut id="productServiceMethods" expression="execution(* product.ProductService.*(..))"/>

        <aop:advisor advice-ref="txAdvice" pointcut-ref="productServiceMethods"/>

    </aop:config>

    <tx:advice id="txAdvice" transaction-manager="myTxManager">

        <tx:attributes>

            <tx:method name="increasePrice*" propagation="REQUIRED"/>

            <tx:method name="someOtherBusinessMethod" propagation="REQUIRES_NEW"/>

            <tx:method name="*" propagation="SUPPORTS" read-only="true"/>

        </tx:attributes>

    </tx:advice>

</beans>

Spring JPA allows a configured JpaTransactionManager to expose a JPA transaction to JDBC access code that accesses the same JDBC DataSource, provided that the registered JpaDialect supports retrieval of the underlying JDBC Connection. Out of the box, Spring provides dialects for the EclipseLink, Hibernate and OpenJPA JPA implementations. See the next section for details on the JpaDialect mechanism.

spring的JPA允许一个配置JpaTransactionManager来获得JPA事务对于JDBC访问代码访问相同的JDBC数据源,提供了注册的JpaDialect来支持获得底层的JDBC连接。之外,spring提供了支持对于EclipseLink、Hibernate和OpenJPA的JPA实现。参见下一节中有关JpaDialect策略的细节。

20.5.4 JpaDialect

As an advanced feature JpaTransactionManager and subclasses of AbstractEntityManagerFactoryBean support a custom JpaDialect, to be passed into the jpaDialect bean property. A JpaDialect implementation can enable some advanced features supported by Spring, usually in a vendor-specific manner:

一个高级特性JpaTransactionManager和AbstractEntityManagerFactoryBean的子类支持自定义的JpaDialect,可以传递给jpaDialect的bean属性。一个JpaDialect实现可以允许spring支持的一些高级的特性,通常在一个特定供应商的情况。

    Applying specific transaction semantics such as custom isolation level or transaction timeout)

应用特定的事务语义例如一个自定义的隔离级别或事务超时

    Retrieving the transactional JDBC Connection for exposure to JDBC-based DAOs)

获得事务的JDBC连接对于暴露基于JDBC的dao

    Advanced translation of PersistenceExceptions to Spring DataAccessExceptions

高级的翻译对于PersistenceExceptions异常为spring的DataAccessExceptions

This is particularly valuable for special transaction semantics and for advanced translation of exception. The default implementation used ( DefaultJpaDialect) does not provide any special capabilities and if the above features are required, you have to specify the appropriate dialect.

这是特殊的值对于特定的事务语义和高级的异常翻译。默认的实现使用(DefaultJpaDialect)没有支持任何特定的功能如果上面的特性被要求的话,你必须指定适当的方言。

See the JpaDialect javadocs for more details of its operations and how they are used within Spring’s JPA support.

参见JpaDialect的javad来了解更多相关的操作和如何使用spring的JPA支持。