게터, 세터등을 .set~ .set~ 이렇게 라인별로 사용하게 하는 것이 아닌
@Builder 처럼 사용 가능하게 해준다.
Foo foo = new Foo();
foo.setAccount(account)
.setPassword(password)
...
@GeneratedValue 는 아이디를 관리 할 때 자동 생성되는 값으로 주입시킨다
그리고 해당 작업은 엔티티의 save 시 영속성 컨텍스트가 플러쉬 될 때 일어난다. 즉 이 전까지는 아이디가 없다 (식별자가 없다) [Jdbc를 사용해서 벌크인서트 예시]
그리고
SPRING + JPA 의 save 시 기본 동작은 다음과 같다
식별자가 존재하지 않으면 (자동 생성하는 값이라, 식별할 녀석이 없으면) persist
식별자가 존재하면 (임의로 id 값을 생성하면) merge
여기서 merge의 동작 순서는, DB에서 식별자가 존재하는 지 확인 후(조회), 있으면 update, 없으면 insert 가 일어난다. 즉, 비효율적이다
그러므로 id를 임의로 생성하는 방식에서는 이 부분에 대한 핸들이 필요하다. 이것을 위해 implemet 하는 녀석이 Persistable< idType > 이다.
요약하자면 위의 설명(식별자 관련)과 더해서
새 엔티티를 넣을 때 식별자를 사용하지 않도록 설정해주는 녀석이 저녀석
자동 생성 전략의 경우, id를 WrapperType(Long, Integer ...)으로 사용하는 경우에는 null값이어야만 식별자가 존재하지 않음으로 판별하며
primitiveType중 Number 타입(int, long)등은 초기값이 0이어야 식별자가 존재하지 않음으로 판단한다.
이 판별 로직은 isNew() 라는 이름으로 구현되어 있는데, (스프링 내부, JpaPersistableEntityInformation)
해당 로직을 오버라이드 해서 재정의해주는 작업으로, 우리는 새로운 엔티티로 판별시켜 merge가 일어나지 않게 할 수 있다.
@Entity
@EntityListeners(AuditingEntityListener.class)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Item implements Persistable<String> {
@Id
private String id;
@CreatedDate
private LocalDateTime createdDate;
public Item(String id) {
this.id = id;
}
@Override
public String getId() {
return id;
}
@Override
public boolean isNew() {
return createdDate == null;
}
}
위와 같은 형태로 isNew를 오버라이드
생성 시간에 따라 판별하면 편리하다
현재 소스에서 가장 의문이 드는 녀석
"저것을 사용한다 == 설계의 오류다" 라던 우아한 테크의 블로그 글이 떠오른다...
JPA에서 엔티티를 업데이트 할 때, 명시적인 .update() 매서드를 통한 업데이트 외에도
영속성 컨텍스트의 더티 체크를 통한 업데이트가 가능하다 (그리고 이게 더 많이 쓰이는 듯 하다)
이 컨텍스트의 판별 로직으로써 기존에는 beforeEntity.equals(afterEntity) 와 같은 형식으로 진행이 된다면
저 어노테이션을 달게 되면 feild1.equals.....fieldN.equals() 만큼의 판별이 필요하다 (jpa 입장에서) (즉, 다른 매서드 상에서도 손해가 발생한다. 더티 체킹은 항상 일어나니까(스냅샷 비교))
더불어서 JPA는 내부적으로 JDBC와 PreparedStatement 를 통해서 SQL 구문을 캐싱 하는데, 위와 같이 사용하게 된다면 이 캐시의 히트율이 떨어진다.
얘는 진짜로 처음봤다
insert 시에 null이 아닌 컬럼만 스캔하여 insert문이 발생하도록 해주는 녀석
결론 : 소스를 보면서 의도를 좀 더 파악해보자!