Saya tidak setuju dengan jawaban yang dipilih, dan seperti yang ditunjukkan davidxxx dengan benar, getReference tidak menyediakan perilaku pembaruan dinamis tanpa pemilihan. Saya mengajukan pertanyaan tentang validitas jawaban ini, lihat di sini - tidak dapat memperbarui tanpa memilih menggunakan penyetel setelah getReference () dari hibernate JPA .
Sejujurnya saya belum melihat siapa pun yang benar-benar menggunakan fungsi itu. DI MANA SAJA. Dan saya tidak mengerti mengapa itu sangat disukai.
Sekarang pertama-tama, apa pun yang Anda panggil pada objek proxy hibernasi, penyetel atau pengambil, SQL dijalankan dan objek dimuat.
Tapi kemudian saya berpikir, jadi bagaimana jika JPA getReference () proxy tidak menyediakan fungsionalitas itu. Saya bisa menulis proxy saya sendiri.
Sekarang, kita semua dapat berargumen bahwa pemilihan pada kunci primer secepat yang bisa didapat kueri dan itu bukan sesuatu yang harus dihindari. Namun bagi kita yang tidak bisa mengatasinya karena satu dan lain hal, di bawah ini adalah implementasi dari proxy tersebut. Tetapi sebelum saya Anda melihat implementasinya, lihat penggunaannya dan betapa sederhananya penggunaannya.
PEMAKAIAN
Order example = ProxyHandler.getReference(Order.class, 3);
example.setType("ABCD");
example.setCost(10);
PersistenceService.save(example);
Dan ini akan mengaktifkan kueri berikut -
UPDATE Order SET type = 'ABCD' and cost = 10 WHERE id = 3;
dan bahkan jika Anda ingin memasukkan, Anda masih bisa melakukan PersistenceService.save (new Order ("a", 2)); dan itu akan mengaktifkan sisipan sebagaimana mestinya.
PENERAPAN
Tambahkan ini ke pom.xml Anda -
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.10</version>
</dependency>
Jadikan kelas ini untuk membuat proxy dinamis -
@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());
}
}
Buat antarmuka dengan semua metode -
public interface EnhancedProxy {
public String getJPQLUpdate();
public HashMap<String, Object> getModifiedFields();
}
Sekarang, buat interseptor yang memungkinkan Anda menerapkan metode ini pada proxy Anda -
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!");
}
}
Dan kelas pengecualian -
public class ProxyInstantiationException extends RuntimeException {
public ProxyInstantiationException(String message) {
super(message);
}
Layanan untuk menyimpan menggunakan proxy ini -
@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);
}
}
}