반응형
영속성 컨텐스트란 엔티티를 영구 저장하는 환경으로 애플리케이션과 데이터베이스 사이에서 객체를 보관하는 가상의 데이터베이스라고 생각하면 된다.
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;
}
반응형