将 dao 用于另一个 class 时的 JDBI 事务

JDBI Transaction when using dao to another class

这是我的:

UserDao

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

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

用户管理器

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();
    }
}

用户资料库

public class UserRepository {
    private final UserManager manager;

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

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

这个@Transaction什么都不做;如何为我这里的结构添加事务支持?

做你想做的正确方法是:

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

就是这样。

更新。 因此,您的代码将如下所示(假设您使用 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);
}