Tôi không đồng ý với câu trả lời đã chọn và như davidxxx đã chỉ ra một cách chính xác, getReference không cung cấp hành vi cập nhật động mà không có lựa chọn. Tôi đã hỏi một câu hỏi liên quan đến tính hợp lệ của câu trả lời này, xem tại đây - không thể cập nhật mà không đưa ra lựa chọn khi sử dụng setter sau getReference () của JPA ngủ đông .
Tôi thực sự chưa thấy bất kỳ ai thực sự sử dụng chức năng đó. BẤT CỨ Ở ĐÂU. Và tôi không hiểu tại sao nó được ủng hộ như vậy.
Trước hết, bất kể bạn gọi gì trên đối tượng proxy ngủ đông, setter hay getter, SQL sẽ được kích hoạt và đối tượng được tải.
Nhưng sau đó tôi nghĩ, vậy điều gì sẽ xảy ra nếu proxy JPA getReference () không cung cấp chức năng đó. Tôi chỉ có thể viết proxy của riêng mình.
Bây giờ, tất cả chúng ta đều có thể tranh luận rằng các lựa chọn trên khóa chính nhanh như một truy vấn có thể nhận được và nó không thực sự là điều gì đó cần phải tránh xa. Nhưng đối với những người trong chúng ta, những người không thể xử lý nó vì lý do này hay lý do khác, dưới đây là cách triển khai một proxy như vậy. Nhưng trước khi tôi nhìn thấy việc thực hiện, hãy xem cách sử dụng và cách sử dụng nó đơn giản.
SỬ DỤNG
Order example = ProxyHandler.getReference(Order.class, 3);
example.setType("ABCD");
example.setCost(10);
PersistenceService.save(example);
Và điều này sẽ kích hoạt truy vấn sau:
UPDATE Order SET type = 'ABCD' and cost = 10 WHERE id = 3;
và ngay cả khi bạn muốn chèn, bạn vẫn có thể thực hiện PersistenceService.save (new Order ("a", 2)); và nó sẽ kích hoạt một phụ trang như nó cần.
THỰC HIỆN
Thêm cái này vào pom.xml của bạn -
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.10</version>
</dependency>
Tạo lớp này để tạo proxy động -
@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());
}
}
Tạo giao diện với tất cả các phương pháp -
public interface EnhancedProxy {
public String getJPQLUpdate();
public HashMap<String, Object> getModifiedFields();
}
Bây giờ, hãy tạo một bộ đánh chặn cho phép bạn triển khai các phương pháp này trên proxy của mình -
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!");
}
}
Và lớp ngoại lệ -
public class ProxyInstantiationException extends RuntimeException {
public ProxyInstantiationException(String message) {
super(message);
}
Một dịch vụ để tiết kiệm bằng cách sử dụng proxy này -
@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);
}
}
}