Jdbi: 在事务中使用“非 sqlobject”DAO

创建于 2018-02-21  ·  5评论  ·  资料来源: jdbi/jdbi

当使用 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()
        );
    }
}
question

所有5条评论

当您说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 对象注释接口。

感谢您的回答!

此页面是否有帮助?
0 / 5 - 0 等级