当使用 sqlobject-annotations 创建 DAO 时,可以通过调用handle.attach(DAO.class)
在一个事务中使用多个 DAO,但是如果我想在不使用带注释的接口的情况下编写我的 DAO,我无法找到一种在一个中使用多个 DAO 的方法交易,见下面的例子。
在示例中h.attach(ProductDAOAnnotated.class);
有效,但h.attach(ProductDAO.class);
会引发NoSuchExtensionException
异常。
一个事务中是否有多个“非sqlobject”DAO,或者在这种情况下我是否被迫使用带注释的接口?
public class ProductService {
private final Jdbi jdbi;
public ProductService(Jdbi jdbi) {
this.jdbi = jdbi;
}
public Product create(Product product) {
return jdbi.inTransaction(h -> {
// This works
ProductDAOAnnotated productDAOAnnotated = h.attach(ProductDAOAnnotated.class);
// This throws NoSuchExtensionException("Extension not found: " + extensionType)
ProductDAO productDAO = h.attach(ProductDAO.class);
// Some code here
return null;
});
}
}
public interface ProductDAOAnnotated {
@SqlUpdate("INSERT INTO product(name) VALUES(:name)")
@RegisterRowMapper(Product.Mapper.class)
<strong i="12">@GetGeneratedKeys</strong>
Product insert(<strong i="13">@BindBean</strong> Product product);
}
public class ProductDAO {
private final Jdbi jdbi;
public ProductDAO(Jdbi jdbi) {
this.jdbi = jdbi;
}
public Product insert(Product product) {
return jdbi.withHandle(h ->
h.createUpdate("INSERT INTO product(name) VALUES(:name)")
.bindBean(product)
.executeAndReturnGeneratedKeys()
.map(new Product.Mapper())
.findOnly()
);
}
}
当您说attach(someclass)
您是在依靠 Jdbi 根据注释为您创建实现。
也就是说,没有什么可以阻止您简单地重用ProductDAO
的打开句柄:
public class ProductDAO {
private final Handle handle;
public ProductDAO(Handle handle) {
this.handle = handle;
}
public Product insert(Product product) {
return handle.createUpdate("INSERT INTO product(name) VALUES(:name)")
.bindBean(product)
.executeAndReturnGeneratedKeys()
.map(new Product.Mapper())
.findOnly();
}
}
因此:
new ProductDao(h).insert(product);
还有一件事:
在您的原始代码示例中,您在ProductService.create()
中调用jdbi.inTransaction()
在ProductService.create()
中调用jdbi.inTransaction()
jdbi.withHandle()
ProductDAO.insert()
。 这两个调用都会在不同的 JDBC 连接上创建单独的、独立的句柄。 因此,您在insert()
方法中所做的任何事情都不会出现在create()
方法的事务中。
啊,是的,当然。
我仍然觉得有点烦人,我需要以两种不同的方式定义相同的函数( insert
),一种用于事务(获取句柄),另一种用于“独立”使用。
就我而言,我正在编写一个 Spring Boot 应用程序,而ProductDAO
是一个 bean。 以“获取这些 DAO 并在事务中使用它们”的方式重用 DAO 会很好,例如在 Spring 中使用@Transactional
通常会这样做,但是像jdbi.inTransaction(...)
那样使其更加明确.
查看未解决的问题,我想这也是 #987 和 #983 的内容。 如果我理解正确,如果#987 被合并,我会通过使用@Transactional
获得我想要的行为吗?
感谢您的回答。
您不需要两次定义相同的 DAO 方法来获得对事务的细粒度控制。
// invoke doStuff() outside of a transaction
jdbi.withExtension(MyDao.class, dao -> dao.doStuff());
jdbi.inTransaction(handle -> {
MyDao dao = handle.attach(MyDao.class);
// Invoke doStuff() as part of a transaction
dao.doStuff();
});
因此,虽然可以使用@Transaction
作为方便,但还有其他选项可以让您更好地控制。
您可能还对Transactional
接口感兴趣,您的 SQL 对象类可以扩展该接口。 这允许您将事务作为 SQL 对象本身的一部分进行控制:
public interface MyDao extends Transactional {
@SqlUpdate("...")
void doStuff();
}
jdbi.useExtension(MyDao.class, dao -> {
dao.begin(); // begin transaction
dao.doStuff();
dao.commit();
}
是的,通过使用接口,我得到了细粒度的控制。 但是,如果我想编写不使用 SQL 对象的 DAO,似乎我必须按照您说的做并将句柄作为参数传递。
就我而言,我编写了多个 DAO,但没有将 SQL 对象用作 Spring Bean。 这意味着我可以通过自动装配想要的 DAO 在我需要的地方使用它们,但是当我需要在同一个事务中调用两个(或更多)单独的 DAO 时,我对 JDBI 似乎不太走运。 然后,我将不得不实现一个 DAO 版本,该版本接受您提到的句柄。 如果@Transactional
(来自 Spring 的那个)得到支持,我将能够重用我的 DAO,而无需任何仅用于事务性使用的特殊代码。
我现在已经改为使用 SQL 对象注释接口。
感谢您的回答!