You can make anything
by writing

C.S.Lewis

by anonymDev Apr 12. 2022

카지노 게임 추천는 Insert와 Update를 어떻게 구분할까

코드로 보는 spring-data-jpa (1)

Prerequisite: Spring Data 카지노 쿠폰 카지노 쿠폰 구조와


아래 코드는 spring-data-jpa의 2.6.x 버전입니다.

spring-data-jpa를 사용할 때 interface type으로 Repository를 정의해서 사용한다.


publicinterfaceCountryRepositoryextendsJpaRepository<Country, Long {

Country findByName(String name);

}

그리고 Repository의 카지노 게임 추천(T object)를 호출해서 Entity를 신규저장/업데이트한다.


아래 예제 코드처럼 Entity를 DB에 Insert(1) 조회한 Entity의 필드(칼럼)를 Update(2)하는 방식으로 많이 사용할 것이다.


<spring-data-jpa 예제 코드

@Test

voidtest카지노 게임 추천OrUpdate(){
Country KOREA_V1 = newCountry();
KOREA_V1.name= "Korea";
countryRepository.카지노 게임 추천(KOREA_V1); // (1) 이름이 "Korea" 신규 국가를 생성하여 저장소에 저장한다.

Country KOREA_V2 = countryRepository.findByName("Korea");
KOREA_V2.code= "KR";
countryRepository.카지노 게임 추천(KOREA_V2); // (2)이름으로 조회한 후 code를 "KR"로 업데이트한다.

assertThat(countryRepository.findByName("Korea").code).isEqualTo("KR");
}


(1)과 (2) 모두 save()를 동일하게 호출하지만 동작 결과는 다르다. (1)의 경우 Insert 문이 실행되고 (2)의 경우 Update 문이 실행된다.


그렇다면 spring-data-jpa의 JpaRepository는 Insert와 Update를 어떻게 구분해서 실행할까?

위 질문에 대한 해답은 SimpleDataJpaRepository에서 찾을 수 있다.


interface로 선언된 JpaRepository의 save()를 호출하면 내부적으로 o.s.d.jpa.repository.support.SimpleDataJpaRepository의 save()를 호출한다. (SimpleDataJpaRepository가 내부적으로 어떻게/왜 호출되는지는 'JPA Repository 생성의 비밀' 에서 알아보자)


SimpleDataJpaRepository카지노 게임 추천Informationtype의 필드 카지노 게임 추천Information를 갖고 있다. 카지노 게임 추천Information은 SimpleDataJpaRepository가 다루는 Entity의 Metadata를 갖고 있다(이름, 타입, 필드 정보 등등).

카지노 게임 추천Information의 isNew에 카지노 게임 추천를 파라미터로 넘겨 새롭게 생성된 카지노 게임 추천인지를 판단한다(아래 코드(3)).


<SimpleDataJpaRepository.class

@Transactional
@Override
public<S extends T S save(S entity){
Assert.notNull(entity, "Entity must not be null.");
if (카지노 게임 추천Information.isNew(entity)){ <----(3)
em.persist(entity); <---- (4)
return entity;
} else {
return em.merge(entity); <---- (5)
}
}


카지노 게임 추천Information은 interface이며 구현체인 o.s.d.j.r.s.JpaMetamodel카지노 게임 추천Information의 isNew()가 호출된다. (Entity가 Persistable의 구현체인 경우 제외)


<JpaMetamodel카지노 게임 추천Information.class

@Override
public booleanisNew(T 카지노 게임 추천) {
if(!versionAttribute.isPresent()
|| versionAttribute.map(Attribute::getJavaType).map(Class::isPrimitive).orElse(false)) {
returnsuper.isNew(entity);// (6) super(Abstract카지노 게임 추천Information)의 isNew 호출
}
BeanWrapper wrapper = new DirectFieldAccessFallbackBeanWrapper(entity);

returnversionAttribute.map(it - wrapper.getPropertyValue(it.getName()) == null).orElse(true); // (7) Entity의 버전 필드가 null이면 true 아니면 false를 반환한다
}


<Abstract카지노 게임 추천Information.class

public booleanisNew(T 카지노 게임 추천) {

IDid = this.getId(entity);
Class<ID idType = this.getIdType();
if(!idType.isPrimitive()) {
returnid == null; // (8) id가 null이면 true 반환
} else if(id instanceofNumber) {
return((Number)id).longValue() == 0L;// (9) id가 Number type에 값이 0이면 true 반환
} else{
throw newIllegalArgumentException(String.format("Unsupported primitive id type %s!", idType));
}
}


1) Version 필드가 존재하지 않는 경우 (참고: 코드 (6))

super class인 Abstract카지노 게임 추천Information.isNew()를 호출하여 Id 필드가 null 또는 0인지 판단한다.

null 또는 0인 경우 New Entity로 판단한다.


2) Version 필드가 존재하는 경우

Version 필드의 값이 null이라면 New Entity로 판단한다. Version 필드를 가진 Entity라면 Id 필드를 New Entity로 판단하는데 활용하지 않는다.


<spring-data-jpa 예제 코드로 다시 돌아가보면 왜 save()를 동일하게 실행했음에도 불구하고 다른 실행결과가 나왔는지 추측해볼 수 있다.

@Test

voidtest카지노 게임 추천OrUpdate(){
Country KOREA_V1 = newCountry(); //아직 Id 필드와 Version필드에 값이 없다
KOREA_V1.name= "Korea";
countryRepository.카지노 게임 추천(KOREA_V1);

// (1) DB에 Insert 되면서 Id(또는 Version)이 할당됐다.

Country KOREA_V2 = countryRepository.findByName("Korea");

//Id (또는 Version)에 값이 존재한다
KOREA_V2.code= "KR";
countryRepository.카지노 게임 추천(KOREA_V2);

//(2) 다시 조회했을 때 Id 필드(또는 Version 필드)의 값이 할당돼있는 상태이다.

assertThat(countryRepository.findByName("Korea").code).isEqualTo("KR");
}


(1)에서 Entity를 저장할 때는 아직 Id 필드와 Version필드에 값이 없는 New Entity인 상태이다. 고로 아래 <SimpleJpaRepository.class의 save에서 em.persist를 실행한다(4).

(2)에서 Id 필드(또는 Version)에 값이 있는 Entity를 다시 save를 친다면 isNew는 false를 반환하고 em.merge()를 실행하게 된다(5).


<SimpleJpaRepository.class

@Transactional
@Override
public<S extends T S save(S entity){

Assert.notNull(entity, "Entity must not be null.");


if (카지노 게임 추천Information.isNew(entity)){ <----(3)

em.persist(entity); <---- (4)

return entity;

} else {

return em.merge(entity);<---- (5)

}

}.


이 경우 EntityManager가 해당 Entity를 persist 하게 되면 Entity가 TRANSIENT 상태로 인식하고 Insert 구문을 실행한다. 반면 merge로 Entity가 DETACHED 상태로 인식하여 Update 구문이 실행된다.


persist면 무조건 Insert이고 merge면 무조건 Update일까? 그건 또 그렇지 않다.이후의 부분부터는 spring-data-jpa가 아니라 hibernate의 EntityManager와 그 구현체의 코드를 살펴봐야 한다. 이번 주제가 너무 커지기 때문에 여기서 멈추도록 하고 EntityManager의 persist와 merge가 어떻게 동작하는지는 따로 다루도록 하자








브런치는 최신 브라우저에 최적화 되어있습니다.