Não concordo com a resposta selecionada e, como davidxxx corretamente apontou, getReference não fornece tal comportamento de atualizações dinâmicas sem select. Eu fiz uma pergunta sobre a validade desta resposta, veja aqui - não é possível atualizar sem emitir select on using setter após getReference () do Hibernate JPA .
Sinceramente, não vi ninguém que realmente usou essa funcionalidade. QUALQUER LUGAR. E eu não entendo por que é tão votado.
Agora, em primeiro lugar, não importa o que você chame em um objeto proxy de hibernação, um setter ou um getter, um SQL é disparado e o objeto é carregado.
Mas então pensei, e se o proxy getReference () JPA não fornecer essa funcionalidade. Posso apenas escrever meu próprio proxy.
Agora, todos nós podemos argumentar que as seleções em chaves primárias são tão rápidas quanto uma consulta pode chegar e não é algo que deva ser evitado. Mas para aqueles de nós que não podem lidar com isso por um motivo ou outro, abaixo está uma implementação de tal proxy. Mas antes de ver a implementação, veja seu uso e como é simples de usar.
USO
Order example = ProxyHandler.getReference(Order.class, 3);
example.setType("ABCD");
example.setCost(10);
PersistenceService.save(example);
E isso dispararia a seguinte consulta -
UPDATE Order SET type = 'ABCD' and cost = 10 WHERE id = 3;
e mesmo se você quiser inserir, você ainda pode fazer PersistenceService.save (new Order ("a", 2)); e dispararia uma inserção como deveria.
IMPLEMENTAÇÃO
Adicione isso ao seu pom.xml -
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.10</version>
</dependency>
Faça esta classe para criar proxy dinâmico -
@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());
}
}
Faça uma interface com todos os métodos -
public interface EnhancedProxy {
public String getJPQLUpdate();
public HashMap<String, Object> getModifiedFields();
}
Agora, faça um interceptor que permitirá que você implemente esses métodos em seu proxy -
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!");
}
}
E a classe de exceção -
public class ProxyInstantiationException extends RuntimeException {
public ProxyInstantiationException(String message) {
super(message);
}
Um serviço para salvar usando este 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);
}
}
}