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

2个事务管理器之间的Spring切换(不需要单个事务行为)

穆锋
2023-03-14

要访问2个数据库,我们使用两个不同的事务管理器(每个都有自己的数据源):

第一个事务管理器:

<bean id="transactionManager"
    class="org.springframework.orm.hibernate4.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

第二个事务管理器:

<bean id="transactionManager2"
    class="org.springframework.orm.hibernate4.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory2" />
</bean>

在服务层,我们希望有一个获取id并返回DTO的方法。问题是DTO必须填充来自两个数据库的数据。

我们尝试了第一种方法:

  • FooDao:使用 sessionFactory(与 transactionManager 相同)
  • BarDao:使用 sessionFactory2(与 transactionManager2 相同的 sessionFactory)

使用以下服务方法:

@Autowired
private FooDao fooDao;

@Autowired
private BarDao barDao;

@Transactional(value="transactionManager", readOnly=true)
public FooBarDto getFoo(int id) {
    Foo foo = fooDao.get(id);

    return createDto(foo);
}

@Transactional(value="transactionManager2", readOnly=true)
public FooBarDto createDto(Foo foo) {
    Bar bar = barDao.get(foo.getId());

    FooBarDto fooBarDto = new FooBarDto();

    fooBarDto.setId(String.valueOf(foo.getId()));
    fooBarDto.setA(String.valueOf(foo.getA()));
    fooBarDto.setB(String.valueOf(foo.getB()));
    fooBarDto.setC(String.valueOf(bar.getC()));
    fooBarDto.setD(String.valueOf(bar.getD()));

    return fooBarDto;
}

但在这种情况下,我们在barDao方法中得到了“未找到当前线程的会话”错误。由于我们不需要在单个事务上执行整个操作(并保持当前配置),因此我们不希望使用JTA。

我们尝试配置事务传播(使用Propagation.REQUIRES_NEW),但看起来它仅适用于来自同一管理器的事务。

我们可以更改这两个方法,并从控制器(顺序)调用这两个,但我们更愿意调用一个服务方法来完成所有工作。有没有办法达到预期的行为?

非常感谢Ricardo Veguilla!按照你的建议,一切都很顺利。我将createDto方法移动到另一个服务(BarService),并在第一个服务(FooService)上自动连接它:

@Service
public class FooServiceImpl implements FooService {

    @Autowired
    private FooDao fooDao;

    @Autowired
    private BarService barService;

    @Transactional(value="transactionManager", readOnly=true)
    public FooBarDto getFoo(int id) {
        Foo foo = fooDao.get(id);

        return barService.createDto(foo);
    }
}

以及创建的 BarService:

@Service
public class BarServiceImpl implements BarService {

    @Autowired
    private BarDao barDao;

    @Transactional(value="transactionManager2", readOnly=true)
    public FooBarDto createDto(Foo foo) {
        FooBarDto fooBarDto = new FooBarDto();

        if (foo != null) {
            Bar bar = barDao.get(foo.getId());

            fooBarDto.setId(String.valueOf(foo.getId()));
            fooBarDto.setA(String.valueOf(foo.getA()));
            fooBarDto.setB(String.valueOf(foo.getB()));

            if (bar != null) {
                fooBarDto.setC(String.valueOf(bar.getC()));
                fooBarDto.setD(String.valueOf(bar.getD()));
            }
        }

        return fooBarDto;
    }
}

共有2个答案

章禄
2023-03-14

如果您使用Java 8,您可以创建如下类:

@Component
class TransactionWrapper {

    @Transactional
    public <T> T doInTransaction(Supplier<T> supplier) {
       return
    }

在事务上下文中调用类内部的方法(即私有方法)。

寿浩言
2023-03-14

您在 barDao bacuse 中没有从同一个 bean 内部调用 createDto 的会话,这意味着方法调用不会通过 Spring Transacitional 代理。

在Spring中,需要从包含Spring注入的< code>myBean实例的其他bean中调用带有用< code>@Transactional注释的公共方法的bean myBean,因为事务支持是使用代理实现的。您需要将< code>createDto方法移动到另一个bean:

 @component
 public class ForBarService {

        @Autowired
        private FooDao fooDao;

        @Autowired
        private DtoFactory dtoFactory;

        @Transactional(value="transactionManager", readOnly=true)
        public FooBarDto getFoo(int id) {
        Foo foo = fooDao.get(id);
             return dtoFactory.createDto(foo);
        }
 }

 @Component
 public class DtoFactory {
      @Autowired
      private BarDao barDao;

      @Transactional(value="transactionManager2", readOnly=true)
      public FooBarDto createDto(Foo foo) {
          Bar bar = barDao.get(foo.getId());

          FooBarDto fooBarDto = new FooBarDto();

          fooBarDto.setId(String.valueOf(foo.getId()));
          fooBarDto.setA(String.valueOf(foo.getA()));
          fooBarDto.setB(String.valueOf(foo.getB()));
          fooBarDto.setC(String.valueOf(bar.getC()));
          fooBarDto.setD(String.valueOf(bar.getD()));

          return fooBarDto;
      }

 }
 类似资料:
  • 我正在使用Spring(3.1.4版本)和HibernateTemplate(3.6.7.Final)以及Spring数据JPA(1.3.0版本)。我在配置文件中定义了两个事务管理器,一个用于Hibernate,另一个用于Spring数据JPA。两个事务管理器使用不同的数据源。 我想在代码中使用Hibernate的事务管理器和Spring data JPA的事务管理器。(Spring文档链接-10

  • 9.2.1 概述 Spring框架支持事务管理的核心是事务管理器抽象,对于不同的数据访问框架(如Hibernate)通过实现策略接口PlatformTransactionManager,从而能支持各种数据访问框架的事务管理,PlatformTransactionManager接口定义如下: public interface PlatformTransactionManager { T

  • 对于每个客户机,我们自然需要发送关于事务的反馈(OK或Exception->rollback)。 我的问题是:如果我使用,是否意味着只使用一个事务,如果第100个客户机遇到问题,第1个客户机的事务也会回滚?

  • 我正在使用Spring的事务支持和JPA(Hibernate)来持久化我的实体。一切正常,但我在处理一个请求中的部分更新时陷入困境: 对于每个用户(HTTP)请求,我必须将一个日志条目写入数据库表,即使“主”业务实体的更新失败(例如,由于验证错误)。因此,我的第一个/主要事务get被回滚,但第二个(写日志)应该提交。这似乎可以使用正确的传播级别来写入日志条目: 然而,我的问题是,我在第二个事务中注

  • 我试图弄清楚如何配置一个方法,使其不在使用Spring的事务中运行。我读到Spring数据存储库默认情况下激活其方法中的事务行为。我不想要这个事务,因为我有许多对存储库的“保存”调用,并且每个调用都是独立的。我认为为每个对存储库方法的调用创建一个事务会降低代码和应用程序的性能。所以: 这是可能的还是每个服务或dao方法都必须在一个事务中运行? 如果有,为什么? 如果可能,如何配置方法不在事务中运行

  • 我试图用不同的事务管理器嵌套事务,其中如果嵌套的事务失败,外部主事务也需要回滚 我的spring配置文件有 但是,当失败时,中的事务不会回滚。如何使回滚工作?