잘못 된 openSession() 사용 예제ㅋ

이전 글에서 DAO 구현을 다음과 같이 했었습니다.

public class MemberDaoImpleWIthSpringTransaction implements MemberDao{

    private SessionFactory sessionFactory;

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    public void add(Member member) {
        Session session = sessionFactory.getCurrentSession();
        session.save(member);
    }
}

여기서 빨간 색 부분은 찬욱군이 알려줬기 때문이고 원래는 아래처럼 코딩했었습니다.

    public void add(Member member) {
        Session session = sessionFactory.openSession(); //(1)
        session.save(member);
        session.flush(); //(2)
        session.close(); //(3)
    }

위에 표시한 빨간 부분이 모두 잘못된 부분이였습니다.

(1) 오픈 세션은 새로운 세션을 만들게 되고 그럼 새로운 트랜잭션에서 save를 실행하게 됩니다. 그런데 지금 저 코드는 새로운 트랜잭션이 아니라 Service Layer에서 사용하던 트랜잭션을 사용해야 합니다. 그러려면 새로운 세션이 아닌 getCurrentSession을 사용하면 기존에 존재하는 트랜잭션을 사용하게 됩니다.

(2) flush()는 무조건 DB에 저장하게 된다고 합니다. 따라서 삭제해야합니다. 지금 이 코드는 트랜잭션 내에 있고 예외가 발생하면 롤백해야 되니까 무조건 DB에 넣어버리는 flush()를 사용하면 안되겠습니다.

(3) getCurrentSession은 자기가 알아서 세션을 닫기 때문에 명시적으로 session을 닫아버리면 안됩니다.(에러가 나더군요.)

찬욱군 베리베리 땡큐 감사!!

Transaction

bl119.bmp[출처 : Hibernate In Action 5.1 Understanding database transactions]

트랜잭션의 특징(ACID)

■ Atomic : 트랜잭션은 하나 이상의 활동들을 묶어놓은 작업의 단위다. 원자성은 이 단위에 있는 모든 활동들이 전부 발생하거나 전부 발생하지 말아야 하는 것을 말한다. 만약 모든 활동들이 원활하게 진행되면 트랜잭션은 성공한다. 그러나 어떤 활동이라도 문제가 발생하면 롤백 하게 된다.

■ Consistent : 트랙잭션이 종료(잘 됐든 안됐든)되면, 시스템은 정상적으로 가동 되는 상태여야 한다. The data should not be corrupted with respect to reality.

■ Isolated : 트랜잭션은 여러명의 유저들이 각각 엉키지 않고 같은 데이타에 접근하는 것이 가능해야 한다. 따라서 트랜잭션은 각각에 독립적이어야 하며 같은 데이타에 동시에 읽거나 쓰는것을 방지해야 한다.(Note that isolation typically involves locking rows and/or tables in a database.) => row나 table별로 locking을 시도 한다고 한느데 이건 마치 자바에서 동기화 처리할 때 객체들의 key나 클래스의 key로 locking할 수 있는 것과 매칭이 됩니다.

■ Durable : 한번 트랜잭션이 완료되면 그 결과는 어떤 종류의 시스템 오류가 발생 하더라도 영구적으로 보존 되어야 한다. 보통 DB나 다른 종류의 Persistent 저장소에 저장하는 일을 포함한다.
[참고 : Spring In Action 5.1.1 Explaining transactions in only four words]

소스코드로 확인 하기

피자의 가격이 음수이면 UnderZeroException이 발생하도록 Pizza의 setPrice를 수정합니다.

public void setPrice(Integer price) throws UnderZeroException {
        if (price < 0) {
            throw new UnderZeroException();
        }
        this.price = price;
    }

그리고 PizzaApp 에서 setPice를 호출 하는 부분을 try-catch 블럭으로 묶습니다.[footnote]Alt + Shift + z -> y[/footnote] 그리고 catch 블럭에서 tx.rollback()을 호출합니다.
try {
            Pizza pizza = new Pizza();
            pizza.setName(“Delicious”);
            pizza.setPrice(-100);
            pizza.setSize(“Large”);
            pizza.setToping(“Shrimp & Stake”);
            s.save(pizza);
            tx.commit();
        } catch (UnderZeroException e) {
            tx.rollback();
            e.printStackTrace();
        } finally {
            s.close();
        }

이제 위의 트랜잭션은 롤백되서 DB에 저장되지 않습니다.