Ich bin mit der ausgewählten Antwort nicht einverstanden, und wie davidxxx richtig hervorhob, bietet getReference kein solches Verhalten für dynamische Aktualisierungen ohne Auswahl. Ich habe eine Frage zur Gültigkeit dieser Antwort gestellt, siehe hier - kann nicht aktualisiert werden, ohne select on using setter nach getReference () von JPA im Ruhezustand auszugeben .
Ich habe ehrlich gesagt niemanden gesehen, der diese Funktionalität tatsächlich genutzt hat. IRGENDWO. Und ich verstehe nicht, warum es so positiv ist.
Unabhängig davon, was Sie für ein Proxy-Objekt im Ruhezustand, einen Setter oder einen Getter aufrufen, wird zunächst eine SQL ausgelöst und das Objekt geladen.
Aber dann dachte ich mir, was ist, wenn der JPA-Proxy getReference () diese Funktionalität nicht bietet? Ich kann einfach meinen eigenen Proxy schreiben.
Jetzt können wir alle argumentieren, dass die Auswahl von Primärschlüsseln so schnell ist, wie eine Abfrage nur sein kann, und dass es nicht wirklich etwas ist, große Anstrengungen zu unternehmen, um dies zu vermeiden. Aber für diejenigen von uns, die aus dem einen oder anderen Grund nicht damit umgehen können, ist unten eine Implementierung eines solchen Proxys aufgeführt. Aber bevor Sie die Implementierung sehen, sehen Sie, wie sie verwendet wird und wie einfach sie zu verwenden ist.
VERWENDUNG
Order example = ProxyHandler.getReference(Order.class, 3);
example.setType("ABCD");
example.setCost(10);
PersistenceService.save(example);
Und dies würde die folgende Abfrage auslösen -
UPDATE Order SET type = 'ABCD' and cost = 10 WHERE id = 3;
und selbst wenn Sie einfügen möchten, können Sie PersistenceService.save (neue Reihenfolge ("a", 2)) ausführen; und es würde einen Einsatz abfeuern, wie es sollte.
IMPLEMENTIERUNG
Fügen Sie dies Ihrer pom.xml hinzu -
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.10</version>
</dependency>
Erstellen Sie diese Klasse, um einen dynamischen Proxy zu erstellen.
@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());
}
}
Erstellen Sie eine Schnittstelle mit allen Methoden -
public interface EnhancedProxy {
public String getJPQLUpdate();
public HashMap<String, Object> getModifiedFields();
}
Erstellen Sie nun einen Interceptor, mit dem Sie diese Methoden auf Ihrem Proxy implementieren können.
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!");
}
}
Und die Ausnahmeklasse -
public class ProxyInstantiationException extends RuntimeException {
public ProxyInstantiationException(String message) {
super(message);
}
Ein Dienst zum Speichern mit diesem Proxy -
@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);
}
}
}