Whiteship's Note


[하이버네이트 배치 추가] flush와 clear

Hibernate/etc : 2010.07.23 14:18


배치 작업이라는 것이 DB에서 데이터를 읽어온 다음 뭔가 수정하고 뭔가를 다시 DB에 넣는 작업인데 이런 작업을 하이버네이트로 할 때 조심해야 할 것이 있습니다.

                InvDailyClosing invDailyClosing = new InvDailyClosing();
                invDailyClosing.setDate(today);
                invDailyClosing.setLocation(location);
                invDailyClosing.setItem(item);
                invDailyClosing.setQtyStart(qtyStart);
                invDailyClosing.setInvInList(invInList);
                invDailyClosing.setInvOutList(invOutList);
                invDailyClosing.closing();
                dao.add(invDailyClosing);

이렇게 작성하면 끝난것 같지만 사실 좀 위험한 코드입니다. 만약 저 코드가 for 루프 안에 있고 굉장히 여러번 반복 된다면 언젠가는 MemoryOutOfException을 보게 될 겁니다. "아니 왜?" 라고 하시는 분들이 계실텐데요. 

흠... 하이버네이트 1차 캐시 때문에 그런 현상이 생깁니다. 하이버네이트는 Persistent 상태의 객체를 하이버네이트 Session에 담아둡니다. 여기사 1차 캐시입니다. 캐시니까 저안에 담아놓고 재사용할 수 있습니다. DB에 다녀오는 횟수를 줄일 수 있겠죠. 그런데 언젠가는 이 캐시를 DB와 동기화 시켜야 합니다. 그래야 Session에 담겨있는 객체(Pesistent 객체)를 지지고 볶은 것이 DB에 반영이 되겠죠. 그렇게 DB와 Session 상태를 동기화 시키는것을 Flush라고 하는데.. 또 Persistent 상태라는 것은... 아.. 이런.. 안되겠군요. @_@ 그냥 하이버네이트 완벽 가이드를 읽어주세요.

아무튼 너무 많이 쌓여서 메모리가 부족해지는 상황이 발생하지 않도록 계속해서 Session을 DB에 동기화 시키고 Session을 비워주는게 좋습니다.

하이버네이트 Session의 flush()와 clear()이 바로 그런 용도로 존재하죠. 그래서 

                InvDailyClosing invDailyClosing = new InvDailyClosing();
                invDailyClosing.setDate(today);
                invDailyClosing.setLocation(location);
                invDailyClosing.setItem(item);
                invDailyClosing.setQtyStart(qtyStart);
                invDailyClosing.setInvInList(invInList);
                invDailyClosing.setInvOutList(invOutList);
                invDailyClosing.closing();
                dao.add(invDailyClosing);
             dao.flushAndClear();

이렇게 하이버네이트용 GenrericDao에 flushAndClear()라는걸 만들어 놓고 써주면 됩니다. 주의하실 것은... 반드시 flush 먼저 하고 나서 clear 해야 합니다. 반대로 하면 음식 담긴 쟁반을 서빙도 안하고 설것이 해버리는거나 마찬가지..

top

  1. Favicon of https://dazzilove2.tistory.com BlogIcon dazzi 2010.07.24 12:22 신고 PERM. MOD/DEL REPLY

    난 개인적으로 배치는 pl/sql 추천! 데이터베이스의 성능을 최대한 이용하는것을 더 선호하는편이얌. 특히 대형사이트에서 자바배치 돌리는건 상당히 무모해보인다는. ^^;;; 새벽에 다 돌아줘야하는 배치들이 아침까지 돌고있거나 순차적으로 돌아야하는 디펜던시 걸려버리면 진짜 답 안나옴 --;; 쿼리도 oltp성이냐 아니냐에따라 플랜을 다르게 가져가야 하듯이 배치도 상황에따라 수행주체를 바꿔줘야할 필요성이 있는것같오. ㅋㅋ. 생각이 그렇다는거~~ 아무래도 내가 디비도 좋아하다보니 이런글에 굳이 덧글달고 있는듯 ^^;;;

    Favicon of http://whiteship.me BlogIcon 기선 2010.07.26 14:25 PERM MOD/DEL

    티스토리가 이상하네요. 댓글 달렸는데 제목 목록에 표시도 안되고 댓글 목록에도 표시가 안되고.. 흠..

    전 딱히 배치를 꼭 자바로 하자 DB로 하자는 주위는 아닌데 아직 배치 로직 검증 단계라.. (일단 제가 후딱 해볼 수 있는)자바로 배치 로직짜서 검증하고 나중에 그걸 SQL로 바꾸던지 해도 될 것 같네요.

    대용량이 아니라 하더라도 20초면 될 일을 20분 동안 하게 되는 사태는 벌어지면 안되겠죠.

Write a comment.


[하이버네이트 퀴즈] Flush

Hibernate/etc : 2009.06.24 14:08


    @Transactional
    @Test
    public void crud() throws Exception {
        Emp emp = new Emp();
        emp.setName("ks");
        ed.save(emp);

        assertThat(ed.getAll().size(), is(1));
        assertThat(ed.get(emp.getId()).getName(), is("ks"));

        emp.setName("tb");
        ed.update(emp);
        assertThat(ed.get(emp.getId()).getName(), is("tb"));

        ed.delete(emp);
        assertThat(ed.getAll().size(), is(0));
    }

이런 테스트가 있는데, 콘솔 창에 쿼리를 봤더니.

Hibernate: insert into Emp (id, dept_id, name) values (null, ?, ?)
Hibernate: call identity()
Hibernate: select emp0_.id as id4_, emp0_.dept_id as dept3_4_, emp0_.name as name4_ from Emp emp0_
Hibernate: delete from Emp where id=?
Hibernate: select emp0_.id as id4_, emp0_.dept_id as dept3_4_, emp0_.name as name4_ from Emp emp0_

update문이 빠져있다. 여기서 발생하는 의문점이 한 두가지가 아니다.

1. DB에 update가 되지도 않았는데 테스트는 어떻게 통과한 것일까?

2. 왜 update 문은 날아가지 않은 것일까?

3. 역으로, 왜 insert와 delete는 날아간 것일까?

이 세 가지 의문을 해결하려면 위에서 작성한 코드를 좀 더 자세히 살펴볼 필요가 있다. 바로 ed.save(), ed.get(), ed.getAll(), ed.update(), ed.delete()  들이다. 이 녀석들이 어떻게 구현되어 있는지 보지 않고서는 알 수 없다. 또하나 Flush 모드 역시 알아야 한다.

- Flush 모드는 기본 모드인 AUTO를 사용했다.
- save(), get(), update(), delete()는 하이버네이트의 Session API와 동일하다고 생각하면 되며, getAll()은 다음과 비슷하게 구현되어 있다. session.createQuery("from Emp"); 실제로는 이 모든게 GenericDao 구현체에 들어있어서 약간 다르긴 하지만, 본질은 그렇다.
- 테스트는 @Transactional한 녀석으로 기본으로 rollback될 녀석이다.

자.. 이제 위 세가지 질문에 대답할 수 있을 것이다. 그랬다면, 다음 퀴즈도 덤으로 풀어보자.
update 쿼리를 볼 수 있는 방법은 현재 두 가지 정도가 떠오른다.

4. 위 테스트 코드에서 한 줄을 삭제하여 update 쿼리가 콘솔에 찍히게 해보자.

5. 위 코드에 ed.flush()를 어디에 추가하면 update문을 볼 수 있을까?

정답은 비공개.. 영원히..
top

  1. Favicon of http://igooo.org/tc BlogIcon igooo 2009.06.24 23:37 PERM. MOD/DEL REPLY

    emp.setName("tb";); 위에서 flush시키면 update가 나오기

    assertThat(ed.get(emp.getId()).getName(), is("ks";)); 지우면 update가 나타나지 않을까요?


    이클립스 설치하느라 실행은 못시켜봤습니다 .ㅎ

    Favicon of http://whiteship.me BlogIcon 기선 2009.06.25 08:33 PERM MOD/DEL

    안타깝게도.. 둘 다 땡입니다.

  2. koasu 2009.06.25 10:39 PERM. MOD/DEL REPLY

    4번: @Transactional 을 지우면 update 가 나타날꺼 같아요.
    5번: ed.update(emp); ed.flush(); 순으로 하면 update가 나타날꺼 같나요.

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2009.06.25 11:02 신고 PERM MOD/DEL

    4번은 땡. 그거 없이 dao 테스트하기는 좀 그르치요. 당장엔 트랜잭션이 없다고 에러가 날 겁니다. 트랜잭션 설정을 롤백=false로 바꾼다 하더라도 결과는 마찬가지 입니다.

    5번은 맞추셨습니다.

  3. crystal 2009.06.25 11:34 PERM. MOD/DEL REPLY

    4. 위 테스트 코드에서 한 줄을 삭제하여 update 쿼리가 콘솔에 찍히게 해보자.
    // ed.delete(emp);


    5. 위 코드에 ed.flush()를 어디에 추가하면 update문을 볼 수 있을까?
    update()아래, delete()전

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2009.06.25 11:41 신고 PERM MOD/DEL

    딩동댕!

  4. crystal 2009.06.25 11:38 PERM. MOD/DEL REPLY

    1. DB에 update가 되지도 않았는데 테스트는 어떻게 통과한 것일까?
    update전 emp객체가 컨택스트에 관리되는 객체, 즉 persistant 상태에 있기 때문이다.

    2. 왜 update 문은 날아가지 않은 것일까?
    아래 해당 객체의 delete가 있기 때문이다.

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2009.06.25 11:41 신고 PERM MOD/DEL

    딩동댕~

Write a comment.