강의

멘토링

커뮤니티

Cộng đồng Hỏi & Đáp của Inflearn

Hình ảnh hồ sơ của gabozanet1044
gabozanet1044

câu hỏi đã được viết

Phát triển ứng dụng web dựa trên Spring và JPA

엔티티에 저장이 안되는 문제에 대한 문의

Đã giải quyết

Viết

·

625

0

안녕하세요. 강의 잘 보았습니다. 강의 도움 받아서 테스트 진행해보고 있는데요. 저장이 안되는 부분에 대해 문의 드려봅니다.

Car, Camp 엔티티가 있고 Car 전체 데이터 읽어서 특정 필드의 값을  Camp 에서 찾아서 업데이트하는 로직입니다.

if (carRepository.count() > 0) {

carRepository.findAll().stream().forEach(s -> {
String campName = s.getCampEngName();
Optional<Car> car = carRepository.findById(s.getId());

Camp camp = campRepository.findByEngName(campName);
if (camp != null) {
car.ifPresent(m -> m.setNote(camp.getKorName()));
}
});
}

해당 부분 코드는 이렇게 했는데, 수행 후에 실제 Car 엔티티의 note 값이 변경이 안되어있습니다. 로그 찍어보면 campName 값은 일일이 잘 가져오고 있는데 setNote로 저장이 안됩니다.

트랜젝션 문제일까요? 강의를 제대로 소화를 못했는지 원인을 못찾겠네요. ㅠㅠ  도움 부탁드립니다.

아.. 저게 수행되는 곳이, 일반 컨트롤러 통해서 들어오는게 아니라 최초 실행시점입니다.(혹시 이게 문제일까요?)

@PostConstruct

public void initCarData() throws IOException {

...

if (carRepository.count() > 0) {

}

...

}

이 부분입니다.

JPAthymeleafspring-bootspringjava

Câu trả lời 7

0

gabozanet1044님의 프로필 이미지
gabozanet1044
Người đặt câu hỏi

감사합니다.~

0

whiteship님의 프로필 이미지
whiteship
Người chia sẻ kiến thức

아 @PostConstruct를 쓴거였군요! 순서가 달라서 @Transactional이 적용이 안됩니다. 구체적으로는 @Transactional이 적용되려면 CarService의 프록시 객체가 만들어져야 가능한건데 그 작업은 @PostConstruct 실행하는 단계 이후에 벌어지는 일이라서 사실상 @Transactional없이 호출한거랑 같습니다. 다른 방법도 있긴하지만 이 경우에는 간단히 save()를 명시적으로 호출해주면 해결할 수 있긴합니다.

0

gabozanet1044님의 프로필 이미지
gabozanet1044
Người đặt câu hỏi

Optional<Car> car = carRepository.findById(s.getId());

아.. 저건 필요없는건데, 저거 없이 했을때 안돼서 이렇게 저렇게 시도해보는 중에 들어간 부분입니다.

원래소스는 아래와 같습니다. campName으로 조회한 Camp도 잘 가져옵니다.

@Service
@Transactional
@RequiredArgsConstructor
public class CarService {

private final CarRepository carRepository;
private final CampRepository campRepository;

@PostConstruct
public void initCarData() throws IOException {

if (carRepository.count() > 0) {
carRepository.findAll().stream().forEach(s -> {
String campName = s.getCampEngName();
Camp camp = campRepository.findByEngName(campName);
if (camp != null) {
addNote(s, camp);
}
});


}
}
public void addNote(Car car, Camp camp) {
System.out.println(">>>car = " +
"" + car.getCarNumber() + ", camp = " + camp + ", campId = " + camp.getId());
car.setNote(camp.getKorName());
}

}

-------------------------------
로그찍어보면 아래와같이 car 갯수만큼 잘 찍힙니다.

>>>car = 81조4766, camp = 광주2(GWJ2)/광주(Gwangju), campId = 45
>>>car = 83보8744, camp = 인천5(ICH5)/인천(Incheon), campId = 5


---------------------------------
근데, 컨트롤러에서 addNote 호출하면 정상적으로 저장이 됩니다.

0

whiteship님의 프로필 이미지
whiteship
Người chia sẻ kiến thức

그리고 campName은 잘 찍힌다고 하셨는데 혹시 그 campName으로 조회한 Camp도 잘 가져오는지 확인해 보셨나요?

0

whiteship님의 프로필 이미지
whiteship
Người chia sẻ kiến thức

해당 코드가 컨트롤러에 있다고 잘못 읽었네요. 죄송합니다.

우선 코드가 잘 이해가 안되는 부분이 있는데요. carRepository로 findAll을 했는데 하나씩 순회하면서 다시 id로 findById는 왜 하시는거죠? 

 Optional<Car> car = carRepository.findById(s.getId());

0

gabozanet1044님의 프로필 이미지
gabozanet1044
Người đặt câu hỏi

답변 감사합니다. 

해당 코드는 서비스 부분에 위치하고 있습니다. 서비스 전체코드는 아래와 같습니다.

클래스 상단에 @transaction 도 기재했고요. 이렇게 해도 안돼서 문의 드렸습니다. 

@Service
@Transactional
@RequiredArgsConstructor
public class CarService {

private final CarRepository carRepository;
private final CampRepository campRepository;

@PostConstruct
public void initCarData() {


if (carRepository.count() > 0) {

carRepository.findAll().stream().forEach(s -> {
String campName = s.getCampEngName();
Optional<Car> car = carRepository.findById(s.getId());

Camp camp = campRepository.findByEngName(campName);
if (camp != null) {
car.ifPresent(m -> addNote(m, camp));
}
});

}
}

}

0

whiteship님의 프로필 이미지
whiteship
Người chia sẻ kiến thức

트랜잭션이 있어야 퍼시스턴스 상태의 객체 변화를 감지(dirty checking)해서 트랜잭션 끝날 때 플러쉬 하면서 데이터 상태를 DB에 동기화 한다고 강좌에서 여러번 언급한거 같은데 아쉽네요. 해당 코드 블럭을 서비스로 옮기고 서비스 클래스나 해당 메서드 위에 @Transactional을 붙여보시기 바랍니다.

Hình ảnh hồ sơ của gabozanet1044
gabozanet1044

câu hỏi đã được viết

Đặt câu hỏi