ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • spring jpa의 patch때 사용하는 유틸
    카테고리 없음 2023. 4. 19. 09:55
    회사에서 잉여짓만하는구만ㅠㅠ 스프링 jpa를 쓰다보면 뭐 form으로 전달하는 것은 상관이 없는데 API 를만들다보면 업데이트시 null로 오면 null을 업데이트 한다. 그래서 옛날객체의 새로운 객체를 넣어 주어 update 하는 방식으로 해왔다. 예를 들어 이런 방식이다.
    if(account.getName() == null){
      oldAccount.setName(account.getName())
    }
    
    필드가 많다보면 이것도 일이다. notnull은 상관없는데 null을 허용하는 것들이 문제다. 그래서 유틸로 만들었다.
    public static <T> void oldInstanceBynewInstance(T oldInstance, T newInstance) {
        Class<?> newInstanceClass = newInstance.getClass();
        Class<?> oldInstanceClass = oldInstance.getClass();
    
        if(!newInstanceClass.isAssignableFrom(oldInstanceClass)){
            return;
        }
    
        for (Field newField : newInstanceClass.getDeclaredFields()) {
            newField.setAccessible(true);
            Object obj;
            try {
                obj = newField.get(newInstance);
            } catch (IllegalAccessException e) {
                throw new ReflecationException("reflecation Exception get field");
            }
            Id id = newField.getAnnotation(Id.class);
            PatchIgnore patchIgnore = newField.getAnnotation(PatchIgnore.class);
            if (id == null) {
                if (obj != null && patchIgnore == null) {
                    Field oldField;
                    try {
                        oldField = oldInstanceClass.getDeclaredField(newField.getName());
                        oldField.setAccessible(true);
                        oldField.set(oldInstance, obj);
                    } catch (NoSuchFieldException e) {
                        throw new ReflecationException("no such field");
                    } catch (IllegalAccessException e) {
                        throw new ReflecationException("reflecation Exception set field");
                    }
                }
            }
        }
    }
    
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface PatchIgnore {
    }
    
    새로운객체의 필드를 오래된 객체에 넣는것 뿐이다. 테스트를 해보자
    public class ReflectionUtilsTest {
    
        Product oldProduct = new Product();
    
        @Before
        public void before() {
            oldProduct.setId(1L);
            oldProduct.setName("wonwoo");
            oldProduct.setEmail("email@test.com");
            oldProduct.setPassword("password");
        }
    
        @Test
        public void reflectionUtilTest() {
            Product product = new Product();
            product.setId(20L);
            product.setName("ooooo");
            product.setEmail("wonwoo@test.com");
            ReflectionUtils.oldInstanceBynewInstance(oldProduct, product);
            assertEquals(oldProduct.getId().longValue(), 1L);
            assertEquals(oldProduct.getName(), "ooooo");
            assertEquals(oldProduct.getEmail(), "wonwoo@test.com");
        }
    
        @Test
        public void reflectionUtilIgnoreTest() {
            Product product = new Product();
            product.setName("ooooo");
            product.setEmail("wonwoo@test.com");
            product.setPassword("repassword");
            ReflectionUtils.oldInstanceBynewInstance(oldProduct, product);
            assertEquals(oldProduct.getName(), "ooooo");
            assertEquals(oldProduct.getEmail(), "wonwoo@test.com");
            assertEquals(oldProduct.getPassword(), "password");
        }
    }
    
    @Data
    class Product {
    
        @Id
        @GeneratedValue
        private Long id;
        private String name;
        private String email;
    
        @PatchIgnore
        private String password;
    }
    
    Id 어노테이션과 PatchIgnore은 무시한다. old객체의 name 에 wonwoo라는 String이 들어가 있다. 그리고 나서 새로운 객체에 ooooo 을 넣었다. 그러면 old객체에 ooooo이 들어간걸 확인 할 수 있다. 또한 PatchIgnore 어노테이션은 무시한다. 기존에 password 라고 넣고 새로운 객체에는 repassword라 넣었다. 하지만 무시당하고 password가 계속 담겨 있다. 실직적으로 사용은 이렇게 하면 된다.
    public Account update(final Long id, final Account account) {
        Account oldAccount = findOne(id);
        if (oldAccount == null) {
            throw new IDNotFoundException("id " + id + " not found");
        }
        ReflectionUtils.oldInstanceBynewInstance(oldAccount, account);
        return oldAccount;
    }
    
    jpa 의 변경감지로 인해 굳이 save를 할 필요 없다 필자가 생각한방법이다. 물론 더 좋은 방법이 있을 수도 있지만 지금 현재 나는 모른다.ㅜㅜㅜ

    댓글

Designed by Tistory.