영속성 컨텍스트와 트랜잭션 관련 에러

Ryu·2023년 2월 10일
0

JPA

목록 보기
1/4
post-thumbnail

문제 상황

  • JPQL 을 통해 ADMIN(관리자)인 회원들을 조회하는 과정에서 조회 쿼리도 나가지 않고, 엔티티 객체 정보도 저장되지 않는 문제가 발생.

실제 구현 코드

try {
    Team team = new Team();
    team.setName("teamA");
    em.persist(team);

    Member member = new Member();
    member.setUsername("Spring1");
    member.setAge(15);
    member.setTeam(team);
    member.setType(MemberType.ADMIN);

    em.persist(member);

    em.flush();
    em.close();

    String query = "select m.username, 'HELLO', true from Member m " +
                    "where m.type = :memberType";
    List<Object[]> result = em.createQuery(query)
            .setParameter("memberType", MemberType.ADMIN)
            .getResultList();
    for (Object[] objects : result) {
        System.out.println("objects[0] = " + objects[0]);
        System.out.println("objects[1] = " + objects[1]);
        System.out.println("objects[2] = " + objects[2]);
    }

    tx.commit();
}
  • 코드만 보고 무엇이 문제인지 알았다면, 영속성 컨텍스트와 트랜잭션과 관련한 이해도가 높다고 할 수 있다. (거의 1시간 이상을 헤맸다..)
  • 런타임 에러도 발생하지 않아서, 엄청 간단한 문제인데도 시간이 오래 걸림.

해결

  • 해결 방법은 너무 간단했다. em.close() 가 문제였던 것.
    em.close() 는 영속성 컨텍스트를 종료해버리고, 내부적으로 DB Connection 을 반환한다.
  • 즉, tx.commit() 이라는 트랜잭션 커밋 단계 전에 엔티티 매니저를 통한 DB Connection 을 끊어버렸기 때문에, 결과적으로 DB 에는 저장이 안된 것이다.
  • 헷갈릴 수 있었던 부분은, em.flush() 를 통해 INSERT 쿼리는 정상적으로 나간다는 것이다. 그럼 보통 이 때 DB 에 내용들이 반영된다고 생각하는데, 반은 맞고 반은 틀린 얘기다.
    트랜잭션 생명주기가 끝나고, 엔티티 매니저를 종료해줘야 정상적으로 DB Connection 을 반환하면서 최종 저장되는 것이다.
  • 실제로, finally 부분에 em.close() 를 빼도, INSERT 쿼리는 나가지만 DB 에 들어가면 반영되지 않는 것을 확인할 수 있다.

느낀점...

  • 해결 방법은 너무 쉽지만, 관련 개념들을 계속 찾아보니 생각보다 어려운 부분들도 많았다. JPA 가 객체와 테이블 사이에서 쿼리를 매핑해주는 역할을 하는 만큼, JDBC API를 사용해 Database 와 통신까지 담당한다는 것을 다시 한 번 느끼는 시간이었다.
profile
Strengthen the core.

0개의 댓글