//---------------------------------------------------------------
// 트랜잭션
// 하나라도 예외가 나면 안됨
//---------------------------------------------------------------
if (bDao.insertNewBoard(newBoard) == 1) { // 게시글 저장 성공함?
System.out.println("insertNewBoard 통과");
int newBoardNo = bDao.selectMaxBoardNo();
for (BoardUpFilesVODTO file : newBoard.getFileList()) {
file.setBoardNo(newBoardNo);
bDao.insertBoardUpFile(file); // 파일 리스트도 보내야지
}
// 로그도 써야지
if (pDao.insertPointLog(new PointLogDTO(newBoard.getWriter(), "글작성")) == 1) {
System.out.println("insertPointLog 통과");
if (mDao.updateUserPoint(newBoard.getWriter()) == 1) {
System.out.println("updateUserPoint 통과");
result = true;
}
}
}
return result;
트랜잭션은 ACID라는 특징이 있다. 이건 이론적인 어려운 이야기지만...
위의 코드를 보면, 게시글 저장과 파일 리스트 삽입과 로그 쓰기는 같이 일어나야 한다. 어느 하나는 성공했는데 어느 하나는 실패하면 안됨... 이를 원자성(atomicity) 한다.
스프링만의 트랜잭션 처리는 어떻게 할까? 스프링에서 트랜잭션 처리는 어떤식으로 이루어질까?
@Transactional어노테이션을 쓰면 된다.
이거 쓰려면 아래와 같이 설정해야 한다.
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
이러면 어노테이션이 붙은 메소드에 대해서 transactionManager가 커밋과 롤백을 적절하게 수행 할 것이다.
반대로 말해서 커밋하고 롤백을 수동으로 잘 할 수 있다면 @Transactional를 굳이 안쓸 수도 있다. 하지만 spring에서는 이게 편리하다.
spring-aop가 좀더 범용적인 개념이다. AOP는 트랜잭션도 있지만 로깅, 캐싱 기능도 만들 수 있기 떄문에...

com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction오류가 나는 원인은 무엇일까?
@Transactional 어노테이션을 쓰면 자동 커밋이 일어난다. 그런데 내 경우엔 어쩐 이유에서인지 커밋이 일어나지 않았다. 어떻게 확인했냐면, 테스트 코드에서 오류는 안 뜨지만 MySQL Workbench에서 select 문을 날렸을 때 갱신이 안되었다.
그래서 커밋이 안 된 상태에서 또 테스트 코드를 실행하면 커밋이 안된 트랜잭션하고 새로운 트랜잭션이 충돌을 일으켜서 lock wait 오류가 나는 것으로 추정된다. Workbench에서 commit; 명령을 수동으로 날려주면 잘 되기 때문에...
파워셀에서 MySQL 서버를 재시작할 수 있다.

그러면 남아있는 트랜잭션이 다 없어지기 때문에 깨끗한 상태에서 시작할 수 있다.
UPDATE, INSERT, DELETE 구문이 있다. 이 구문은 commit을 하기 전까지 rollback이 가능하다.
SELECT는 commit을 할 필요도 없고 rollback도 되지 않는다.