提问人:mattsmith5 提问时间:8/16/2022 最后编辑:mattsmith5 更新时间:8/25/2022 访问量:739
Java JDBC Select 语句是否应该始终位于 Try-Catch 块中?
Should Java JDBC Select statements always be in a Try-Catch block?
问:
将所有 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());
答:
首先,如果您使用的是原始 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;
}
}
}
这立即解决了两个问题:
您不需要将样板 JDBC 代码放在任何地方;
它会立即记录任何 JDBC 错误,因此您无需经历复杂的调试过程。
评论
namedJdbcTemplate
如果使用 JDBC 进行查询,则对数据库层代码进行 try-catch 非常重要。
想一想,如果连接中断了怎么办?或者如果数据库崩溃怎么办?或者出现其他一些不幸的情况。
对于这些事情,我建议您始终将数据库层代码保持在 try-catch 中。
还建议您在发生上述事件时使用一些回退机制。
评论
简要说明:
- 首先,任何涉及 I/O 访问(数据库访问是 I/O 访问)的资源都必须始终关闭,否则会导致内存泄漏。
- 其次,最好依靠来关闭任何资源,因为必须手动调用方法总是面临在运行时无法有效执行的风险,因为可能会 // 事先抛出;即使在方法中关闭资源也是不可取的,因为与 - try-with-resources 的自动关闭发生在块的末尾,而在所有 / 块的末尾执行,除了基本问题之外,它不是一个安全的解决方案,因为即使在块内部也可能发生, 阻止它正确完成。
try-with-resources
.close()
Exception
RuntimeException
Error
finally
try-with-resources
try
finally
try
catch
throw
finally
也就是说,您始终需要关闭:
Statement
/PreparedStatement
/CallableStatement
- 任何
ResultSet
- 当您不再需要数据库访问时,整个
Connection
由于这个问题被标记为 并且您正在使用 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 的异常,它们不是您可以修复的,您只想让它被抛出,让当前事务回滚,然后让中央异常处理程序捕获并记录它。
您应该始终使用 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
}
}
评论
Spring 将这些异常翻译为(有关更多详细信息的链接)。捕获这些异常会很好,您可以使用 .DataAccessException
@Transactional
评论