회사에서 잉여짓만하는구만ㅠㅠ
스프링 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를 할 필요 없다
필자가 생각한방법이다.
물론 더 좋은 방법이 있을 수도 있지만 지금 현재 나는 모른다.ㅜㅜㅜ