面试复盘:JDBC相关问题归纳
在最近的面试中,我遇到了几个关于JDBC的核心问题,这些问题不仅考察了对JDBC基础的理解,还涉及其局限性及解决方案。以下是问题的归纳与解答。
1. JDBC的脏读是什么?哪种隔离级别可以解决脏读?
脏读是什么?
脏读(Dirty Read)是数据库事务中的一种并发问题,指一个事务读取了另一个尚未提交事务修改的数据。如果该未提交事务回滚,读取到的数据就变成了"脏数据",导致不一致性。
示例:
- 事务A将余额从100改为50,但未提交。
- 事务B读取到余额为50。
- 事务A回滚,余额恢复为100,但事务B已基于"脏数据"50执行操作。
解决脏读的隔离级别:
JDBC通过Connection.setTransactionIsolationLevel()
设置事务隔离级别。脏读可以通过以下隔离级别解决:
- Read Committed(读已提交): 确保只能读取已提交的数据,解决了脏读问题。但可能出现不可重复读。
- 更高的隔离级别(如Repeatable Read或Serializable)也能解决,但性能开销更大。
结论: Read Committed 是最低解决脏读的隔离级别,常用且平衡了性能与一致性。
2. JDBC中的execute、executeQuery、executeUpdate有什么区别?
JDBC提供了三种执行SQL的方法,区别如下:
-
execute()
:- 用途: 通用的SQL执行方法,适用于任何SQL语句(查询、更新、DDL等)。
- 返回值: boolean,true表示返回结果集(如SELECT),false表示无结果集(如UPDATE)。
- 使用场景: 不确定SQL类型时使用,需后续调用
getResultSet()
或getUpdateCount()
处理结果。
-
executeQuery()
:- 用途: 专门执行查询语句(如SELECT)。
- 返回值: ResultSet,直接返回查询结果集。
- 使用场景: 明确需要查询数据时使用,简洁高效。
-
executeUpdate()
:- 用途: 执行更新操作(如INSERT、UPDATE、DELETE)或DDL语句。
- 返回值: int,表示受影响的行数。
- 使用场景: 修改数据库时使用,不返回结果集。
总结:
execute
:万能但复杂。executeQuery
:查询专用。executeUpdate
:更新专用。
3. JDBC的不足之处是什么?MyBatis是如何解决的?
JDBC的不足:
- 代码冗余: 手动编写SQL、获取连接、关闭资源,重复性高。
- SQL硬编码: SQL语句写在Java代码中,维护困难,难以动态调整。
- 结果映射繁琐: 从ResultSet手动映射到对象,容易出错且效率低。
- 异常处理复杂: SQLException需要频繁try-catch,代码臃肿。
- 缺乏缓存支持: JDBC本身不提供查询缓存,性能优化受限。
MyBatis如何解决:
- 简化开发: 通过XML或注解配置SQL,减少样板代码。
- SQL分离: SQL语句与Java代码解耦,维护更方便,支持动态SQL。
- 自动映射: 提供结果集到Java对象的自动映射,减少手动转换。
- 资源管理: 集成连接池,自动管理数据库连接。
- 缓存支持: 内置一级缓存(SqlSession级别)和二级缓存,提升性能。
结论: MyBatis在JDBC基础上封装了持久层操作,极大提高了开发效率和灵活性。
4. JDBC的ResultSet是什么?
定义:
ResultSet 是JDBC中表示数据库查询结果的对象,由executeQuery()
返回。它本质是一个游标,指向查询返回的数据行,允许逐行读取数据。
特点:
- 游标移动: 默认只能向前移动(next()),支持双向移动(TYPE_SCROLL_INSENSITIVE)。
- 数据访问: 通过
getXXX()
方法(如getInt()、getString())按列名或索引读取数据。 - 生命周期: 与Statement和Connection绑定,关闭任一都会导致ResultSet不可用。
示例:
java
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT id, name FROM users");
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
System.out.println(id + ": " + name);
}
rs.close();
stmt.close();
类型与并发性:
- 类型: 只读(CONCUR_READ_ONLY)或可更新(CONCUR_UPDATABLE)。
- 滚动性: 只向前(TYPE_FORWARD_ONLY)或可滚动(TYPE_SCROLL_SENSITIVE)。
作用: ResultSet 是JDBC查询的核心桥梁,将数据库数据传递给Java程序。
总结
这些问题涵盖了JDBC的核心概念(事务、执行方法、结果集)及其局限性。通过对比MyBatis,可以看出JDBC虽基础但功能有限,现代框架在生产中更受欢迎。复盘这些问题让我对JDBC的理解更加深入,也为后续优化代码提供了思路。