使用dao到另一个类时的JDBI事务

时间:2017-06-14 21:35:52

标签: java transactions jdbi

这就是我所拥有的:

userDAO的

public interface UserDao {
    @SqlUpdate("...")
    int action1(@BindBean(..))

    @SqlUpdate("...")
    int action2(@BindBean(..))
}

的UserManager

public class UserManager {
    private final UserDao dao;

    public UserManager (final UserDao dao) {
        this.dao = dao;
        // there are other daos and clients passed here
    }

    @Transaction
    public void foo() {
        action1();
        action2();
    }
}

UserRepository

public class UserRepository {
    private final UserManager manager;

    public UserRepository(final UserManager manager) {
        this.manager = manager;
    }

    public void doSomething() {
        manager.foo();
    }
}

@Transaction没有做任何事情;如何为我这里的结构添加事务支持?

1 个答案:

答案 0 :(得分:1)

正确的做法是:

  1. 您必须能够从事务方法体访问DBI实例。将其作为方法参数传递或作为服务实例字段。
  2. 内部事务方法调用dbi.inTransaction(...)(或dbi.useTransaction(...),如果您不需要返回结果)传入回调。此方法不再需要@Transactional注释。
  3. 在回调中,使用handle.attach(YourDaoInterface.class)
  4. 获取所需的每个dao的实例

    就是这样。

    UPD。 所以,你的代码看起来像(假设你使用Java8或更新版本):

    public class UserManager {
    
    private final DBI dbi;
    
    public UserManager (final DBI dbi) {
        this.dbi = dbi;
        // there are other daos and clients passed here
    }
    
    public void foo() {
        dbi.useTransaction((handle, transactionStatus) -> {
            UserDao dao = handle.attach(UserDao.class);
            dao.action1();
            dao.action2();
        });
      }
    }
    

    这是用于为这种服务方法编写单元测试的JUnit + Mockito代码:

    @RunWith(MockitoJUnitRunner.class)
    ........
    
    @Mock
    private Handle handle;
    
    @Mock
    private UserDao userDao;
    
    @Spy
    private DBI dbi = new DBI(mock(ConnectionFactory.class));
    
    @InjectMocks
    private UserManager userManager;
    
    @Before
    public void setUpDbi() {
        Mockito.doReturn(handle)
                .when(dbi).open();
        Mockito.when(handle.attach(UserDao.class))
                .thenReturn(userDao);
    
        TransactionStatus status = Mockito.mock(TransactionStatus.class);
    
        Answer<Object> transactionalAnswer = invocation ->
                ((TransactionCallback) invocation.getArguments()[0])
                        .inTransaction(handle, status);
    
        Mockito.when(handle.inTransaction(any()))
                .thenAnswer(transactionalAnswer);
    }