繁体   English   中英

Java JDBC Select 语句是否应该始终位于 ZAD5D946E0C8E258E8023C796FZ 块中?

[英]Should Java JDBC Select statements always be in a Try-Catch block?

将所有 Java JDBC Select 语句放入 ZA75131CB1C90FC1C0AZ52 块中是否是一种好习惯? 目前我在没有它的情况下编写了大部分代码。 但是,我使用 try-catch 进行插入/更新/删除。

注意:当前使用 Sprint Boot。

String sqlQuery = "Select productId, productName, productStartDate from dbo.product where productId = 5"

public getProductData() {
    ....
    List<Product> productList =  namedJdbcTemplate.query(sqlQuery, new ProductMapper());

首先,如果您使用的是原始 JDBC API,则应始终使用PreparedStatement

是的,您只需要在某个时候用 try-catch 块包装代码,尽管立即或在逻辑上适合的地方捕获异常是一种很好的做法。 In case of SQL queries, you actually should wrap all of them into some Service class that will give you an access to modify your database objects without running through JDBC API every time. 例如:

public class UserService {
    private static final String CREATE_USER_SQL = "...";
    private final Connection jdbcConnection;

    public @Nullable User createUser(final String name) {
        try (final PreparedStatement stmt = jdbcConnection.prepareStatement(CREATE_USER_SQL)) {
            jdbcConnection.setAutoCommit(false);
            stmt.setString(1, name);
            stmt.executeQuery();
            jdbcConnection.commit();
            return new User(name);
        } catch (final SQLException createException) {
            System.out.printf("User CREATE failed: %s\n", createException.getMessage());
            try {
                jdbcConnection.rollback();
            } catch (final SQLException rollbackException) {
                System.out.printf("Rollback failed: %s\n", rollbackException.getMessage());
            }
            return null;
        }
    }
}

这立即解决了两个问题:

  1. 您无需将样板代码 JDBC 代码放在任何地方;

  2. 它将立即记录任何 JDBC 错误,因此您无需通过复杂的调试过程来记录 go。

由于这个问题被标记为spring-boot并且您使用的是 JdbcTemplate,因此我给您一个 Spring 特定的答案。

Spring 的要点之一是避免开发人员的样板。 如果您发现自己重复添加内容,例如将 try-catch 块放置在执行 DML 的代码周围,那么就会怀疑您做错了什么。 使用 Spring 在代码中添加自己的 try-catch 并不总是错误的,但通常是错误的。

SpringJdbc 为您处理了很多事情。 它处理关闭 JDBC 资源并将连接返回到它们的池,并将异常从 SQLException 转换为未经检查的 DataAccessExceptions 的层次结构。 在 Spring 中,包装在事务代理中的方法抛出的未经检查的异常会导致事务回滚。 如果您执行自己的 try-catch 逻辑,则可以防止在需要时发生回滚,如果您捕获异常并且代理永远不会看到它。

确实需要在某个地方捕获异常。 在 Spring web 应用程序中,您可以设置一个异常处理程序来捕获从 controller 层抛出的任何内容,以便您记录它。 抛出异常是为了逃避无法处理问题的当前上下文,并将控制权重新定位到安全的地方。 对于大多数来自 JDBC 的异常,它们不是您可以修复的,您只想让它被抛出,让当前事务回滚,然后让中央异常处理程序捕获并记录它。

如果您使用 JDBC 进行查询,则 DB 层代码的 Try-catch 很重要。

想一想,如果连接中断了怎么办? 或者如果数据库崩溃了怎么办? 或者出现其他一些不幸的情况。

对于这些事情,我建议您始终将 DB 层代码保留在 try-catch 内。

还建议您有一些后备机制以防上述事件。

简要说明:

  1. 首先,任何涉及 I/O 访问的资源(数据库访问就是 I/O 访问)必须始终关闭,否则会导致memory 泄漏
  2. 其次,最好依靠try-with-resources来关闭任何资源,因为必须手动调用.close()方法总是面临由于潜在的Exception /运行时RuntimeException / Error而无法在运行时有效执行的风险被提前扔掉; 即使在finally方法中关闭资源也是不可取的,因为与 try-with-resources 相比,这样的块在不同的阶段执行 - try-with-resources的自动关闭发生在try块的末尾,而finally在所有try / catch块的结束 - 除了它不是安全解决方案的基本问题之外,因为即使在finally块内也可能发生throw ,从而阻止它正确完成。

这就是说,你总是需要关闭:

  1. Statement / PreparedStatement / CallableStatement
  2. 任何ResultSet
  3. 当您不再需要数据库访问时,整个Connection

您应该始终使用 try cactch 来处理它。

原因:例如,您开始连接到 db,然后发生了一些异常,如果您不回滚事务,它会留在 db 上并且性能会降低,并且会发生 memory 泄漏。

想象一下,如果您的连接限制是 100 并且在事务开始后抛出 100 异常并且您没有回滚它,您的系统将被锁定,因为您无法创建与数据库的任何新连接。

但是,如果您想要“try catch finally”的替代方法,您可以这样使用:

EmUtil.consEm(em -> {
    System.out.println(em.createNativeQuery("select * from temp").getResultList().size());
});

源代码:

public final class EmUtil {

    interface EmCons {
        public void cons(EntityManager em);
    }

    public static void consEm(EmCons t) {
        EntityManager em = null;
        try {
            em = getEmf().createEntityManager();
            t.cons(em);
        } finally {
            if (em != null && em.getTransaction().isActive())
                em.getTransaction().rollback();
            if (em != null && em.isOpen())
                em.close();
        }
    }

    private static EntityManagerFactory getEmf() {
        //TODO
    }
   
}

Spring 将这些异常转换为DataAccessException (更多详细信息链接)。 捕获这些异常会很好,您可以使用@Transactional回滚。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM