UPDATE account SET balance = balance - 20000 WHERE id = 'A'
그렇다면 B는 돈을 받는 과정이 필요하다
UPDATE account SET balance = balance + 20000 WHERE id = 'B'
이렇게 두 개의 SQL이 필요한데, 위 SQL 문만 진행되고 밑에는 진행되지 않아서 20,000원이 A 계좌에서는 나가지만 B에는 안들어오는 문제가 생긴다.
그래서 이런 단일한 SQL문을 묶어서 하나의 단위로 만들어줘야 하는데 이걸 Transaction이라고 한다.
START TRANSACTION;
UPDATE account SET balance = balance - 20000 WHERE id = 'A'
UPDATE account SET balance = balance + 20000 WHERE id = 'B'
COMMIT;
MySQL은 autocommit이 있어서 SQL문을 하나씩 돌릴 때마다 자동 저장된다. 만약 문제있으면 rollback이 된다.
하지만 START TRANSACTION이라고 SQL문을 시작하게 되면 autocommit이 자동으로 꺼진다는 것을 잊지말자.
public void transfer(String fromId, String toId, int amount) {
Connection connection = null;
try {
// get DB connection
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/yourdatabase", "username", "password");
connection.setAutoCommit(false); // means START TRANSACTION
// update at fromId
String updateFromAccountSQL = "UPDATE accounts SET balance = balance - ? WHERE id = ?";
try (PreparedStatement updateFromAccountStmt = connection.prepareStatement(updateFromAccountSQL)) {
updateFromAccountStmt.setInt(1, amount);
updateFromAccountStmt.setString(2, fromId);
updateFromAccountStmt.executeUpdate();
}
// update at toId
String updateToAccountSQL = "UPDATE accounts SET balance = balance + ? WHERE id = ?";
try (PreparedStatement updateToAccountStmt = connection.prepareStatement(updateToAccountSQL)) {
updateToAccountStmt.setInt(1, amount);
updateToAccountStmt.setString(2, toId);
updateToAccountStmt.executeUpdate();
}
connection.commit();
} catch (Exception e) {
try {
if (connection != null) {
connection.rollback();
}
} catch (SQLException se) {
se.printStackTrace();
}
e.printStackTrace();
} finally {
try {
if (connection != null) {
connection.setAutoCommit(true);
connection.close();
}
} catch (SQLException se) {
se.printStackTrace();
}
}
}
Try 문으로 잘 되면 commit을 하고 catch에서는 예외가 발생한다면 Exception을 만들면 되는데
특히 Finally에서 Transaction을 시작하면서 오토커밋을 껐기 때문에 다시 True를 사용해서 켜주어야 한다.
J가 H에게 송금했는데, 이 20만원을 받기 위해 먼저 계좌의 잔액을 읽고, 20만원을 더해 적는 과정이 필요하다.
근데 20만원을 보내고 다시 또 30만원을 보내는 과정에서 문제가 생긴다.
Read - Write의 과정에서 Read Read - Write Write가 되면, H의 잔고가 200만원인걸 두 번 Read 하게 되어서 200 + 20, 200 + 30을 하게되어 250이 아니라 230을 받게 된다.
하나의 SQL 조건이 끝나고 다음 조건을 넣어야 하는데 SQL 구동 중간에 또 다른 SQL 문이 들어가서는 안된다. 그걸 막는게 Isolation이다.