Java ATM CLI Dev Log #2:转账,卡住了?
I’m happy to translate the article for you, but I’ll need the full text of the post (the paragraphs, headings, etc.) in order to do so. Could you please paste the content you’d like translated? I’ll keep the source link at the top exactly as you requested and preserve all formatting, code blocks, URLs, and technical terms.
Narrative
我从没想到构建这个功能会如此疯狂。开发过程中,一切都很顺利——直到函数在执行时开始挂起。
我使用 ChatGPT 和 Gemini 进行深入调查,以找出问题根源。在此过程中,我了解了 COMMIT、ROLLBACK,以及事务在数据库中为何至关重要——尤其是对我正在构建的金融应用而言。
事务 是一系列被视为单一逻辑单元的 SQL 语句。事务以数据更改操作开始,并以
COMMIT(成功)或ROLLBACK(错误)结束。
初始实现
AccountService.java
public static void transferCash(Account sender, Account receiver, double amount) {
if (sender.getBalance() > amount) {
double newSenderBalance = sender.getBalance() - amount;
double newReceiverBalance = receiver.getBalance() + amount;
boolean isTransactionSuccessful = AccountDAO.makeTransfer(
sender.getId(),
receiver.getId(),
sender.getAccountType(),
receiver.getAccountType(),
newSenderBalance,
newReceiverBalance
);
if (isTransactionSuccessful) {
System.out.println("Transaction Successful!");
sender.setBalance(newSenderBalance);
System.out.println("Withdrawal successful. New balance: $" + sender.getBalance());
sender.stringifyAccount();
} else {
System.out.println("Transaction Failed!");
}
} else {
System.out.println("Insufficient funds.");
}
}
AccountDAO.java
public static boolean makeTransfer(
int senderId,
int receiverId,
String senderAccountType,
String receiverAccountType,
double newSenderBalance,
double newReceiverBalance) {
String senderQuery = "UPDATE accounts SET balance = ? WHERE customerId = ? AND accountType = ?";
String receiverQuery = "UPDATE accounts SET balance = ? WHERE customerId = ? AND accountType = ?";
boolean isTransactionSuccessful = false;
try (Connection conn = DBHelper.getConnection()) {
conn.setAutoCommit(false);
// ---- Sender ----
try (PreparedStatement senderStmt = conn.prepareStatement(senderQuery)) {
senderStmt.setDouble(1, newSenderBalance);
senderStmt.setInt(2, senderId);
senderStmt.setString(3, senderAccountType);
int senderRowsAffected = senderStmt.executeUpdate();
if (senderRowsAffected == 0) {
conn.rollback();
return false;
}
} catch (SQLException e) {
e.printStackTrace();
conn.rollback();
return false;
}
// ---- Receiver ----
try (PreparedStatement receiverStmt = conn.prepareStatement(receiverQuery)) {
receiverStmt.setDouble(1, newReceiverBalance);
receiverStmt.setInt(2, receiverId);
receiverStmt.setString(3, receiverAccountType);
int receiverRowsAffected = receiverStmt.executeUpdate();
if (receiverRowsAffected == 0) {
conn.rollback();
return false;
}
} catch (SQLException e) {
e.printStackTrace();
conn.rollback();
return false;
}
conn.commit();
System.out.println("\nBalance Updated Successfully!");
isTransactionSuccessful = true;
} catch (Exception e) {
e.printStackTrace();
isTransactionSuccessful = false;
}
return isTransactionSuccessful;
}
Source: …
重构后根据高级工程师反馈
一位拥有银行 Java 经验的高级工程师指出,之前的做法是 不良实践。
服务层已拆分为两个更小、单一职责的操作:借记(debit) 和 贷记(credit)。
更新后的 AccountService.java
public class AccountService {
public static void debitAccount(Account account, double amount) {
if (account.getBalance() > amount) {
double newBalance = account.getBalance() - amount;
boolean isTransactionSuccessful = AccountDAO.changeBalance(
account.getId(),
account.getAccountType(),
newBalance
);
if (isTransactionSuccessful) {
System.out.println("Transaction Successful!");
account.setBalance(newBalance);
System.out.println("Withdrawal successful. New balance: $" + account.getBalance());
account.stringifyAccount();
} else {
System.out.println("Transaction Failed!");
}
} else {
System.out.println("Insufficient funds.");
}
}
public static void creditAccount(Account account, double amount) {
double newBalance = account.getBalance() + amount;
boolean isTransactionSuccessful = AccountDAO.changeBalance(
account.getId(),
account.getAccountType(),
newBalance
);
if (isTransactionSuccessful) {
System.out.println("Transaction Successful!");
account.setBalance(newBalance);
System.out.println("Deposit successful. New balance: $" + account.getBalance());
account.stringifyAccount();
} else {
System.out.println("Transaction Failed!");
}
}
}
注意:
AccountDAO.changeBalance是一个新辅助方法,用于更新单个账户的余额。
现在每一次数据库操作都是原子性的,调用方可以在适当的事务范围内编排完整的转账流程(借记 → 贷记)。
当前状态
在完成所有这些更改后,我决定暂停此功能的开发,先完成应用程序的其余部分,然后再回来正确地修复转移逻辑。
随后,我应该已经掌握了在 Java 中正确处理事务的方法。
如果你想查看 GitHub 仓库,可以点击这里自行查看。
好了,暂时就这些。
下次提交再见 👋