작성
·
260
0
안녕하세요 스프링 부트 환경에서 JPA 를 쓸때 이해하지 못하는 에러가 나서 질문합니다.
현재 서비스와 레포지토리 레이어를 분리해서 사용하고 있고, @Transactional 어노테이션은 레포지토리 클래스에만 적용해 놓은 상태입니다.
우선 save 함수와 findOne 함수는 이런식으로 존재합니다.
public void save(Lost entity)
{em.persist(entity);}
public Optional<Lost> findOne(Long id)
{return Optional.ofNullable(em.find(Lost.class, id));}
Lost 엔티티는 이런식으로 존재합니다 (더 많은 컬럼이 있지만 필요한 것만 적었씁니다)
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "lost")
@Entity
@JsonIgnoreProperties({"organization", "postCodeType"})
public class Lost {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "lost_id")
private Long id;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "lost_user_id", referencedColumnName = "user_id", insertable = false, updatable = false)
@EqualsAndHashCode.Exclude
@ToString.Exclude
private User user;
}
근데 서비스 레이어에서
Lost result = lostsRepository.save(LostMapper.INSTANCE.toEntity(lostRequest));
System.out.println(lostsRepository.findOne(result.getId()).get().getUser());
이런식으로 findOne을 해서 user 필드를 가져오려고 하면 null 이 뜹니다ㅠㅠ
@ManyToOne(fetch = FetchType.EAGER)
이 설정도 잘 해줬고, 커밋도 레퍼지토리에서 해서 그러면 user 필드가 채워져서 와야 할텐데 이해가 안됩니다..ㅠ @ToString.Exclude 문제는 아닌게 getUser 을 해서 봤을때도 null 이 뜹니다. 혹시 뭐가 문제일까요? findOne을 쓰는 다른 곳에서는 잘 동작하는데, 저렇게 save 를 한 직후에 save 한 엔티티 아이디로 findOne을 하면 조인한 다른 객체가 null로 뜹니다.
추가로 우선 해당문제는
public void save(Lost entity)
{em.persist(entity);
em.flush();
em.refresh(entity);
}
이런식으로 바꿔서 해결했습니다.
메커니즘이 이해가 안가서 질문드립니다!!
감사합니다
답변 1
1
안녕하세요, 인프런 AI 인턴이에요.
영속성 컨텍스트 에러는 JPA 에서 관리하는 엔티티 객체와 데이터베이스의 데이터가 일치하지 않아서 발생할 수 있습니다. 이 경우, 엔티티 객체를 수정한 후 트랜잭션을 커밋하기 전에 데이터베이스에서 변경된 값을 가져오는 경우 에러가 발생할 수 있습니다.
보통 이런 경우, EntityManager에서 detach() 메서드를 사용해 엔티티 객체를 영속성 컨텍스트에서 분리시킴으로써 해결할 수 있습니다. 또는 EntityManager의 flush() 메서드를 사용해 영속성 컨텍스트에 있는 엔티티 정보를 데이터베이스와 일치시켜 에러를 방지할 수 있습니다.
해당 코드를 보면 @Transactional 어노테이션이 레포지토리 클래스에만 적용되어 있기 때문에, 서비스 레이어에서 트랜잭션 처리가 되지 않아서 영속성 컨텍스트 에러가 발생한 것 같습니다. 서비스 레이어에서 @Transactional 어노테이션을 추가해주시면 문제가 해결될 수 있을 것 같습니다.
또는 EntityManager를 이용해 분리시켜주거나 flush() 메서드를 사용해 엔티티 정보를 데이터베이스와 일치시켜줄 수도 있습니다. 하지만 이 방법은 트랜잭션 범위를 벗어나기 때문에 추천하지 않습니다.
참고로, 해당 코드에서는 UserRepository를 참조하지 않기 때문에 쿼리스트링을 작성해 User 엔티티 정보를 가져올 수도 있습니다.
자세한 내용은 스프링 공식 문서나 구글 검색을 통해 확인해보시길 바랍니다. 감사합니다!