将 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
什么都不做;如何为我这里的结构添加事务支持?
做你想做的正确方法是:
- 您必须有权从事务方法主体访问 DBI 实例。要么将其作为方法参数传递,要么将其作为服务实例字段。
- 内部事务方法调用 dbi.inTransaction(...)(或 dbi.useTransaction(...) 如果您不需要 return 结果)传递回调.您不再需要此方法上的@Transactional 注释。
- 在回调中,通过handle.attach(YourDaoInterface.class)
获取你需要的每个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);
}
这是我的:
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
什么都不做;如何为我这里的结构添加事务支持?
做你想做的正确方法是:
- 您必须有权从事务方法主体访问 DBI 实例。要么将其作为方法参数传递,要么将其作为服务实例字段。
- 内部事务方法调用 dbi.inTransaction(...)(或 dbi.useTransaction(...) 如果您不需要 return 结果)传递回调.您不再需要此方法上的@Transactional 注释。
- 在回调中,通过handle.attach(YourDaoInterface.class) 获取你需要的每个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);
}