작성
·
489
0
안녕하세요.
현업에서 JPA를 사용하게 되어 JPA 기본편을 듣고 JPA 활용을 듣고 있는 중입니다. 책도 사서 틈틈이 보고 있구요..
우선은 좋은 강의 정말 감사하다고 말씀드리고 싶구요. 이해에 정말 큰 도움이 되고 있습니다.
저는 지금 Spring Quartz 를 통해 배치를 돌면서 데이터를 생성/수정/삭제하는 어플리케이션 개발에 참여하게 되었습니다.
그 과정에서 아무리 삽질을 해도 이해가 안 되는 부분이 있어 질문드립니다..ㅠㅠ
질문은 크게 2가지 입니다.
1. Repository에 @Transactional 어노테이션이 없으면 "no entity manager 에러"
2. @Transactional 을 가진 Service와 @Transactional을 가진 Repository 를 오가며 쿼리하는 경우 "detached 에러"
첫번째는 크게 중요한 건 아닌데 두번째 질문과 연관되어 있는 내용인 거 같아서 질문 드립니다..
강사님은 Repository에 @Transactioanl 어노테이션을 안 붙이시는데,,
제가 현재 개발하고 있는 환경에서는 Repository에 @Transactioanl 어노테이션을 붙이지 않으면
"No EntityManager with actual transaction available for current thread" 에러가 발생합니다.
Spring Quartz 특성상 멀티 쓰레드로 동작하기 때문에 그런 거 같은데 모르겠네요 ㅠㅠ
참고로 repository는 @Autowire 가 아니라 아래처럼 직접 applicationContext에서 꺼내서 사용하고 있습니다.
두번째가 저에게 현재 큰 어려움을 주고 있는 부분인데요 ㅠ
영속성 관리에 대한 질문입니다..
이런 Id를 멀티 키로 2개 사용하는 엔티티가 있다고 할 때,
이렇게 레포지토리에서 2개의 메소드를 만듭니다.
1. ID 중 하나를 조건으로 목록을 조회하는 메소드
2. 목록을 주면 순회하며 삭제하는 메소드
이제 서비스에서 해당 목록 조회 메소드 호출 후 삭제 메소드를 호출했을 때,
저의 예상은 이렇습니다.
1. 목록 조회 시 영속성 컨텍스트에 객체 목록이 올라감 > select 쿼리 1번 호출
2. 컨텍스트에 있는 객체 목록을 삭제함 > delete 쿼리 N번 호출
하지만 실제 동작은 이렇습니다.
1. 목록 조회 -> select 쿼리 1회 호출
2. 목록 삭제 -> 객체 삭제 -> select 쿼리 1회 호출 후 "Removing a detached instance" 에러 발생
"Removing a detached instance ~~.SomeEntity#SomeEntityId nested exception is java.lang.IllegalArgumentException"
왜 그러는 걸까요..??? ㅠㅠㅠ
remove 를 요청했는데 select 쿼리가 날라가는 이유는 또 뭘까요..???
1. 목록 조회 메소드에서 삭제까지 바로 이어서 진행하면 또 예상대로 삭제가 잘 진행됩니다..
2. 삭제 메소드에서 다시 한 번 조회 쿼리를 호출한 후 삭제해도 삭제가 됩니다..
즉, 레포지토리에서 조회->삭제하면 정상 삭제가 되는데,
서비스 -> 레포지토리:조회 -> 서비스 -> 레포지토리:삭제 처럼 서비스와 레포지토리를 거쳐가면 detached 에러가 발생하는 상황입니다..
영속성 컨텍스트가 서비스 클래스까지 유지가 안 되는 걸까요?
유지시키려면 어떻게 해야할까요..???
영속성 컨텍스트가 내부적으로 어떻게 동작하고 있는 건지 알 수가 없네요 ㅠㅠ
질문이 조금 길어졌지만.. 답변해주시면 정말 감사할 거 같습니다..ㅠㅠ 부탁드립니다..!
답변 3
1
안녕하세요. 이영규님
영속성 컨텍스트는 스프링의 트랜잭션 범위와 같다고 생각하시면 됩니다.
그런데 지금 SampleTask가 제 생각에는 @Transactional이 붙어있지만 트랜잭션이 정상 동작하지 않은 것 같아요.
이 부분에 대한 점검이 먼저 필요합니다.
그런데 우선은 다음과 같이 한번 진행해보세요.
XxxService를 만들고 여기에 트랜잭션을 붙인 다음 다음과 같이 이 서비스를 꺼내서 한번 사용해보세요.
XxxService xxxService = getBean(XxxService);
SampleTask는 단순히 이 서비스의 로직을 호출하기만 하는 것으로 변경해주세요.
xxxService.execute(..)처럼요
그리고 모든 로직은 이 서비스 안에 있어야 합니다. 이 서비스 밖에 나오는 순간 트랜잭션이 종료되면서 영속성 컨텍스트도 날라가버립니다.
감사합니다.
0
ㅎㅎ 네 잘 찾으셨네요. new Service를 하게되면 스프링이 관리하지 않기 때문에 트랜잭션 관리가 안되겠지요.
(팀 분들은 집에서도, 회사에서도 제 목소리가 들린다고 합니다 ㅋㅋ)
0
감사합니다..!
말씀해주신대로 테스트해봤더니 잘 되더라구요.
원인은 아직 정확히는 이해하지 못 했지만,,
결론은 이렇습니다.
new Service() 로 Service 생성 -> 서비스 내부에서 getBean으로 Repository 생성 : Service와 Repository 간의 Transactional 유지 안 됨
getBean으로 Service 생성 -> 서비스 내부에서 @Autowire 로 Repository 생성 : Service와 Repository 간의 Transactional 유지 됨
애초에 Service를 new Service()로 생성한 거부터 문제였던 거 같아요.
덕분에 문제 잘 해결했습니다. 정말 감사합니다.
요즘에 여자친구 목소리보다 선생님 목소리를 더 많이 듣고 있는 거 같습니다..ㅎㅎ
앞으로도 열심히 수강하겠습니다. 감사합니다.