当前位置: 首页 > 知识库问答 >
问题:

跨多个方法的Spring JPA事务

樊桐
2023-03-14

我在运行于Tomcat7的web应用程序中使用Spring3.2和JPA以及Hibernate4。应用程序分为控制器类、服务类和DAO类。服务类在类和方法级别具有带注释的事务配置。DAO是由@PersistenceContext注释注入实体管理器的普通JPA。

@Service("galleryService")
@Transactional(propagation=Propagation.SUPPORTS, readOnly=true)
public class GalleryServiceImpl implements GalleryService {

  @Override
  public Picture getPicture(Long pictureId) {
    return pictureDao.find(pictureId);
  }

  @Override
  public List<PictureComment> getComments(Picture picture) {
    List<PictureComment> comments = commentDao.findVisibleByPicture(picture);
    Collections.sort(comments, new Comment.ByCreatedOnComparator(Comment.ByCreatedOnComparator.SORT_DESCENDING));
    return comments;
  }
  ...
}

@Controller
@RequestMapping("/gallery/displayPicture.html")
public class DisplayPictureController extends AbstractGalleryController {

  @RequestMapping(method = RequestMethod.GET)
  public String doGet(ModelMap model, @RequestParam(REQUEST_PARAM_PICTURE_ID) Long pictureId) {
    Picture picture = galleryService.getPicture(pictureId);
    if (picture != null) {
      model.addAttribute("picture", picture);
      // Add comments
      model.addAttribute("comments", galleryService.getComments(picture));
    } else {
      LOGGER.warn(MessageFormat.format("Picture {0} not found.", pictureId));
      return ViewConstants.CONTENT_NOT_FOUND;
    }
    return ViewConstants.GALLERY_DISPLAY_PICTURE;
  }
  ...
}

我打开了org的调试日志记录。springframework。交易并注意到,“创建新交易”、“打开新EntityManager”、“获取…”,“结束……”并且“提交事务”是为我的服务类中方法的每次调用完成的。即使这些方法在我的控制器类中由一个方法调用。以下是我的日志输出示例:

2014-12-03 10:53:00,448 org.springframework.transaction.support.AbstractPlatformTransactionManager getTransaction
DEBUG: Creating new transaction with name [de.domain.webapp.gallery.service.GalleryServiceImpl.getPicture]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly
2014-12-03 10:53:00,448 org.springframework.orm.jpa.JpaTransactionManager doBegin
DEBUG: Opened new EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@6133a72f] for JPA transaction
2014-12-03 10:53:00,468 org.springframework.orm.jpa.JpaTransactionManager doBegin
DEBUG: Exposing JPA transaction as JDBC transaction [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@5182c1b7]
2014-12-03 10:53:00,468 org.springframework.transaction.interceptor.TransactionAspectSupport prepareTransactionInfo
TRACE: Getting transaction for [de.domain.webapp.gallery.service.GalleryServiceImpl.getPicture]
2014-12-03 10:53:00,489 org.springframework.transaction.interceptor.TransactionAspectSupport commitTransactionAfterReturning
TRACE: Completing transaction for [de.domain.webapp.gallery.service.GalleryServiceImpl.getPicture]
2014-12-03 10:53:00,489 org.springframework.transaction.support.AbstractPlatformTransactionManager processCommit
DEBUG: Initiating transaction commit
2014-12-03 10:53:00,489 org.springframework.orm.jpa.JpaTransactionManager doCommit
DEBUG: Committing JPA transaction on EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@6133a72f]
2014-12-03 10:53:00,489 org.springframework.orm.jpa.JpaTransactionManager doCleanupAfterCompletion
DEBUG: Closing JPA EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@6133a72f] after transaction
2014-12-03 10:53:00,489 org.springframework.orm.jpa.EntityManagerFactoryUtils closeEntityManager
DEBUG: Closing JPA EntityManager

我知道我可以使用OpenSessionInView来保存hibernate会话以获得完整的请求,但有些人说,OSIV是一种反模式。相反,我使用的是SpringOpenEntityManagerViewFilter。但是没有成功。如何实现,Spring使用单个事务来调用控制器的多个服务层方法?还是我不明白?

这里是我的Spring配置的一部分:

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
            <property name="database" value="MYSQL" />
            <property name="showSql" value="false" />
        </bean>
    </property>
</bean>

<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="java:comp/env/jdbc/tikron" />
</bean>

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

<bean id="jpaTemplate" class="org.springframework.orm.jpa.JpaTemplate">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

<tx:annotation-driven transaction-manager="transactionManager" />

<context:component-scan base-package="de.domain.webapp">
    <context:include-filter type="regex" expression=".*Service"/>
</context:component-scan>

我的持久性单位:

<persistence-unit name="tikron-data" transaction-type="RESOURCE_LOCAL">
  <!-- Entities located in external project tikron-data -->
  <jar-file>/WEB-INF/lib/tikron-data-2.0.1-SNAPSHOT.jar</jar-file>
  <!-- Enable JPA 2 second level cache -->
  <shared-cache-mode>ALL</shared-cache-mode>
  <properties>
    <property name="hibernate.hbm2ddl.auto" value="validate" />
    <property name="hibernate.cache.use_second_level_cache" value="true" />
    <property name="hibernate.cache.use_query_cache" value="true" />
    <property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory"/>
    <property name="hibernate.generate_statistics" value="false" /> 
  </properties>
</persistence-unit>

应用程序启动时的更多日志输出:

2014-12-03 10:46:48,428 org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean createNativeEntityManagerFactory
INFO: Building JPA container EntityManagerFactory for persistence unit 'tikron-data'
2014-12-03 10:46:48,428 org.hibernate.ejb.HibernatePersistence logDeprecation
WARN: HHH015016: Encountered a deprecated javax.persistence.spi.PersistenceProvider [org.hibernate.ejb.HibernatePersistence]; use [org.hibernate.jpa.HibernatePersistenceProvider] instead.
2014-12-03 10:46:48,448 org.hibernate.jpa.internal.util.LogHelper logPersistenceUnitInformation
INFO: HHH000204: Processing PersistenceUnitInfo [
    name: tikron-data
    ...]

...

2014-12-03 10:46:51,101 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@6cccf90d: defining beans [propertyConfigurer,messageSource,entityManagerFactory,dataSource]; root of factory hierarchy
2014-12-03 10:46:51,111 org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization completed in 3374 ms

提前谢谢。

共有1个答案

孙阳舒
2023-03-14

您需要发挥您的传播级别,并正确地构造您的服务bean调用。如果您在服务类上使用@Transactional,那么您所描述的是正常的,因为事务划分发生在公共方法级别。因此,根据进入服务bean的公共方法时的传播级别,事务将启动、加入现有事务、抛出异常或以非事务方式执行。

要让服务方法在一个事务中执行,只需将传播级别设置为与GalleryService中相同的支持级别(前提是不在方法级别上重写它),并从另一个服务的单个方法调用这些方法,该服务的注释为@Transactional(传播=传播.REQUIRED)。让您的调用通过bean(例如(galleryService.getPicture而不是本地调用getPicture)传递是很重要的,因为注入事务语义的方面与封装bean的代理相对应

@Service("exampleService")
@Transactional(propagation=Propagation.REQUIRED)
public class ExampleServiceImpl implements ExampleService {

  @Autowired
  private GalleryService galleryService;

  @Override
  public void singleTransaction() {
    galleryService.getPicture
    galleryService.getComments
  }

  ...
}

简要PROPAGATION_LEVEL词汇表

MANDATORY
          Support a current transaction, throw an exception if none exists.
NESTED
          Execute within a nested transaction if a current transaction exists, behave like PROPAGATION_REQUIRED else.
NEVER
          Execute non-transactionally, throw an exception if a transaction exists.
NOT_SUPPORTED
          Execute non-transactionally, suspend the current transaction if one exists.
REQUIRED
          Support a current transaction, create a new one if none exists.
REQUIRES_NEW
          Create a new transaction, suspend the current transaction if one exists.
SUPPORTS
          Support a current transaction, execute non-transactionally if none exists.

关于评论的更新

但是,将服务方法调用组合到一个服务方法中是否是在单个事务中处理这些调用的唯一方法?

不,但我认为这是你最好的选择。考虑这篇文章http://www.ibm.com/developerworks/java/library/j-ts2/index.html.我所描述的是本文中提到的API层策略。其定义

当您有粗粒度方法作为后端功能的主要入口点时,将使用API Layer事务策略。(如果你愿意,可以打电话给他们服务。)在这种情况下,客户端(无论是基于Web的、基于Web服务的、基于消息的,还是甚至基于桌面的)对后端进行一次调用以执行特定的请求。

现在,在标准的三层体系结构中,您有了表示层、业务层和持久层。简单地说,您可以为控制器、服务或DAO添加注释。服务是包含逻辑工作单元的服务。如果对控制器进行注释,它们是表示层的一部分,如果存在事务语义,并且决定切换或添加非http客户端(例如Swing客户端),则必须迁移或复制事务逻辑。DAO层也不应该是事务的所有者,“DAO方法的粒度远小于业务逻辑单元的粒度。我限制了最佳实践等要点。但是,如果您不确定,请选择您的业务(服务)作为您的API事务层:)

你有很多帖子全方位讨论这个话题

为什么将@transactional与@service一起使用而不是与@controller一起使用“@transactional”应该放在服务层还是DAO上

非常好,有趣的阅读,很多观点和非常上下文相关

 类似资料:
  • null 下面是一个示例代码- 我的问题是,updateDb02中的setRollbackOnly()只回滚Db01事务。

  • 问题内容: 您将如何从业务逻辑层中的一种方法在数据访问层中调用几种方法,以使所有SQL命令都驻留在一个SQL事务中? 可以从BLL中的其他位置分别调用每个DAL方法,因此不能保证数据层方法始终是事务的一部分。我们需要此功能,因此,如果数据库在长时间运行的过程中脱机,则不会提交任何内容。业务层基于每个先前调用的结果来编排不同的数据层方法调用。我们只想在整个过程的最后(从业务层)提交。 问题答案: 好

  • 本文向大家介绍js实现跨域的多种方法,包括了js实现跨域的多种方法的使用技巧和注意事项,需要的朋友参考一下 从域说起 域: 域是WIN2K网络系统的安全性边界。我们知道一个计算机网最基本的单元就是“域”,这一点不是WIN2K所独有的,但活动目录可以贯穿一个或多个域。在独立的计算机上,域即指计算机本身,一个域可以分布在多个物理位置上,同时一个物理位置又可以划分不同网段为不同的域,每个域都有自己的安全

  • 我正在开发一个spring批处理应用程序(内存为2GB),尝试处理数据(在处理过程中使用select查询获取数据),并在postgres DB中插入大约100万条处理过的记录。我在这个项目中使用Spring Data JPA。但是Spring JPA在处理这些记录时消耗了太多内存

  • 我正在使用Spring MVC/Security/JPA/Hibernate开发一个web应用程序。 我有一个用户类,它与UserRole有一个omany关系。 UserRole类与用户之间有很多关系。 用户存储库界面非常简单。 当我使用下面的代码片段使用存储库加载用户信息时,它会很好地检索用户和用户角色数据。 这是日志文件。 然而,问题出在这里。 如果我使用UserRepository的save

  • 本文向大家介绍Java中checkbox实现跨页多选的方法,包括了Java中checkbox实现跨页多选的方法的使用技巧和注意事项,需要的朋友参考一下 最近要实现一个功能,就是checkbox跨页多选,在网上看了一下,资料很少,而且大多是不完全的。不过经过我的努力,终于做出来了。     JSP页面:    1,定义三个Hidden变量:     2,javascript    每次翻页的时候调用