【问题标题】:Spring switch between 2 transaction managers (no single transaction behavior needed)在 2 个事务管理器之间进行 Spring 切换(不需要单个事务行为)
【发布时间】:2014-08-29 20:08:07
【问题描述】:

为了访问 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;
    }
}

【问题讨论】:

    标签: spring hibernate spring-transactions


    【解决方案1】:

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

    在 Spring 中,需要从包含 Spring 注入的 myBean 实例的其他 bean 调用带有 @Transactional 注释的公共方法的 bean myBean,因为事务支持是使用代理实现的。您需要将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;
          }
    
     }
    

    【讨论】:

      【解决方案2】:

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

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

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

      【讨论】:

      • 你能不能说的更具体一些并举个例子?
      猜你喜欢
      • 1970-01-01
      • 2014-05-18
      • 1970-01-01
      • 1970-01-01
      • 2019-11-26
      • 2014-03-22
      • 2013-07-30
      • 1970-01-01
      • 2015-08-23
      相关资源
      最近更新 更多