vlambda博客
学习文章列表

读书笔记《spring-5-design-patterns》第 8 章使用 Spring ORM 和事务实现模式访问数据库

第 8 章使用 Spring ORM 和事务实现模式访问数据库

第7章中,使用 Spring 和 JDBC 模板模式访问数据库< /span>,我们学习了如何使用 JBDC 访问数据库,以及 Spring 如何使用模板模式和回调将样板代码从开发人员端删除到框架中。在本章中,我们将学习使用对象关系映射ORM) 框架和跨应用程序管理事务。

我儿子 Arnav 一岁半的时候,他经常玩假手机。但随着他的成长,他的需求也超过了智能手机的虚拟手机。

类似地,当您的应用程序具有业务层的一小部分数据时,JDBC 工作正常,但随着应用程序的增长和变得越来越复杂,将表映射到应用程序中的对象变得困难。 JDBC 是数据访问世界的虚拟小电话。但是对于复杂的应用程序,我们需要能够将对象属性映射到数据库列的对象关系映射解决方案。我们还需要在数据访问层为我们的应用程序提供更复杂的平台,这些平台为我们创建独立于数据库技术的查询和语句,并且我们可以通过声明或编程方式进行定义。

许多 ORM 框架可用于在应用程序的数据访问层提供服务。此类服务的示例包括对象关系映射、数据的延迟加载、数据的急切加载、级联等。这些 ORM 服务使您免于编写大量用于错误处理和管理应用程序资源的代码。 ORM 框架减少了开发时间,并有助于编写无错误的代码,让您只关注业务需求。 Spring 没有实现自己的 ORM 解决方案,但它提供了对许多持久性框架的支持,例如 Hibernate、Java Persistence API (JPA)、iBATIS 和 Java 数据对象 (JDO)。 Spring 还为 ORM 框架提供了 integration 点,以便我们可以轻松地将 ORM 框架集成到 Spring 应用程序中。

Spring 为您的应用程序中的所有这些技术提供支持。在本章中,我们将探讨 Spring 对 ORM 解决方案的支持,并涵盖以下主题:

  • ORM Framework and used patterns
    • The data access object pattern
    • Creating DAOs using the Factory design pattern in Spring
    • The Data Mapper pattern
    • The domain model pattern
    • Proxy for the lazy loading pattern
    • The Hibernate template pattern
  • Integrating Hibernate with Spring
    • Configuring Hibernate's SessionFactory in a Spring container
    • Implementing DAOs based on plain Hibernate API
    • Transaction management strategies in Spring
    • Declarative transaction implementation and demarcation
    • Programmatic transaction implementation and demarcation
    • Best practices for Spring ORM and transaction modules in the application

在继续讨论有关 ORM 框架的更多信息之前,让我们先看一下 数据访问层DAL) 的应用程序。

ORM 框架和使用的模式


Spring 提供对多个 ORM 框架的支持,例如 Hibernate、Java Persistence API (JPA )、iBATIS 和 Java 数据对象 (JDO )。通过在您的应用程序中使用任何 ORM 解决方案,您可以轻松地以 POJO 对象的形式从关系数据库中持久化和访问数据。 Spring ORM 模块是前面讨论的 Spring JDBC DAO 模块的扩展。 Spring 提供 ORM 模板,例如基于 JDBC 的模板,以在集成层或数据访问层工作。以下是 Spring Framework 支持的 ORM 框架和集成:

  • Hibernate
  • Java Persistence API
  • Java Data Objects
  • iBATIS
  • Data access object implementations
  • Transaction strategies

您可以使用 Spring 的依赖注入功能在您的应用程序中配置 ORM 解决方案。 Spring 还为您的数据访问应用程序中的 ORM 层添加了重要的增强功能。以下是使用 Spring 框架创建 ORM DAO 的好处:

  • Easier development and testing: Spring's IoC container manages the beans for ORM DAOs. You can easily swap the implementation of the DAO interface by using Spring's dependency injection feature. It also makes it easy to test persistence-related code in isolation.
  • Common data access exceptions: Spring provides a consistent data exception hierarchy to handle exceptions at the persistence layer. It wraps all the checked exceptions from the ORM tool, and converts these exceptions to unchecked general exceptions which are not related to any specific ORM solution and are DB-specific.
  • General resource management: Resources such as DataSource, DB connections, Hibernates SessionFactory, JPA EntityManagerFactory, and others are managed by the Spring IoC container. Spring also manages transactions--local or global--using JTA.
  • Integrated transaction management: Spring provides declarative and programmatic transaction management in your application. For declarative transaction management, you can use the @Transactional annotation.

Spring 与 ORM 解决方案集成的主要方法是应用程序层之间的松散耦合;即业务层和数据访问层。它是清晰的应用分层,并且独立于任何特定的数据库和事务技术。应用程序中的业务服务不再依赖于数据访问和特定的事务策略。由于 Spring 管理集成层中使用的资源,因此您无需为特定的数据访问技术查找资源。 Spring 为 ORM 解决方案提供了模板以删除样板代码,并为所有 ORM 解决方案提供了一致的方法。

第7章中,使用 Spring 和 JDBC 模板模式访问数据库< /span>,您看到了 Spring 如何解决应用程序中集成层的两个主要问题。第一个问题是用于管理应用程序资源的冗余代码,第二个问题是处理检查开发时应用程序中的异常。同样,Spring ORM 模块也为这两个问题提供了解决方案,我们将在下面的部分中讨论。

资源和事务管理

在 Spring JDBC 模块中,连接处理、语句处理、异常处理等资源由 Spring 的 JdbcTemplate 管理。它还将数据库的 SQL 错误代码转换为有意义的未经检查的异常类。 Spring ORM 模块也是如此——Spring 通过使用各自的 Spring 事务管理器来管理企业应用程序中的本地事务和全局事务。 Spring 为所有受支持的 ORM 技术提供事务管理器。例如,Spring 为 Hibernate 提供了 Hibernate 事务管理器,为 JPA 提供了 JPA 事务管理器,并为全局或分布式事务提供了 JTA 支持。

一致的异常处理和翻译

在 Spring JDBC 模块中,Spring 提供了 DataAccessException 来处理所有类型的数据库特定的 SQL 错误代码,并生成有意义的异常类。在 Spring ORM 模块中,我们已经知道,Spring 支持在一个 DAO 中集成多个 ORM 解决方案,例如 Hibernate、JPA 或 JDO,这些持久化技术提供了自己的原生异常类,如 < code class="literal">HibernateException、PersistenceExceptionJDOException,具体取决于技术。 ORM 框架的这些原生异常是未经检查的异常,因此我们不必在应用程序中处理它们。 DAO 服务的调用者不能做具体的处理,除非应用程序是强基于 ORM 的,或者不需要任何特殊的异常处理。 Spring 在整个 ORM 框架中提供了一致的方法;您不需要为 Spring 应用程序中的任何 ORM 实现特定代码。它通过使用 @Repository 注释来启用异常翻译。如果 Spring 应用程序中的任何类使用 @Repository 注释进行注释,则该类符合 Spring DataAccessException 翻译的条件。以 AccountDaoImpl 类的以下代码为例:

    @Repository 
    public class AccountDaoImpl implements AccountDao { 
      // class body here... 
    } 
 
    <beans> 
      <!-- Exception translation bean post processor --> 
      <bean class="org.springframework.dao.annotation. PersistenceExceptionTranslationPostProcessor"/> 
      <bean id="accountDao" class="com.packt.patterninspring.chapter8. bankapp.dao.AccountDaoImpl"/> 
    </beans> 

正如您在前面的代码中看到的,PersistenceExceptionTranslationPostProcessor 类是一个 bean 后处理器,它会自动搜索所有异常翻译器和还建议在容器中使用 @Repository 注释注释的所有已注册 bean。它将发现的异常翻译器应用于那些带注释的 bean,这些翻译器可以拦截并在抛出的异常上应用适当的翻译。

让我们看看更多在 Spring ORM 模块中实现的设计模式,为企业应用程序的集成层提供最佳企业解决方案。

数据访问对象模式


数据访问对象DAO)模式是一个非常< J2EE 应用程序中持久层的 span>popular 设计模式。它将业务逻辑层和持久层分开。 DAO 模式基于面向对象的封装和抽象原则。使用 DAO 模式的上下文是根据底层供应商实现和存储类型(例如面向对象的数据库、平面文件、关系数据库等)访问和持久化数据。使用 DAO 模式,您可以创建一个 DAO 接口,并实现该 DAO 接口来抽象和封装对数据源的所有访问。此 DAO 实现管理数据库的资源,例如与数据源的连接。

DAO 接口对所有底层数据源机制都是非常通用的,不需要为底层持久性技术的任何变化而改变。此模式允许您采用任何不同的数据访问技术,而不会影响企业应用程序中的业务逻辑。让我们看下图来进一步了解 DAO 模式:

读书笔记《spring-5-design-patterns》第 8 章使用 Spring ORM 和事务实现模式访问数据库

正如您在上图中所见,以下 参与者 使用此模式:

  • BusinessObject: This object works on the business layer, and is a client for the data access layer. It requires data for business modeling, and for preparing Java objects for the helper or controllers in the application.
  • DataAccessObject: This is a primary object of the DAO pattern. This object hides all the low-level implementation of the underlying database implementation for the BusinessObject.
  • DataSource: This is also an object to contain all the low-level information about the underlying database implementation, such as an RDBMS, flat files, or XML.
  • TransferObject: This is also an object, and it is used as a data carrier. This object is used by DataAccessObject to return data to the business object.

我们来看下面的 DAO 模式示例,其中 AccountDao 是一个 DataAccessObject 接口,而 AccountDaoImplAccountDao 接口的实现类:

    public interface AccountDao { 
      Integer totalAccountsByBranch(String branchName); 
    } 
 
    public class AccountDaoImpl extends JdbcDaoSupport implements
    AccountDao { 
      @Override 
      public Integer totalAccountsByBranch(String branchName) { 
        String sql = "SELECT count(*) FROM Account WHERE branchName = 
        "+branchName; 
        return this.getJdbcTemplate().queryForObject(sql,   
        Integer.class); 
       } 
 
    } 

在 Spring 中使用工厂设计模式创建 DAO

众所周知,有很多设计模式在 Spring Framework 中发挥作用。如第2章中所述,GOF 设计模式概述 --核心设计模式,工厂模式是一种创建型设计模式,用于在不将底层逻辑暴露给客户端的情况下创建对象,并使用通用接口或抽象类将新对象分配给调用者.您可以使用 Factory 方法和抽象工厂设计模式使 DAO 模式高度灵活。

让我们看看在我们的示例中,我们在哪里实现了这种策略,其中工厂为单个数据库实现生成 DAO。请参考下图:

读书笔记《spring-5-design-patterns》第 8 章使用 Spring ORM 和事务实现模式访问数据库

您可以在上图中看到 AccountDao 对象是由 AccountDaoFactory< /span>,而 AccountDaoFactoryAccountDao 的工厂。我们可以随时更改底层数据库,这样我们就不需要更改业务代码——工厂负责这些事情,Spring提供支持维护bean工厂中的所有DAO以及DAO工厂中的所有DAO .

数据映射器模式

映射器层,在对象和数据库之间移动数据,同时保持它们彼此独立以及映射器本身< /跨度>。 - Martin Fowler:企业应用架构模式

ORM 框架提供了对象和关系数据库之间的映射,因为我们知道关系数据库中的对象和表有不同的方式为应用程序存储数据。此外,对象和表具有结构化数据的机制。在您的 Spring 应用程序中,如果您使用任何 ORM 解决方案,例如 Hibernate、JPA 或 JDO,那么您无需担心对象和关系数据库之间的映射机制。让我们看下图来了解更多关于 Data Mapper 模式的信息:

读书笔记《spring-5-design-patterns》第 8 章使用 Spring ORM 和事务实现模式访问数据库

如上图所示,Account这个对象通过AccountMapper映射到关系型数据库。它的工作方式类似于 Java 对象和应用程序中的底层数据库之间的中介层。让我们看看在数据访问层中使用的另一种模式。

领域模型模式

结合了行为和数据的领域对象模型。 - 作者:Martin Fowler:企业应用架构模式

领域模型是具有行为和数据的对象,因此,行为定义了企业应用程序的业务逻辑,数据是关于业务输出的信息。领域模型结合了数据和流程。在企业应用中,数据模型位于业务层之下,用于插入业务逻辑,并从业务行为中返回数据。让我们看下图更清楚地说明这一点:

读书笔记《spring-5-design-patterns》第 8 章使用 Spring ORM 和事务实现模式访问数据库

如上图所示,我们根据业务需求在应用中定义了两个领域模型。 TransferService 类中定义了将资金从一个帐户转移到另一个帐户的业务行为。 TransferServiceAccountService 类属于领域模型模式企业应用程序。

延迟加载模式的代理

延迟加载是一种设计模式,这种设计 pattern 被一些 ORM 解决方案(如企业应用程序中的 Hibernate)用于延迟一个对象的初始化,直到它被另一个对象在它所在的点调用需要。这种设计模式的目的是在应用程序中进行内存优化。 Hibernate 中的延迟加载设计模式是通过使用虚拟代理对象来实现的。在延迟加载演示中,我们使用了代理,但这不是代理模式的一部分。

Spring 的 Hibernate 模板模式

Spring 提供了一个辅助class 来访问DAO 层中的数据——这个类基于GoF 模板方法设计模式。 Spring提供了HibernateTemplate类,用于提供savecreate等数据库操作、删除更新HibernateTemplate 类确保每个事务只使用一个 Hibernate 会话。

让我们在下一节中了解 Spring 对 Hibernate 的支持。

将 Hibernate 与 Spring 集成


Hibernate 是一个持久化的 ORM 框架,它是开源的,它提供不仅在 Java 对象和数据库表之间进行简单的对象关系映射,而且还为您的应用程序提供了许多复杂的功能来提高性能,并有助于更好地利用资源,例如缓存、延迟加载、急切获取和分布式缓存。

Spring Framework 为集成 Hibernate Framework 提供了全面支持,Spring 有一些内置库可以充分利用 Hibernate Framework。我们可以使用 Spring 的 DI 模式和 IoC 容器在您的应用程序中配置 Hibernate。

让我们在下一节中看看如何在 Spring IoC 容器中配置 Hibernate。

在 Spring 容器中配置 Hibernate 的 SessionFactory

作为在任何企业应用程序中配置 Hibernate 和其他持久性技术的最佳方法,业务对象应该与硬编码的资源查找分开,例如 JDBC DataSource 或 Hibernate 会话工厂。您可以将这些资源定义为 Spring 容器中的 bean。但是业务对象需要这些资源的引用,例如 SessionFactory 和 JDBC DataSource,才能访问它们。让我们看看下面的 DAO 类,它有 SessionFactory 来访问应用程序的数据:

    public class AccountDaoImpl implements AccountDao { 
      private SessionFactory sessionFactory; 
 
      public void setSessionFactory(SessionFactory sessionFactory) { 
        this.sessionFactory = sessionFactory; 
      } 
       //..... 
    } 

正如您在前面的代码中看到的,DAO 类 AccountDaoImpl 遵循依赖注入模式。它被注入 Hibernate 的 SessionFactory 对象来访问数据,并且非常适合 Spring IoC 容器。这里,Hibernate 的 SessionFactory 是单例对象;它产生了Hibernate的org.hibernate.Session接口的主要对象。 SessionFactory 管理着 Hibernate 的 Session 对象,同时也负责打开和关闭 Session 对象。 Session 接口具有实际的数据访问功能,例如 saveupdatedelete从数据库中加载对象。在应用程序中,AccountDaoImp 或任何其他存储库使用此 Hibernate Session 对象来执行其所有持久性需求。

Spring 提供了内置的 Hibernate 模块,您可以在应用程序中使用 Spring 的 Hibernate 会话工厂 bean。

org.springframework.orm.hibernate5.LocalSessionFactoryBean bean 是 Spring 的 FactoryBean 接口的实现。 LocalSessionFactoryBean 基于抽象工厂模式,在应用程序中生成 Hibernate SessionFactory。您可以在应用程序的 Spring 上下文中将 Hibernate SessionFactory 配置为 bean,如下所示:

    @Bean 
    public LocalSessionFactoryBean sessionFactory(DataSource 
    dataSource) { 
      LocalSessionFactoryBean sfb = new LocalSessionFactoryBean(); 
      sfb.setDataSource(dataSource); 
      sfb.setPackagesToScan(new String[] {   
        "com.packt.patterninspring.chapter8.bankapp.model" }); 
        Properties props = new Properties(); 
        props.setProperty("dialect", 
        "org.hibernate.dialect.H2Dialect"); 
        sfb.setHibernateProperties(props); 
        return sfb; 
    } 

在前面的代码中,我们使用 Spring 的 LocalSessionFactoryBean 类将 SessionFactory 配置为 bean。这个 bean 方法以 DataSource 作为参数; DataSource 指定查找数据库连接的方式和位置。我们还为带有名为 "com.packt 的包的 LocalSessionFactoryBean 指定了一个属性 setPackagesToScan .patterninspring.chapter8.bankapp.model" 被扫描,并设置 SessionFactory 的属性是 hibernateProperties 为找到我们将在应用程序中处理的数据库类型。

让我们看看在 Spring 应用程序上下文中配置 Hibernate SessionFactory bean 后如何为应用程序的持久层实现 DAO。

基于普通 Hibernate API 实现 DAO

让我们创建 following DAO 植入类:

    package com.packt.patterninspring.chapter8.bankapp.dao; 
 
    import org.hibernate.SessionFactory; 
    import org.springframework.stereotype.Repository; 
    import org.springframework.beans.factory.annotation.Autowired; 
    @Repository 
    public class AccountDaoImpl implements AccountDao { 
      @Autowired 
      private SessionFactory sessionFactory; 
 
      public void setSessionFactory(SessionFactory sessionFactory) { 
        this.sessionFactory = sessionFactory; 
      } 
     
      @Override 
      public Integer totalAccountsByBranch(String branchName) { 
        String sql = "SELECT count(*) FROM Account WHERE branchName =
        "+branchName; 
        return this.sessionFactory.getCurrentSession().createQuery(sql,
        Integer.class).getSingleResult(); 
      } 
      @Override 
      public Account findOne(long accountId) { 
        return (Account)   
        this.sessionFactory.currentSession().
        get(Account.class, accountId); 
      } 
      @Override 
      public Account findByName(String name) { 
        return (Account) this.sessionFactory.currentSession().
        createCriteria(Account.class) 
        .add(Restrictions.eq("name", name)) 
        .list().get(0); 
      } 
      @Override 
      public List<Account> findAllAccountInBranch(String branchName) { 
       return (List<Account>) this.sessionFactory.currentSession() 
           
       .createCriteria(Account.class).add(Restrictions.eq("branchName",  
       branchName)).list(); 
      }  
    } 

在前面的代码中可以看到,AccountDaoImpl是一个DAO实现类,通过使用Hibernate的SessionFactory bean注入@Autowired 注释。前面描述的 DAO 实现将抛出未经检查的 Hibernate PersistenceExceptions——让这些传播到服务层或 DAO 的其他用户是不可取的。但是 Spring AOP 模块允许转换为 Spring 丰富的、供应商中立的 DataAccessException 层次结构——它隐藏了使用的访问技术。 Spring 通过使用 @Repository 注释 DAO 实现类提供了开箱即用的功能,您只需定义一个 Spring 提供的 BeanPostProcessor< /code>,即PersistenceExceptionTranslationPostProcessor

让我们为我们的 Hibernate DAO 实现类添加一个异常翻译;我们可以通过在 Spring 应用程序上下文中添加一个 PersistenceExceptionTranslationPostProcessor bean 来做到这一点,如下所示:

    @Bean 
    public BeanPostProcessor persistenceTranslation() { 
      return new PersistenceExceptionTranslationPostProcessor(); 
    } 

前面注册的bean PersistenceExceptionTranslationPostProcessor 负责为带有 @Repository 注解的bean添加一个advisor,它是对于代码中捕获的任何特定于平台的异常,重新抛出为特定于 Spring 的未经检查的数据访问异常。

在下一节中,让我们看看 Spring 如何跨 Spring 应用程序的业务和持久层管理事务。

Spring中的事务管理策略


Spring 为 Spring 应用程序中的 transaction 管理提供了全面的支持。这是 Spring 框架最引人注目的特性之一。大多数情况下,此功能迫使软件行业使用 Spring 框架开发企业应用程序。 Spring 框架提供了一种一致的方式来使用任何持久性技术来管理应用程序中的事务,例如 Java Transaction API、JDBC、Hibernate、Java Persistence API 和 Java 数据对象。 Spring 支持声明式事务管理以及编程式 transaction 管理。

Java事务有两种类型,如下:

  • Local transactions - single resource: Local transactions managed by the underlying resource; these are resource-specific. Let's explain this with the help of the following diagram:
读书笔记《spring-5-design-patterns》第 8 章使用 Spring ORM 和事务实现模式访问数据库

如上图所示,应用程序和数据库平台之间存在一个事务,以确保每个任务单元都遵循数据库的 ACID 属性。

  • Global (distributed) transactions - multiple: Global transactions, which are managed by separate, dedicated transaction managers, enable you to work with multiple transactional resources. Take a look at the following diagram to understand more about Global or distributed transactions:
读书笔记《spring-5-design-patterns》第 8 章使用 Spring ORM 和事务实现模式访问数据库

正如您在上一张图中所见,Transaction Manager 在应用程序中与多种数据库技术一起工作。全局事务独立于特定于平台的持久性技术。

Spring 为 Java 应用程序中的两种事务类型提供了相同的 API。 Spring 框架通过以声明方式配置事务或以编程方式配置事务,在任何环境中提供一致的编程模型。

让我们继续接下来的部分,看看如何在 Spring 应用程序中配置事务。

声明式事务划分和实现

Spring 支持 declarative 事务管理。 Spring 将 transaction 分界与事务实现分开。分界是通过 Spring AOP 以声明方式表示的。我们始终建议在您的 Spring 应用程序中使用 Spring 的声明式事务划分和实现,因为声明式编程模型使您可以从代码中替换外部事务划分 API,并且您可以使用 Spring AOP 事务拦截器对其进行配置。交易基本上是横切关注点;这种声明性事务模型允许您将应用程序的业务逻辑与重复的事务划分代码分开。

如前所述,Spring 为 Spring 应用程序中的事务处理提供了一致的模型,并提供了一个接口 PlatformTransactionManager 来隐藏实现细节。在 Spring Framework 中有几个可用于该接口的实现,下面列出了其中的一些:

  • DataSourceTransactionManager
  • HibernateTransactionManager
  • JpaTransactionManager
  • JtaTransactionManager
  • WebLogicJtaTransactionManager
  • WebSphereUowTransactionManager

以下是关键接口:

    public interface PlatformTransactionManager { 
      TransactionStatus getTransaction( 
        TransactionDefinition definition) throws TransactionException; 
       void commit(TransactionStatus status) throws   
         TransactionException; 
       void rollback(TransactionStatus status) throws
         TransactionException; 
   } 

在上述代码中,getTransaction() 方法返回一个 TransactionStatus 对象。该对象包含交易的状态;它要么是新的,要么返回当前调用堆栈中的现有值。它取决于 TransactionDefinition 参数。与 JDBC 或 ORM 模块一样,Spring 也提供了一种一致的方式来处理任何事务管理器抛出的异常。 getTransaction() 方法抛出 TransactionException 异常,这是一个未经检查的异常。

Spring 对应用程序中的全局和本地事务使用相同的 API。在应用程序中从本地事务转移到全局事务需要非常小的更改——这只是更改事务管理器。

部署事务管理器

在 Spring 应用程序中部署 transaction 有两个步骤。第一步是您必须使用您的应用程序实现或配置一个预先实现的 Spring 事务管理器类。第二步,声明事务分界,也就是要放置Spring事务的地方。

第 1 步 - 实现事务管理器

像任何其他 Spring bean 一样为 required 实现创建 bean。您可以根据需要为任何持久性技术(例如 JDBC、JMS、JTA、Hibernate、JPA 等)配置事务管理器。但在以下示例中,这里是使用 JDBC 的 DataSource 的管理器:

在 Java 配置中,让我们看看如何在应用程序中定义 transactionManager bean:

    @Bean 
    public PlatformTransactionManager transactionManager(DataSource 
    dataSource) { 
      return new DataSourceTransactionManager(dataSource); 
    } 

在 XML 配置中,可以像这样创建 bean:

    <bean id="transactionManager" class="org.springframework.jdbc.datasource. DataSourceTransactionManager"> 
     <property name="dataSource" ref="dataSource"/> 
    </bean> 

在前面的代码中,我们使用了 dataSource bean; dataSource bean 必须在别处定义。 bean ID "transactionManager" 是默认名称。我们可以更改它,但必须在任何地方指定替代名称,而这并不容易做到!

第 2 步 - 声明事务分界

作为最佳方法,应用程序的服务层是划分事务的最佳位置。让我们在下面的代码中看到这一点:

    @Service 
    public class TransferServiceImpl implements TransferService{ 
      //... 
      @Transactional 
      public void transfer(Long amount, Long a, Long b){ 
        // atomic unit-of-work 
      } 
      //... 
    } 

在前面的代码中可以看到,TransferServiceImpl是我们在应用服务层的服务类。此服务是划分工作单元事务的最佳位置。 Spring提供了@Transactional注解来划分事务;此注解可用于应用程序中服务类的类级别或方法级别。让我们看看类级别的 @Transactional

    @Service 
    @Transactional 
    public class TransferServiceImpl implements TransferService{ 
      //... 
      public void transfer(Long amount, Account a, Account b){ 
        // atomic unit-of-work 
      } 
      public Long withdraw(Long amount,  Account a){ 
        // atomic unit-of-work 
      } 
      //... 
    } 

如果你在类级别声明了@Transactional注解,那么这个服务中的所有业务方法都是事务方法。

笔记

注意——如果您使用 @Transactional 注释,方法可见性应该是公开的。如果将此注解用于非公共方法,例如 protectedprivatepackage-visible,没有错误或异常被抛出,但是这个注解的方法没有显示事务行为。

但是仅在 Spring 应用程序中使用这个注解是不够的。我们必须使用 Spring 的 Java 配置文件中的 @EnableTransactionManagement 注解来启用 Spring Framework 的事务管理功能,或者我们可以使用命名空间 <tx:annotation-driven/> 在 XML 配置文件中。让我们看看下面的代码,例如:

    @Configuration 
    @EnableTransactionManagement 
    public class InfrastructureConfig { 
    
      //other infrastracture beans definitions 
    
     @Bean 
     public PlatformTransactionManager transactionManager(){ 
         return new DataSourceTransactionManager(dataSource()); 
     } 
   } 

在前面的代码中可以看到,InfrastructureConfig是Spring应用的Java配置文件——这里我们定义了基础设施相关的bean,transactionManager bean 也已在此处定义。这个配置类多了一个注解是@EnableTransactionManagement——这个注解在应用中定义了一个Bean Post-Processor,它代理了@Transactional 豆子。现在,看看下图:

读书笔记《spring-5-design-patterns》第 8 章使用 Spring ORM 和事务实现模式访问数据库

正如您在上图中看到的,TransferServiceImpl 类包装在 Spring 代理中。

但是要知道应用程序中的 @Transactional bean 究竟发生了什么,让我们看看以下步骤:

  1. The target object is wrapped in a proxy; it uses an Around advice as we have discussed in Chapter 6, Spring Aspect Oriented Programming with Proxy & Decorator Pattern.
  2. The Proxy implements the following behavior:

1. 进入业务方式前开始交易。

2. 在业务方法结束时提交。

3. 如果业务方法抛出 RuntimeException 则回滚——这是 Spring 事务的默认行为,但您也可以针对已检查和自定义异常覆盖它。

  1. The transaction context is now bound to the current thread in the application.
  2. All steps controlled by the configuration either in XML, Java or Annotations.

现在看一下带有关联事务管理器的本地 JDBC 配置的下图:

读书笔记《spring-5-design-patterns》第 8 章使用 Spring ORM 和事务实现模式访问数据库

在上图中,我们已经使用 JDBC 和 DataSource Transaction Manager 定义了一个本地数据源。

在下一节中,我们将讨论如何在应用程序中以编程方式实现和划分事务。

程序化事务划分和实现

Spring 允许您使用 TransactionTemplatePlatformTransactionManager 在应用程序中以编程方式实现和划分 transactions直接执行。但强烈推荐使用声明式事务管理,因为它提供了干净的代码和非常灵活的配置。

让我们看看如何以编程方式在应用程序中实现事务:

    package com.packt.patterninspring.chapter8.bankapp.service; 
 
    import org.springframework.beans.factory.annotation.Autowired; 
    import org.springframework.stereotype.Service; 
    import org.springframework.transaction.PlatformTransactionManager; 
    import org.springframework.transaction.TransactionStatus; 
    import org.springframework.transaction.support.TransactionCallback; 
    import org.springframework.transaction.support.TransactionTemplate; 
 
    import com.packt.patterninspring.chapter8.bankapp.model.Account; 
    import com.packt.patterninspring.chapter8.bankapp.
      repository.AccountRepository; 
 
    @Service 
    public class AccountServiceImpl implements AccountService { 
      //single TransactionTemplate shared amongst all methods in this 
      instance 
      private final TransactionTemplate transactionTemplate; 
      @Autowired 
      AccountRepository accountRepository; 
    
      // use constructor-injection to supply the 
      PlatformTransactionManager 
      public AccountServiceImpl(PlatformTransactionManager 
      transactionManager) { 
        this.transactionTemplate = new 
        TransactionTemplate(transactionManager); 
      } 
     
      @Override 
      public Double cheeckAccountBalance(Account account) { 
        return transactionTemplate.execute(new 
        TransactionCallback<Double>() { 
          // the code in this method executes in a transactional 
          context 
          public Double doInTransaction(TransactionStatus status) { 
            return accountRepository.checkAccountBalance(account); 
          } 
        });  } 
    } 

在前面的应用程序代码中,我们显式地使用 TransactionTemplate 在事务上下文中执行应用程序逻辑。 TransactionTemplate也是基于模板方法设计模式,与Spring Framework中的其他模板如JdbcTemplate有相同的做法。与 JdbcTemplate 类似,TransactionTemplate 也使用回调方法,它使应用程序代码摆脱了用于管理事务资源的样板代码。我们在Service类构造中构造了一个TransactionTemplate类的对象,并将一个PlatformTransactionManager的对象作为参数传递给构造函数TransactionTemplate 类的。我们还编写了一个 TransactionCallback 实现,其中包含应用程序的业务逻辑代码,它显示了应用程序逻辑和事务代码之间的紧密耦合。

在本章中,我们已经看到 Spring 如何有效地管理企业应用程序中的事务。现在让我们研究一些我们在处理任何企业应用程序时必须牢记的最佳实践。

应用程序中 Spring ORM 和事务模块的最佳实践


以下是我们在设计和开发应用程序时必须遵循实践

避免在 DAO 实现中使用 Spring 的 HibernateTemplate 辅助类,并使用 SessionFactoryEntityManager 在您的应用程序中。由于 Hibernate 的上下文会话功能,请直接在您的 DAO 中使用 SessionFactory。此外,使用 getCurrentSession() 方法访问事务当前会话,以便在应用程序中执行持久性操作。请参考以下代码:

    @Repository 
    public class HibernateAccountRepository implements 
    AccountRepository { 
      SessionFactory sessionFactory; 
      public HibernateAccountRepository(SessionFactory 
      sessionFactory) { 
        super(); 
        this.sessionFactory = sessionFactory; 
      } 
     //... 
   } 

在您的应用程序中,始终对数据访问对象或存储库使用 @Repository 注释;它提供异常翻译。请参考以下代码:

    @Repository 
    public class HibernateAccountRepository{//...} 

即使服务中的业务方法仅将其职责委托给相应的 DAO 方法,服务层也必须是独立的。

始终在应用程序的服务层而不是 DAO 层实现事务——这是事务的最佳位置。请参考以下代码:

    @Service 
    @Transactional 
    public class AccountServiceImpl implements AccountService {//...} 

声明式事务管理在应用程序中配置更加强大和方便,并且是强烈推荐在 Spring 应用程序中使用的方法。它将横切关注点与业务逻辑分开。

始终抛出运行时异常,而不是来自服务层的已检查异常。

注意 @Transactional 注释的 readOnly 标志。当服务方法仅包含查询时,将事务标记为 readOnly=true

概括


第7章中,使用 Spring 和 JDBC 模板模式访问数据库< /span>,我们看到Spring提供了基于GOF模板方法设计模式的JdbcTemplate类。此类处理传统 JDBC API 底层所需的所有样板代码。但是当我们使用 Spring JDBC 模块时,将表映射到对象变得非常繁琐。在本章中,我们看到了将对象映射到关系数据库中的表的解决方案——通过在复杂的应用程序中使用 ORM,我们可以对关系数据库做更多的事情。 Spring 支持与多个 ORM 解决方案(如 Hibernate、JPA 等)集成。这些 ORM 框架为数据持久性启用了声明式编程模型,而不是使用 JDBC 编程模型。

我们还研究了在数据访问层或集成层中实现的几种设计模式。这些模式作为 Spring 框架中的一个特性实现为延迟加载的代理模式、用于与业务层集成的 Facade 模式、用于数据访问的 DAO 模式等。

在下一章中,我们将看到如何通过使用 Spring 对缓存模式的支持来提高应用程序在生产中的性能。