๐ Spring Data JPA - ์๋ก์ด ์ํฐํฐ ์ฌ๋ถ ํ๋จ ๋ฐ ์ง์  ID ํ ๋น ์ ์ฃผ์์ 
๐ Spring Data JPA - ์๋ก์ด ์ํฐํฐ ์ฌ๋ถ ํ๋จ ๋ฐ ์ง์  ID ํ ๋น ์ ์ฃผ์์ 
๐ 1. Spring Data JPA์์ ์ํฐํฐ์ ์ ๊ท ์ฌ๋ถ ํ๋จ
Spring Data JPA์์ ์ํฐํฐ๊ฐ ์๋ก์ด์ง ์ฌ๋ถ๋ฅผ ํ๋จํ๋ ๊ฒ์ ๋งค์ฐ ์ค์ํฉ๋๋ค.
์ด๊ฒ์ JPA๊ฐ ์ํฐํฐ๋ฅผ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅํ  ๋ persist๋ฅผ ์ฌ์ฉํ ์ง, ์๋๋ฉด merge๋ฅผ ์ฌ์ฉํ ์ง๋ฅผ ๊ฒฐ์ ํ๋ ๋ฐ ์ง์ ์ ์ธ ์ํฅ์ ์ค๋๋ค.
Spring Data JPA์์๋ JpaEntityInformation์ isNew(T entity) ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ์ํฐํฐ์ ์ ๊ท ์ฌ๋ถ๋ฅผ ํ๋จํฉ๋๋ค.
์ด ๋ฉ์๋๋ JpaMetamodelEntityInformation ํด๋์ค๋ฅผ ํตํด ๋์ํ๋ฉฐ, ์ํฐํฐ์ @Version ํ๋๋ @Id ํ๋์ ์ํ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ ๊ท ์ฌ๋ถ๋ฅผ ๊ฒฐ์ ํฉ๋๋ค.
โ
 ์ฆ, ์ํฐํฐ๊ฐ ์๋ก์ด ๊ฒฝ์ฐ (isNew()๊ฐ true ๋ฐํ)
โ persist()๋ฅผ ์ฌ์ฉํ์ฌ ์๋ก์ด ๋ฐ์ดํฐ๋ก ์ฝ์
โ
 ์ํฐํฐ๊ฐ ๊ธฐ์กด ๋ฐ์ดํฐ๋ก ํ๋จ๋๋ ๊ฒฝ์ฐ (isNew()๊ฐ false ๋ฐํ)
โ merge()๋ฅผ ์ฌ์ฉํ์ฌ ๊ธฐ์กด ๋ฐ์ดํฐ๋ฅผ ์์ 
๐ 2. ์ง์  ID ํ ๋น ์ ๋ฐ์ํ๋ ๋ฌธ์ 
์ผ๋ฐ์ ์ผ๋ก JPA์์๋ @GeneratedValue(strategy = GenerationType.IDENTITY)์ ์ฌ์ฉํ์ฌ ์๋์ผ๋ก ID๋ฅผ ์์ฑํฉ๋๋ค.
๊ทธ๋ฌ๋ ๊ฐ๋ฐ์๊ฐ ์ง์  ์ํฐํฐ์ ID๋ฅผ ์ค์ ํ๋ ๊ฒฝ์ฐ, ์์์น ๋ชปํ ๋ฌธ์ ๊ฐ ๋ฐ์ํ  ์ ์์ต๋๋ค.
๐น ๋ฌธ์  ์ํฉ
@Entity
public class User {
    @Id
    private String id;  // ์ง์  ID ํ ๋น
    private String name;
    public User(String id, String name) {
        this.id = id;
        this.name = name;
    }
}
์์ ๊ฐ์ด @GeneratedValue ์์ด ์ง์  ID๋ฅผ ์ค์ ํ๋ ๊ฒฝ์ฐ,
Spring Data JPA๋ ํด๋น ์ํฐํฐ๋ฅผ ๊ธฐ์กด ๋ฐ์ดํฐ๋ก ์ธ์ํ  ์ ์์ต๋๋ค.
๐น ๋ฐ์ํ๋ ๋ฌธ์ 
User user = new User("123", "John Doe");
userRepository.save(user);
- save()๋ฅผ ํธ์ถํ๋ฉด Spring Data JPA๋ ํด๋น ID(โ123โ)๊ฐ ์กด์ฌํ๋์ง ํ์ธํ๊ธฐ ์ํด ๋จผ์  ์กฐํ ์ฟผ๋ฆฌ๋ฅผ ์คํ
- ID๊ฐ ์์ผ๋ฉด persist()๋ฅผ ์คํํ์ง๋ง, ID๊ฐ ์กด์ฌํ๋ค๊ณ ํ๋จํ๋ฉด ๋ถํ์ํmerge()๋ฅผ ์ํ
- ์ฆ, ๋ถํ์ํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์กฐํ๊ฐ ๋ฐ์ํ๋ฉฐ ์ฑ๋ฅ ์ ํ ๊ฐ๋ฅ์ฑ์ด ์์
๐ 3. ํด๊ฒฐ ๋ฐฉ๋ฒ - Persistable ์ธํฐํ์ด์ค ๊ตฌํ
์ง์  ID๋ฅผ ์ค์ ํ๋ ๊ฒฝ์ฐ, Spring Data JPA๊ฐ ์ํฐํฐ์ ์ ๊ท ์ฌ๋ถ๋ฅผ ์ ํํ ํ๋จํ  ์ ์๋๋ก Persistable ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
๐น Persistable์ ํ์ฉํ ํด๊ฒฐ ๋ฐฉ๋ฒ
import org.springframework.data.domain.Persistable;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Transient;
@Entity
public class User implements Persistable<String> {
    @Id
    private String id;
    private String name;
    @Transient // JPA๊ฐ ์ธ์ํ์ง ์๋๋ก ์ค์ 
    private boolean isNewEntity = false;
    public User(String id, String name) {
        this.id = id;
        this.name = name;
        this.isNewEntity = true; // ์ ์ํฐํฐ์์ ํ์
    }
    @Override
    public String getId() {
        return id;
    }
    @Override
    public boolean isNew() {
        return isNewEntity; // ์ ์ํฐํฐ ์ฌ๋ถ๋ฅผ ์ง์  ๊ด๋ฆฌ
    }
}
โ ์ค๋ช
- Persistable<T>์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ์ฌ Spring Data JPA๊ฐ- isNew()๋ฉ์๋๋ฅผ ํธ์ถํ๋๋ก ์ ๋
- isNew()๊ฐ- true์ด๋ฉด- persist()๊ฐ ์คํ๋๋ฉฐ,- false์ด๋ฉด- merge()๊ฐ ์คํ๋จ
- @Transient์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ์ฌ JPA๊ฐ ๊ด๋ฆฌํ์ง ์๋๋ก ์ค์  (- isNewEntityํ๋๋ DB์ ์ ์ฅ๋์ง ์์)
๐ 4. isNew() ๋ด๋ถ ๋์ ๋ฐฉ์
Spring Data JPA์์ isNew()๋ ์ํฐํฐ์ ID ๊ฐ ๋ฐ @Version ๊ฐ์ ํ์ธํ์ฌ ์ ๊ท ์ฌ๋ถ๋ฅผ ๊ฒฐ์ ํฉ๋๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก JpaMetamodelEntityInformation์ ํตํด ์ํฐํฐ์ ์ํ๋ฅผ ํ์ธํฉ๋๋ค.
@Override
public boolean isNew(T entity) {
    if(versionAttribute.isEmpty()
          || versionAttribute.map(Attribute::getJavaType).map(Class::isPrimitive).orElse(false)) {
        return super.isNew(entity);
    }
    BeanWrapper wrapper = new DirectFieldAccessFallbackBeanWrapper(entity);
    return versionAttribute.map(it -> wrapper.getPropertyValue(it.getName()) == null).orElse(true);
}
โ ์ค๋ช
- @Version์ด ์๋ ๊ฒฝ์ฐ, ๋๋- @Version์ด primitive ํ์ ์ด๋ฉด ๊ธฐ๋ณธ- isNew()๋ฅผ ํธ์ถ
- @Version์ด wrapper ํ์ ์ด๋ฉด ํด๋น ํ๋๊ฐ- null์ธ์ง ํ์ธํ์ฌ ์ ๊ท ์ฌ๋ถ๋ฅผ ํ๋จ
๐น AbstractEntityInformation์์์ ๋์
public boolean isNew(T entity) {
    Id id = getId(entity);
    Class<ID> idType = getIdType();
    if (!idType.isPrimitive()) {
        return id == null;
    }
    if (id instanceof Number) {
        return ((Number) id).longValue() == 0L;
    }
    throw new IllegalArgumentException(String.format("Unsupported primitive id type %s", idType));
}
โ ์ค๋ช
- @Version์ด ์์ผ๋ฉด- @Idํ๋๋ฅผ ํ์ธ
- primitive ํ์
์ด ์๋๋ฉด null์ฌ๋ถ, ์ซ์(Number) ํ์ ์ด๋ฉด0์ฌ๋ถ๋ฅผ ํ์ธํ์ฌ ์ ๊ท ์ฌ๋ถ ํ๋จ
๐ 5. ์ ๋ฆฌ
โ
 Spring Data JPA๋ JpaEntityInformation#isNew(T entity)๋ฅผ ์ฌ์ฉํ์ฌ ์ํฐํฐ์ ์ ๊ท ์ฌ๋ถ๋ฅผ ํ๋จ
โ
 ID๋ฅผ ์ง์  ์ค์ ํ๋ฉด JPA๊ฐ ๊ธฐ์กด ์ํฐํฐ๋ก ์ค์ธํ  ๊ฐ๋ฅ์ฑ์ด ์์
โ
 ๋ถํ์ํ SELECT ๋ฐ merge()๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด Persistable ์ธํฐํ์ด์ค๋ฅผ ํ์ฉํ  ์ ์์
โ
 isNew()์ ๋ด๋ถ ๋์ ๋ฐฉ์์ @Version ๋ฐ @Id ํ๋์ ์ํ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๊ฒฐ์ ๋จ
๐ ์ด๋ฌํ ๋ด์ฉ์ ๊ณ ๋ คํ์ฌ ID๋ฅผ ์ง์  ํ ๋นํ ๋ JPA์ ๋์์ ์ดํดํ๊ณ ์ต์ ํํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค. ๐
์ถ์ฒ
๋งค์ผ๋ฉ์ผ : Spring Data JPA์์ ์๋ก์ด Entity์ธ์ง ํ๋จํ๋ ๋ฐฉ๋ฒ์ ๋ฌด์์ผ๊น์?
๋๊ธ๋จ๊ธฐ๊ธฐ