面试题答案
一键面试- JDBC事务管理确保转账原子性
- 获取数据库连接:
import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; Connection connection = null; try { connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/bank", "username", "password"); } catch (SQLException e) { e.printStackTrace(); }
- 开启事务:默认情况下,JDBC连接是自动提交模式,为了开启事务,需要将自动提交设置为
false
。try { if (connection!= null) { connection.setAutoCommit(false); } } catch (SQLException e) { e.printStackTrace(); }
- 执行转账操作(涉及多张表更新):
假设存在
accounts
表记录账户余额,transactions
表记录转账操作记录。import java.sql.PreparedStatement; import java.sql.SQLException; // 从转出账户减去金额 String subtractSql = "UPDATE accounts SET balance = balance -? WHERE account_id =?"; PreparedStatement subtractStmt = connection.prepareStatement(subtractSql); subtractStmt.setBigDecimal(1, transferAmount); subtractStmt.setInt(2, fromAccountId); subtractStmt.executeUpdate(); // 向转入账户增加金额 String addSql = "UPDATE accounts SET balance = balance +? WHERE account_id =?"; PreparedStatement addStmt = connection.prepareStatement(addSql); addStmt.setBigDecimal(1, transferAmount); addStmt.setInt(2, toAccountId); addStmt.executeUpdate();
- 批量插入操作记录:
String insertSql = "INSERT INTO transactions (from_account_id, to_account_id, amount, transfer_time) VALUES (?,?,?, NOW())"; PreparedStatement insertStmt = connection.prepareStatement(insertSql); insertStmt.setInt(1, fromAccountId); insertStmt.setInt(2, toAccountId); insertStmt.setBigDecimal(3, transferAmount); insertStmt.addBatch(); // 如果有多个类似操作记录,重复addBatch()步骤 insertStmt.executeBatch();
- 提交或回滚事务:
如果所有操作都成功,提交事务;如果任何一步出现异常,回滚事务。
try { if (connection!= null) { connection.commit(); } } catch (SQLException e) { if (connection!= null) { try { connection.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } } e.printStackTrace(); } finally { // 关闭连接等资源 if (connection!= null) { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } }
- 获取数据库连接:
- 提升性能和避免数据库锁问题
- 批量操作:如上述代码中对插入操作记录使用
addBatch()
和executeBatch()
,减少与数据库的交互次数,从而提升性能。同时批量操作在一定程度上可以减少锁的持有时间。 - 合理设置事务隔离级别:根据业务需求设置合适的事务隔离级别,例如
READ_COMMITTED
,在保证数据一致性的前提下,降低锁的粒度和持有时间。 - 优化SQL语句:确保更新和插入语句的索引使用合理,避免全表扫描,这样可以减少锁的范围。例如,
accounts
表的account_id
字段应该有索引,transactions
表的from_account_id
和to_account_id
字段也可以考虑添加索引,以便快速定位数据,减少锁等待时间。 - 分段操作:如果涉及的数据量非常大,可以考虑分段进行操作。比如将批量插入操作记录分成多个小批次执行,这样可以避免长时间持有锁,提升并发性能。
- 批量操作:如上述代码中对插入操作记录使用