Digking's cave

My First Blog Project (8) : Update / Delete / 더티체킹 / @Transactional 본문

Spring/My First Blog Project

My First Blog Project (8) : Update / Delete / 더티체킹 / @Transactional

디깅 2022. 12. 16. 17:42
728x90

DummyControllerTest.java

package com.cos.blog.test;

import com.cos.blog.model.RoleType;
import com.cos.blog.model.User;
import com.cos.blog.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.web.bind.annotation.*;

import javax.transaction.Transactional;
import java.util.List;
import java.util.function.Supplier;

@RestController
public class DummyControllerTest {
    @Autowired
    private UserRepository userRepository;

	// 회원 가입
    @PostMapping("/dummy/join")
    public String join(User user){
        user.setRoles(RoleType.USER);
        userRepository.save(user);
        return "회원가입 완료";
    }
	
    // 회원 모두 조회
	@GetMapping("/dummy/users")
    public List<User> list(){
        return userRepository.findAll();
    }
    
    @GetMapping("/dummy/user/{id}")
    public User detail(@PathVariable int id){
        //IllegalArgumentException 을 return
        User user = userRepository.findById(id).orElseThrow(new Supplier<IllegalArgumentException>() {
            @Override
            public IllegalArgumentException get() {
                return new IllegalArgumentException("id "+id+" : 해당 유저는 없습니다.");
            }
        });
        
        return user;
    }


    // 한 페이지 당 2건의 데이터를 리턴 (페이징)
    @GetMapping("/dummy/user")
    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;
    }

    @Transactional //함수 종료시 자동 commit
    @PutMapping("/dummy/user/{id}")
    public User updateUser(@PathVariable int id,@RequestBody User requestUser){

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

        user.setPassword(requestUser.getPassword());
        user.setEmail(requestUser.getEmail());

        return user;
    }
    
        @DeleteMapping("/dummy/user/{id}")
    public String delete(@PathVariable int id){
        try{
            userRepository.deleteById(id);
        } catch (EmptyResultDataAccessException e){
            return "없는 id라서 삭제 실패";
        }


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

 

    @Transactional //함수 종료시 자동 commit
    @PutMapping("/dummy/user/{id}")
    public User updateUser(@PathVariable int id,@RequestBody User requestUser){

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

        user.setPassword(requestUser.getPassword());
        user.setEmail(requestUser.getEmail());

//        userRepository.save(requestUser);
        return user;
    }

 

@RequestBody User requestUser

- json 데이터를 요청했는데 Java Object 로 변환

- MessageConvert의 Jackson 라이브러리가 변환해서 받아줘요

 

# save 사용 하는 경우

    @PutMapping("/dummy/user/{id}")
    public User updateUser(@PathVariable int id,@RequestBody User requestUser){

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

        user.setPassword(requestUser.getPassword());
        user.setEmail(requestUser.getEmail());

        userRepository.save(user);
        return user;
    }

save로 하면 null값이 생긴다. --> save는 insert 할 때만 주로 사용한다.

+ save로 하려면 아래처럼 user를 찾아준 후, set으로 변경값을 변경후에 다시 save 한다.

 

save는 id를 전달하지 않으면 insert

save는 id를 전달하면 해당id에 대한 데이터가 있으면 update

save는 id를 전달하면 해당id에 대한 데이터가 없으면 insert

 

#

@Transactional

더티 체킹

save하지 않아도, 변화된 값만 update 쓴다.

    @Transactional //함수 종료시 자동 commit
    @PutMapping("/dummy/user/{id}")
    public User updateUser(@PathVariable int id,@RequestBody User requestUser){

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

        user.setPassword(requestUser.getPassword());
        user.setEmail(requestUser.getEmail());

        return user;
    }

 

영속성컨텍스트

1.

JPA 에 영속성컨텍스트 가 있다.

Controller에서 user객체 만들어서 save하면 영속성컨텍스트 안에 1차캐시 안에 쌓인다.

쌓인 user객체를 실제 table에 넣는다. (실제 table에 data를 밀어넣는걸 flush라고한다.)

단! flush 해도 1차캐시가 비워지지 않는다.

 

그래서 이후에 controller가 해당 data를 가져올 때, 영속화 된 1차캐시에서 data를 가져온다.

 

2.

2번 data의 password/email을 update를 하려고 한다.

table에서 2번 data를 가져와서 1차캐시에 영속화를 하고, controller로 가져온다.

영속화 된 data에 값을 변경한다.

변경 후 save 하면 영속화 된 1차캐시에 값이랑 비교한다.

비교했더니 같은 값이니까 영속화 된 1차캐시에 값을 update한다.

1차캐시를 flush해서 table을 update한다.

 

3.

@Transactional 이 있으면 transaction이 update 함수 시작과 종료에 따라 시작/종료 된다.

종료되면 자동으로 commit 된다.

 

find해서 user에 담을 때 영속화 된다.

이후 set으로 값을 변경하면 변경 인식하고 flush 된다.

더티체킹 = 변경감지하고 DB 에 flush 하는 것

 

# Delete

    @DeleteMapping("/dummy/user/{id}")
    public String delete(@PathVariable int id){
        try{
            userRepository.deleteById(id);
        } catch (EmptyResultDataAccessException e){
            return "없는 id라서 삭제 실패";
        }


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

출처 : 29강

https://www.youtube.com/watch?v=dDsI1J4QC6g&list=PL93mKxaRDidECgjOBjPgI3Dyo8ka6Ilqm&index=31 

 

 

반응형