MST

星途 面试题库

面试题:Java JDBC高级难度:事务管理与批量操作

假设你正在开发一个银行转账的功能,使用Java JDBC进行数据库操作。描述如何在JDBC中进行事务管理,确保转账操作的原子性。并且,若转账操作涉及到多张表的数据更新,同时需要批量插入一些相关的操作记录,如何高效地实现这些功能以提升性能,避免潜在的数据库锁问题?
16.9万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试
  1. 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();
              }
          }
      }
      
  2. 提升性能和避免数据库锁问题
    • 批量操作:如上述代码中对插入操作记录使用addBatch()executeBatch(),减少与数据库的交互次数,从而提升性能。同时批量操作在一定程度上可以减少锁的持有时间。
    • 合理设置事务隔离级别:根据业务需求设置合适的事务隔离级别,例如READ_COMMITTED,在保证数据一致性的前提下,降低锁的粒度和持有时间。
    • 优化SQL语句:确保更新和插入语句的索引使用合理,避免全表扫描,这样可以减少锁的范围。例如,accounts表的account_id字段应该有索引,transactions表的from_account_idto_account_id字段也可以考虑添加索引,以便快速定位数据,减少锁等待时间。
    • 分段操作:如果涉及的数据量非常大,可以考虑分段进行操作。比如将批量插入操作记录分成多个小批次执行,这样可以避免长时间持有锁,提升并发性能。