2014-08-29 32 views
1

要访问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(同SessionFactory的作为transactionManager2)

有以下服务方法:

@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; 
    } 
} 

回答

1

你得到的barDao bacuse没有会话你从同一个bean中调用createDto,这意味着方法调用不会经历Spring Transacitional代理。

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

} 
0

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

@Component 
class TransactionWrapper { 

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

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