@DeleteMapping("/dummy/user/{id}")
public String delete(@PathVariable int id) {
    try {
        userRepository.deleteById(id);
    } catch (EmptyResultDataAccessException e) {
        return "삭제에 실패하였습니다. 해당 id는 DB에 없습니다.";
    }

    return "삭제되었습니다. id : " + id;
}

deleteById는 void 반환타입이여서 try catch를 이용하여 예외처리를 잡아줍니다.

 

 

강의 주소 : https://youtu.be/vOXgQYX7nok

더티체킹

 

- 영속화 되었다 : Request에 의해 Controller에서 User객체를 Save 할때 JPA내의 영속성 컨텍스트내의 1차캐시에 해당 객체가 생성되는것을 말합니다.

 

- flush : 영속화 된 객체를 DB에 밀어넣는 행위를 말합니다.(버퍼를 비운다라고 말합니다.)

간단한 예로 어떤 창고에 있는 물건들을 더 큰 창고로 옮기는 행위라고 말 할 수 있습니다.

 

특징으로 flush긴 flush지만 이때 1차캐시에 있는 객체를 지우지는 않습니다.

 

- 더티체킹의 동작원리

메소드 상단에 Transactional 어노테이션을 선언한 이후, 2번 id를 가진 객체를 update를 하고 싶어 호출하였다고 가정합니다. 그러면, DB에서 2번 id를 가진 데이터를 JPA  영속성 컨텍스트에 객체를 영속화를 시키며, 컨트롤러의 user객체에 영속화 된 객체를 넣습니다.

이후, 컨트롤러의 user 객체의 값을 변경하고 해당 메소드가 끝나게 되면 영속성 컨텍스트 내의 user 객체와의 차이를 판단하여 자동으로 commit을 합니다.

 

 

해당 강의에 대한 자세한 설명은 강의 하단 댓글에 잘 정리되어있습니다. 참고하시면 좋을 듯 합니다.

 

 

강의 주소 : https://youtu.be/dDsI1J4QC6g

updateUser 메소드를 아래와 같이 생성합니다.

// save 함수는 id를 전달하지 않으면 insert를 해주고
// save 함수는 id를 전달하면 해당 id에 대한 데이터가 있으면 update를 해주고
// save 함수는 id를 전달하면 해당 id에 대한 데이터가 없으면 insert를 해요.
// email, password
@PutMapping("/dummy/user/{id}")
public User updateUser(@PathVariable int id, @RequestBody User requestUser) { // json데이터를 요청 => Java Object(MessageConvertor의 Jackson라이브러리가 변환해서 받아줘요.)
    System.out.println("id : " + id);
    System.out.println("password : " + requestUser.getPassword());
    System.out.println("email : " + requestUser.getEmail());

    User user = userRepository.findById(id).orElseThrow(()->{
        return new IllegalArgumentException("수정에 실패하였습니다.");
    });
    user.setPassword(requestUser.getPassword());
    user.setEmail(requestUser.getEmail());

    userRepository.save(user);
    return null;
}

RequestBody 어노테이션은 json데이터를 요청한것에 대하여 Java Object로 변환하여 받아줍니다.

이때 MessageConverter의 Jackson 라이브러리가 이 역활을 합니다.

 

save 메소드는 update도 되고 insert도 됩니다.

save 메소드를 이용한 update시 아래와 같이 먼저 해당 id에 따른 정보값을 찾은 후 그 정보값으로 다시 update를 해줍니다.

 

 

@Transactional
@PutMapping("/dummy/user/{id}")
public User updateUser(@PathVariable int id, @RequestBody User requestUser) { // json데이터를 요청 => Java Object(MessageConvertor의 Jackson라이브러리가 변환해서 받아줘요.)
    System.out.println("id : " + id);
    System.out.println("password : " + requestUser.getPassword());
    System.out.println("email : " + requestUser.getEmail());

    User user = userRepository.findById(id).orElseThrow(()->{
        return new IllegalArgumentException("수정에 실패하였습니다.");
    });
    user.setPassword(requestUser.getPassword());
    user.setEmail(requestUser.getEmail());


    // 더티 체킹
    return null;
}

Transactional 어노테이션을 붙이고 조회된 user에 수정하고자 하는 정보들을 지정하면, save메소드와 같은 역할을 진행합니다.

이걸 더티체킹이라 합니다.(자세한거든 다음 강의에서...)

 

 

 

강의 주소 : https://youtu.be/oijoJtiGPhI

모든 유저를 조회합니다.

@GetMapping("/dummy/users")
public List<User> list() {
    return userRepository.findAll();
}

 

페이징을 이용한 조회입니다.

// 한 페이지당 2건의 데이터를 리턴받아 볼 예정
@GetMapping("/dummy/user/page")
//public Page<User> pageList(@PageableDefault(size=2, sort = "id", direction = Sort.Direction.DESC) Pageable pageable) {
public List<User> pageList(@PageableDefault(size=2, sort = "id", direction = Sort.Direction.DESC) Pageable pageable) {
    Page<User> pagingUser = userRepository.findAll(pageable);
    List<User> users = pagingUser.getContent();

    return users;
}

PageableDefault 어노테이션을 이용해 한 페이지당 표시되는 수는 2개고, 정렬은 id이며 방향은 역방향으로 한다는 의미입니다.

선언대 Pageaable 객체를 이용하여 페이징을 포함하여 조회시 아래와 같이 결과가 나옵니다.

page에 대한 부분은 queryString으로 조절 가능합니다.

 

 

 

강의 주소 : https://youtu.be/dPfjqBB-T4U

DummyControllerTest 내에 id 조회용 메서드를 생성합니다.

// http://localhost:8000/blog/dummy/user/5
// {id} 주소로 파라메터를 전달 받을 수 있음.
@GetMapping("/dummy/user/{id}")
public User detail(@PathVariable int id) {
    // user/4을 찾으면 내가 데이터베이스에서 못찾아오게 되면 user가 null이 될 것 아냐?
    // 그럼 return null이 리턴이 되자나... 그럼 프로그램에 문제가 있지 않겠니?
    // Optional로 너의 User객체를 감싸서 가져올테니 null인지 아닌지 판단해서 return해!!!

    User user = userRepository.findById(id).orElseThrow(new Supplier<IllegalArgumentException>() {
        @Override
        public IllegalArgumentException get() {
            // TODO Auto-generated method stub
            return new IllegalArgumentException("해당 유저는 없습니다. id : " + id);
        }
    });

    return user;
}

이때 {id}는 주소로 파라메터를 전달 받을 수 있는 규칙입니다.

(단, 메서드 파라메터 부분에 @PathVariable을 추가해줘야 합니다.)

 

JPA를 통해 userRepository.findById 이용시 반환 타입은 Optional입니다.

필자는 User 객체로 반환하기 위해 findById 이후 orElseThrow를 사용합니다.

이때, findById의 상세내용을 봐보면 아래와 같습니다.

/**
 * Retrieves an entity by its id.
 *
 * @param id must not be {@literal null}.
 * @return the entity with the given id or {@literal Optional#empty()} if none found.
 * @throws IllegalArgumentException if {@literal id} is {@literal null}.
 */
Optional<T> findById(ID id);

만약 해당 값이 없을 경우에 대해서는 IllegalArgumentException으로 예외처리하기를 권장합니다.

 

먼저 orElseThrow를 사용하기에 앞서 orElseGet()을 통해 해당 메서드 내에서 Supplier객체를 선언하여 빈 객체를 던지도록 작성 할 수 있습니다.

User user = userRepository.findById(id).orElseGet(new Supplier<User>() {
    @Override
    public User get() {
        return new User();
    }
});

그러나 앞서 말씀드린것처럼 예외처리하는 방향을 선호하기에 orElseThrow를 통해 코드 작성을 마무리 짓습니다.

 

잘못된 id 값을 집어 넣었을 경우 아래와 같은 결과로 표출됩니다.

 

번외로 위의 코드와 같이 어떤 객체를 리턴하며, 어떤 exception을 넣어야하는지에 대한 불편함을 해소하는 방법으로 람다식을 이용 할 수 있습니다.

// 람다식
User user = userRepository.findById(id).orElseThrow(() -> {
    return new IllegalArgumentException("해당 사용자는 없습니다.");
});

 

작성 된 코드를 보게되면 return 타입이 User 객체임을 알 수 있습니다.

그러나 웹브라우저를 통한 요청에서는 해당 User 객체인 자바 오브젝트를 인식 할 수 없습니다.

기존의 스프링에서는 이를 위해서 json으로 변환을 하고자 Gson 라이브러리를 이용하였으나,

스프링 부트에서는 MessageConverter가 Jackson 라이브러리를 호출하여 자동적으로 변환하여 응답해줍니다.

 

 

 

 

 

강의 주소 : https://youtu.be/z_yxfFUX1xI

+ Recent posts