
기본적으로는 각각의 쿼리를 수행하고 난 다음 자동으로 커밋이 된다. 하지만 이렇게 쿼리 하나마다 자동으로 커밋을 하면 작업을 묶어서 수행하는 트랜잭션의 의미를 살리기 어렵다. 따라서 수동 커밋을 지원한다.
트랜잭션 시작시 자동 커밋을 끄고 마지막에 커밋이나 롤백을 직접 호출하는 것이다. 직접 호출하면서 어디서 부터 어디까지 하나의 단위 즉 트랜잭션으로 묶을지를 정할 수 있다.
트랜잭션 시작시 set autocommit false 라 적고 트랜잭션 종료 시 commit이나 rollback을 호출해야 한다.
한 쪽에서 데이터를 추가하고 커밋 하지 않은 예시

디비에는 임시 상태로 들어가 있고 왼쪽 유저와 관련된 세션에만 임시 데이터가 들어있다.
첫번째 예시에서 커밋을 하면

디비의 임시 상태 데이터가 완료로 바뀌고 다른 세션에서도 조회할 수 있다.
첫번째 예시에서 롤백을 하면

세션과 디비 모두 트랜잭션을 시작하기 전 상태로 돌아가는 것을 볼 수 있다.
한 쪽에서 데이터를 수정하는 예시
한 쪽에서 데이터를 수정하면 디비의 원래 데이터는 임시 상태로 변한다. 수정을 시도한 쪽의 세션에는 변경이 된 임시 데이터가 있고 다른 세션에서 데이터를 조회하면 수정 전 원래 데이터가 보인다.

이후 커밋을 하고 반대편에서 다시 조회하면 변경된 데이터가 보이고 롤백을 하고 반대편에서 다시 조회하면 원래 데이터가 보인다.
세션1이 트랜잭션을 시작하고 데이터를 수정하는 동안 커밋하지 않았는데도 세션2가 동시에 같은 데이터를 수정하려고 하면 문제가 발생한다.
따라서 세션이 트랜잭션을 시작하면 커밋이나 롤백 전까지 다른 세션이 데이터를 수정하지 못하게 만들어야 한다. 이를 위해 락이 존재한다.
만약 한 세션이 특정 레코드의 데이터를 변경하려면 먼저 그 레코드에 대한 락을 얻어야 한다. 어떤 세션도 해당 레코드를 건들지 않아 락이 남아있으면 락을 획득할 수 있다.
이후 늦게 온 세션들은 데이터를 변경하려면 락이 풀릴 때까지 기다려야 한다. 무한정 기다릴 수는 없으므로 타임아웃을 설정한다. 타임아웃이 만료되었는데도 락을 얻지 못하면 오류가 발생한다.
조회를 할 때는 락을 얻지 않아도 조회할 수 있다. 한 세션에서 데이터를 변경하고 있어도 위의 예시에서 본 것처럼 다른 세션에서는 트랜잭션 시작 전의 데이터를 조회할 수 있다.
조회 시점에 락을 걸 수도 있다. 이를 위해선 select 구문 뒤에 for update라고 적어 select for update 구문을 사용하면 된다. 이걸 사용하면 지금은 단순 조회하는 거지만 이 데이터가 나중에 변경될 것이므로 락을 건다.
비지니스 로직은 다음과 같이 수행이 된다.

비지니스 로직을 하나의 트랜잭션으로 묶어야 하므로 트랜잭션은 서비스 계층에서 시작되어야 한다.
이를 위해 con.setAutoCommit(false); 로 자동 커밋을 수동 커밋으로 바꿔야 한다. 이 메소드는 오토 커밋을 끄는 명령을 디비에 날린다.
비지니스 로직을 트랜잭션으로 설정하였으면 트랜잭션 수행을 위해 먼저 커넥션을 얻어야 한다. 따라서 서비스 계층에서 커넥션을 생성해야 한다.
트랜잭션을 수행하는 동안 같은 세션을 사용해야 하므로 트랜잭션 수행하는 동안은 같은 커넥션이 유지되어야 한다. 따라서 하나의 커넥션을 유지하기 위해 서비스 계층에서 생성한 커넥션은 리포지토리로 매개변수를 통해 넘겨주어 동일한 커넥션을 사용하도록 한다.
리포지토리는 받은 커넥션을 작업 완료 후 닫으면 안 된다. 왜냐하면 트랜잭션 수행동안 다른 리포지토리 등에서 또 사용해야 할 수도 있기 때문이다.
따라서 커넥션의 종료 및 반환은 서비스 계층으로 돌아와 트랜잭션은 커밋하거나 롤백해서 종료하고 난 후에 이뤄져야 한다.
커넥션 풀에 커넥션을 반환할 때 이 커넥션은 현재 자동 커밋이 꺼진 상태이다. 따라서 반환하기 전에 con.setAutoCommit(true)을 호출하여 기본값으로 바꿔야 한다. 그래야 다른 사람이 이 커넥션을 받았을 때 자동 커밋을 끄면서 트랜잭션을 시작할 수 있다.