나는 선택한 대답에 동의하지 않으며 davidxxx가 올바르게 지적했듯이 getReference는 선택하지 않고 동적 업데이트 동작을 제공하지 않습니다. 이 답변의 유효성에 관한 질문을했습니다. 여기를 참조하십시오- 최대 절전 모드 JPA의 getReference () 후 setter를 사용하여 select를 발행하지 않고 업데이트 할 수 없습니다 .
솔직히 그 기능을 실제로 사용한 사람은 본 적이 없습니다. 어딘가에. 그리고 나는 그것이 왜 그렇게 upvoted인지 이해하지 못합니다.
우선, 하이버 네이트 프록시 객체, setter 또는 getter에서 무엇을 호출하든 SQL이 실행되고 객체가로드됩니다.
하지만 JPA getReference () 프록시가 해당 기능을 제공하지 않으면 어떻게 될까요? 나만의 프록시를 작성할 수 있습니다.
이제 우리는 기본 키에 대한 선택이 쿼리가 얻을 수있는 것만 큼 빠르며 피해야 할 큰 길이로 이동하는 것이 실제로는 아니라고 주장 할 수 있습니다. 그러나 어떤 이유로 든 처리 할 수없는 사람들을 위해 다음은 그러한 프록시의 구현입니다. 그러나 구현을보기 전에 사용법과 사용이 얼마나 간단한 지 확인하십시오.
용법
Order example = ProxyHandler.getReference(Order.class, 3);
example.setType("ABCD");
example.setCost(10);
PersistenceService.save(example);
그리고 이것은 다음 쿼리를 실행합니다.
UPDATE Order SET type = 'ABCD' and cost = 10 WHERE id = 3;
삽입하려는 경우에도 PersistenceService.save (new Order ( "a", 2)); 삽입물을 발사합니다.
이행
이것을 pom.xml에 추가하십시오-
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.10</version>
</dependency>
이 클래스를 만들어 동적 프록시를 만듭니다.
@SuppressWarnings("unchecked")
public class ProxyHandler {
public static <T> T getReference(Class<T> classType, Object id) {
if (!classType.isAnnotationPresent(Entity.class)) {
throw new ProxyInstantiationException("This is not an entity!");
}
try {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(classType);
enhancer.setCallback(new ProxyMethodInterceptor(classType, id));
enhancer.setInterfaces((new Class<?>[]{EnhancedProxy.class}));
return (T) enhancer.create();
} catch (Exception e) {
throw new ProxyInstantiationException("Error creating proxy, cause :" + e.getCause());
}
}
모든 방법으로 인터페이스 만들기-
public interface EnhancedProxy {
public String getJPQLUpdate();
public HashMap<String, Object> getModifiedFields();
}
이제 프록시에서 이러한 메서드를 구현할 수있는 인터셉터를 만듭니다.
import com.anil.app.exception.ProxyInstantiationException;
import javafx.util.Pair;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import javax.persistence.Id;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
/**
* @author Anil Kumar
*/
public class ProxyMethodInterceptor implements MethodInterceptor, EnhancedProxy {
private Object target;
private Object proxy;
private Class classType;
private Pair<String, Object> primaryKey;
private static HashSet<String> enhancedMethods;
ProxyMethodInterceptor(Class classType, Object id) throws IllegalAccessException, InstantiationException {
this.classType = classType;
this.target = classType.newInstance();
this.primaryKey = new Pair<>(getPrimaryKeyField().getName(), id);
}
static {
enhancedMethods = new HashSet<>();
for (Method method : EnhancedProxy.class.getDeclaredMethods()) {
enhancedMethods.add(method.getName());
}
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
//intercept enhanced methods
if (enhancedMethods.contains(method.getName())) {
this.proxy = obj;
return method.invoke(this, args);
}
//else invoke super class method
else
return proxy.invokeSuper(obj, args);
}
@Override
public HashMap<String, Object> getModifiedFields() {
HashMap<String, Object> modifiedFields = new HashMap<>();
try {
for (Field field : classType.getDeclaredFields()) {
field.setAccessible(true);
Object initialValue = field.get(target);
Object finalValue = field.get(proxy);
//put if modified
if (!Objects.equals(initialValue, finalValue)) {
modifiedFields.put(field.getName(), finalValue);
}
}
} catch (Exception e) {
return null;
}
return modifiedFields;
}
@Override
public String getJPQLUpdate() {
HashMap<String, Object> modifiedFields = getModifiedFields();
if (modifiedFields == null || modifiedFields.isEmpty()) {
return null;
}
StringBuilder fieldsToSet = new StringBuilder();
for (String field : modifiedFields.keySet()) {
fieldsToSet.append(field).append(" = :").append(field).append(" and ");
}
fieldsToSet.setLength(fieldsToSet.length() - 4);
return "UPDATE "
+ classType.getSimpleName()
+ " SET "
+ fieldsToSet
+ "WHERE "
+ primaryKey.getKey() + " = " + primaryKey.getValue();
}
private Field getPrimaryKeyField() throws ProxyInstantiationException {
for (Field field : classType.getDeclaredFields()) {
field.setAccessible(true);
if (field.isAnnotationPresent(Id.class))
return field;
}
throw new ProxyInstantiationException("Entity class doesn't have a primary key!");
}
}
그리고 예외 클래스-
public class ProxyInstantiationException extends RuntimeException {
public ProxyInstantiationException(String message) {
super(message);
}
이 프록시를 사용하여 저장할 서비스-
@Service
public class PersistenceService {
@PersistenceContext
private EntityManager em;
@Transactional
private void save(Object entity) {
// update entity for proxies
if (entity instanceof EnhancedProxy) {
EnhancedProxy proxy = (EnhancedProxy) entity;
Query updateQuery = em.createQuery(proxy.getJPQLUpdate());
for (Entry<String, Object> entry : proxy.getModifiedFields().entrySet()) {
updateQuery.setParameter(entry.getKey(), entry.getValue());
}
updateQuery.executeUpdate();
// insert otherwise
} else {
em.persist(entity);
}
}
}