반응형

 

영속성 컨텐스트란 엔티티를 영구 저장하는 환경으로 애플리케이션과 데이터베이스 사이에서 객체를 보관하는 가상의 데이터베이스라고 생각하면 된다.

JPA는 영속성 컨텍스트라는게 존재하고 그 안에 캐시가 있어서 DB에 접근 안 하고 캐시에 존재하는 데이터는 빠르게 가져오거나 DB에 부담을 줄일 수 있습니다. 그리고 그 캐시안에 있는 걸 영속화 되었다라고 표현합니다.

 

📝Insert

Insert시 영속성 컨텍스트에 해당 값이 올라가게 되고 flush해 영속성 컨텍스트에 있는 데이터를 DB에 데이터를 넣게 됩니다.

 

📝Select

Select시 DB에 해당 데이터를 영속성 컨텍스트에 올리게 됩니다. 그 후 컨트롤러에서는 영속성 컨텍스트에 있는 값을 가져가게 됩니다.

 

📝Update

Update의 경우 Select와 같은 동작이 선행이 되어서 영속성 컨텍스트에 올라가게 되고 그 후 업데이트된 값을 다시 영속화 시키고 DB에 flush해 데이터를 업데이트 시킵니다.

 

📝더티체킹

Update와 같은 행위를 Transaction 어노테이션을 이용해 하는 걸 더티체킹이라고 합니다.

Transaction 어노테이션이 선언된 곳에서 영속성 컨텍스트에 변경이 일어날 시 자동으로 데이터베이스에 반영하는 JPA 특징입니다.

 

 

@Transactional // 함수 종료시 자동으로 DB Commit (영속성컨테이너 데이터를 DB로 Flush)
// Update ( Put방식은 Body에 Json만 인식하고 @RequestBody는 두개 이상 사용이 불가능 (객체에 담던가 해야한다.)
@PutMapping("/dummy/users/{id}") 
public String updateUser(@PathVariable int id, @RequestBody BlogUser requestUser) {
    System.out.println("id : " + id);
    System.out.println("password : " + requestUser.getPassword());
    System.out.println("email : " + requestUser.getEmail());

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

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

    /** @Transactional 사용 안 할시 **/
    // save 함수는 id를 전달하지 않으면 insert
    // save 함수는 id를 전달하면 해당 id 존재시 udpate 없으면 insert
    // userRepository.save(user);

    return id + "가 update 가 되었습니다. ";
}

 

 

@Transaction 다른 예제

@RestController
public class DummyController {

	@Autowired // DI
	private UserRepository userRepository;
	
	@PostMapping("/api/user")
	//@Transacitonal
	public ResponseDto<String> save(@RequestBody BlogUser user){

		BlogUser usre2 = new BlogUser();
		
		Random random = new Random();

		usre2.setUsername(String.valueOf(random.nextInt(10)));
		usre2.setPassword("abc@naver.com");
		usre2.setEmail("abc@naver.com");
		userRepository.save(usre2); 
		
		user.setRole(RoleType.USER); // Enum을 이용한 방식을 통해 Setter 주입 실수를 방지할 수 있다. 
		join(user);
	
		return new ResponseDto<String>(HttpStatus.OK .value(), "1");
		// new ResponseDto<String>(HttpStatus.OK, result);
	}
	
	@Transactional // Transaction 처리하겠다. 성공시에만 Commit 
	public void join(BlogUser user) { 
		userRepository.save(user); // UserRepostiory에 save하면 DB 테이블에 insert가 된다.
	}
	
 }
 
 
 데이터 Body 형식 →
 {
    "username" : "lee6",
    "password" : "1234",
    "email" : "tjdwo37@naver.com"
}
 
 
 
 - @Transaction이 join 함수에 있는 경우
join 함수를 실행시에 에러가 날시 join함수 내용만 rollback
위에 랜덤함수로 UserName을 넣고 insert 과정은 join함수를 안 쓰기 때문에 
해당 API 계속 호출시 랜덤함수로 UserName 넣는 데이터 계속 insert는 되고 있음

- @Transcaation을 api 함수에 넣은 경우
api에서 에러가 날시 모든 내용을 rollback하기 때문에 위와 같이 랜덤함수로 UserName넣고 insert하는 과정도
rollback이 되어서 데이터가 계속 누적되지 않는다.
package com.lsj.blog;

import java.sql.Timestamp;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.DynamicInsert;

import com.lsj.blog.model.RoleType;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/** 하기 어노테이션 사용하려면 Javax Persistence API Gradle 추가 필요 **/
//@DynamicInsert // null 인 값일 때는 무시하고 Default값으로 넣어준다
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Entity // 하기 필드 기준으로 DB에  User 테이블을 생성하라는 의미
public class BlogUser {

	@Id // Primary key
	@GeneratedValue(strategy = GenerationType.IDENTITY) // 프로젝트에서 연결된 DB의 넘버링 전략을 따라간다.
	// → 즉, DB에 따라 AutoIncrement 또는 Sequence가 결정 되어서 알아서 만들어준다. (자동 증가 PK 필요함으로 이와 같은 행동 함) 
	// (MySql에서는 AutoIncrement, Oracle에서는 Sequence 사용)
	private int id;
	
	@Column(nullable = false, length = 30, unique=true) // null 방지 및 길이 30 + 중복값 방지
	private String username;
	
	//@ColumnDefault(" 'no password' ") //→ @DynamicInsert 와 같이 사용한다. null일시에는 ColumnDefault에 선언된 값(no password)으로 넣어준다.   
	@Column(nullable = false, length = 100) 
	private String password;

	@Column(nullable = false, length = 50) 
	private String email;
	
	//@ColumnDefault("'user'") // → Column Default 값 String이기 때문에 ' ' 안에 넣어야한다.
	//private String role; // Enum을 
	
	@Enumerated(EnumType.STRING) // DB에는 RoleType이라는 타입이 없기 때문에 String 이라고 선언
	private RoleType role;
	
	@CreationTimestamp // 현재 시각 바로 자동 입력
	private Timestamp createDate;
	
}

 

반응형