невозможно обновить без выдачи выбора при использовании сеттера после getReference () гибернации JPA - PullRequest
0 голосов
/ 17 декабря 2018

У меня есть следующий метод -

 @Transactional
 public void savethis(){
    EntityObject t = entityManagerTreasury.getReference(EntityObject.class, 1);
    t.setAction("abc");
 }

Теперь, следуя следующему ответу - https://stackoverflow.com/a/1608621/4881766

Я должен видеть только запрос на обновление в моих журналах sql.

Однако наблюдаемое мною поведение выглядит следующим образом:

  1. Данный код - выберите, а затем обновите
  2. , комментируя t.setAction ("abc");строка - без выбора и без обновления
  3. с заменой getReference () на find () - выберите, а затем обновите

Я ожидал, что при использовании какого-либо геттера на прокси, тогда должен быть выдан выбор, но когда я использовал только сеттер, я хотел, чтобы изменения были зафиксированы в конце метода с обновлением, а выбор не выдавался.

Получается, независимо от того, что яделать с прокси-объектом, геттером или сеттером, он выдает выбор.

Я хочу обновить выбранные поля объекта для данного идентификатора.Если есть какой-либо способ обновить какие-либо поля, которые я хочу, без написания jpql или нативного запроса, я буду очень признателен.

Заранее спасибо!

Ответы [ 2 ]

0 голосов
/ 27 декабря 2018

Так что, если прокси JPA getReference () не предоставляет такую ​​функциональность.Я могу просто написать свой собственный прокси.

Теперь мы все можем утверждать, что выбор по первичным ключам настолько быстр, насколько может быть получен запрос, и на самом деле это не то, чего нужно избегать.Но для тех из нас, кто не может справиться с этим по той или иной причине, ниже приведена реализация такого прокси.Но прежде чем вы увидите реализацию, посмотрите, как она используется и как просто ее использовать.

USAGE

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);
    }

}
}
0 голосов
/ 17 декабря 2018

Из документации EntityManager.getReference () :

Получить экземпляр, состояние которого может быть лениво извлечено.

Поэтому послеentityManagerTreasury.getReference выбор не производится.

Только после t.setAction("abc"), если состояние объекта еще не выбрано, выдается выбор для выборки состояния.

Точка: менеджер сущностей не может сохранить состояние сущности, пока не будет извлечено состояние сущности .Поэтому вы не можете пропустить предыдущий выбор, если вы не используете JPQL.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...