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

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

提问人:mattsmith5 提问时间:8/16/2022 最后编辑:mattsmith5 更新时间:8/25/2022 访问量:739

问:

将所有 Java JDBC Select 语句放在 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());
java spring-boot jdbc

评论


答:

1赞 VoidPointer 8/16/2022 #1

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

是的,你只需要在某个时候用 try-catch 块包装代码,尽管最好立即或在逻辑上合适的位置捕获异常。对于 SQL 查询,您实际上应该将它们全部包装到某个类中,这样您就可以修改数据库对象,而无需每次都运行 JDBC API。例如:Service

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 错误,因此您无需经历复杂的调试过程。

评论

2赞 Klitos Kyriacou 8/16/2022
OP 正在使用 Spring Boot,因此 .这允许您使用有意义的参数名称,而不是容易出错的参数编号。namedJdbcTemplate
0赞 VoidPointer 8/16/2022
@KlitosKyriacou,我什至没有注意到,谢谢:D
0赞 Nathan Hughes 8/19/2022
此外,如果 OP 使用 spring boot 和 jdbcTemplate,则异常将转换为 DataAccessExceptions,并且在服务中捕获异常会干扰事务。
0赞 mattsmith5 8/24/2022
顺便说一句,我正在使用 Spring boot,不确定这是否会改变答案
0赞 Ankit Sharma 8/16/2022 #2

如果使用 JDBC 进行查询,则对数据库层代码进行 try-catch 非常重要。

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

对于这些事情,我建议您始终将数据库层代码保持在 try-catch 中。

还建议您在发生上述事件时使用一些回退机制

评论

0赞 mattsmith5 8/24/2022
顺便说一句,我正在使用 Spring boot,不确定这是否会改变答案
0赞 Ankit Sharma 8/24/2022
答案,不会改变。您正在将spring-boot与JdbcTemplate一起使用。
0赞 mattsmith5 8/24/2022
好的,是的,这个答案是说别的,也许你可以解决这个问题,再次感谢,stackoverflow.com/a/73420824/15435022
1赞 Luca Tampellini 8/20/2022 #3

简要说明:

  1. 首先,任何涉及 I/O 访问(数据库访问是 I/O 访问)的资源都必须始终关闭,否则会导致内存泄漏
  2. 其次,最好依靠来关闭任何资源,因为必须手动调用方法总是面临在运行时无法有效执行的风险,因为可能会 // 事先抛出;即使在方法中关闭资源也是不可取的,因为与 - try-with-resources 的自动关闭发生在块的末尾,而在所有 / 块的末尾执行,除了基本问题之外,它不是一个安全的解决方案,因为即使在块内部也可能发生, 阻止它正确完成。try-with-resources.close()ExceptionRuntimeExceptionErrorfinallytry-with-resourcestryfinallytrycatchthrowfinally

也就是说,您始终需要关闭:

  1. Statement/PreparedStatement/CallableStatement
  2. 任何ResultSet
  3. 当您不再需要数据库访问时,整个Connection
3赞 Nathan Hughes 8/20/2022 #4

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

Spring 的重点之一是避免来自开发人员的样板。如果你发现自己重复添加一些东西,比如在执行 DML 的代码周围放置 try-catch 块,那就有理由怀疑你没有做对的事情。使用 Spring 在代码中添加自己的 try-catches 并不总是错误的,但通常是错误的。

在 Spring 参考文档 https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#jdbc 中,有一个表格显示了开发人员的责任和 Spring 的责任。处理异常、处理事务和关闭 jdbc 资源都显示为 Spring 的责任。

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

异常确实需要在某个地方捕获。在 Spring Web 应用程序中,您可以设置一个异常处理程序来捕获从控制器层抛出的任何内容,以便您可以记录它。这样一来,正在进行的操作就会被完全中断,当前事务就会回滚,异常也会以一致的方式处理。如果您有其他入口点,例如从队列中读取消息,则这些入口点将需要自己的异常处理程序。

抛出异常是为了逃避当前上下文,该上下文无法处理问题,并将控制权重新定位到安全的位置。对于大多数来自 JDBC 的异常,它们不是您可以修复的,您只想让它被抛出,让当前事务回滚,然后让中央异常处理程序捕获并记录它。

0赞 utrucceh 8/23/2022 #5

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

原因:例如,您启动了与 db 的连接,然后发生了一些异常,如果您不回滚事务,它会停留在 db 上,性能会降低,并且会发生内存泄漏。

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

但是,如果您想要“最终尝试捕获”的替代方案,则可以这样使用:

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
    }
   
}

评论

0赞 mattsmith5 8/24/2022
顺便说一句,我正在使用 Spring boot,不确定这是否会改变答案
0赞 muhammed ozbilici 8/25/2022 #6

Spring 将这些异常翻译为(有关更多详细信息的链接)。捕获这些异常会很好,您可以使用 .DataAccessException@Transactional