Отсоединить объект от контекста сохранения JPA / EJB3

StackOverflow https://stackoverflow.com/questions/31446

  •  09-06-2019
  •  | 
  •  

Вопрос

Каков был бы самый простой способ отсоединить конкретный компонент сущности JPA, который был приобретен через EntityManager?В качестве альтернативы, могу ли я заставить запрос возвращать отдельные объекты в первую очередь, чтобы они, по сути, действовали как "только для чтения"?

Причина, по которой я хочу это сделать, заключается в том, что я хочу изменить данные внутри компонента только в моем приложении, но никогда не сохранять их в базе данных.В моей программе мне в конечном итоге приходится вызывать flush() в EntityManager, который сохранит все изменения от подключенных объектов к базовой базе данных, но я хочу исключить определенные объекты.

Это было полезно?

Решение

К сожалению, в текущей реализации JPA, AFAIR, нет способа отключить один объект от entity manager.

EntityManager.clear() отключит ВСЕ объекты JPA, так что это может быть неподходящим решением не во всех случаях, если у вас есть другие объекты, которые вы планируете поддерживать подключенными.

Поэтому лучше всего было бы клонировать объекты и передавать клоны в код, который изменяет объекты.Поскольку примитивные и неизменяемые поля объектов обрабатываются механизмом клонирования по умолчанию надлежащим образом, вам не придется писать много программного кода (кроме глубокого клонирования любых агрегированных структур, которые у вас могут быть).

Другие советы

(возможно, уже слишком поздно отвечать, но может быть полезно для других)

Прямо сейчас я разрабатываю свою первую систему с JPA.К сожалению, я столкнулся с этой проблемой, когда эта система была почти завершена.

Проще говоря.Используйте режим гибернации или дождитесь JPA 2.0.

В режиме гибернации вы можете использовать 'session.evict(object)', чтобы удалить один объект из сеанса.В JPA 2.0, в черновике прямо сейчас, существует метод 'EntityManager.detach(object)' для отделения одного объекта от контекста сохранения.

Независимо от того, какую реализацию JPA вы используете, просто используйте entityManager.detach(object) теперь это в JPA 2.0 и является частью JEE6.

Если вам нужно отсоединить объект от EntityManager и вы используете Hibernate в качестве базового уровня ORM, вы можете получить доступ к Сеанс перехода в спящий режим возражать и использовать Сессия.выселить(Объект) метод, о котором упоминал выше Маурисио Канада.

public void detach(Object entity) {
    org.hibernate.Session session = (Session) entityManager.getDelegate();
    session.evict(entity);
}

Конечно, это сломалось бы, если бы вы переключились на другого поставщика ORM, но я думаю, что это предпочтительнее, чем пытаться сделать глубокую копию.

Насколько я знаю, единственными прямыми способами сделать это являются:

  1. Зафиксировать txn - Вероятно, не самый разумный вариант
  2. Очистите контекст сохранения - EntityManager.clear() - Это жестоко, но позволит устранить его
  3. Скопируйте объект - Большую часть времени ваши объекты JPA сериализуемы, так что это должно быть легко (если не особенно эффективно).

При использовании EclipseLink у вас также есть варианты,

Используйте подсказку по запросу, eclipselink.maintain-cache"="false - все возвращенные объекты будут отсоединены.

Используйте EclipseLink JpaEntityManager copy() API для копирования объекта на нужную глубину.

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

Это может быть реализовано как конструктор копирования, например:

public Thing(Thing oldBean) {
  this.setPropertyOne(oldBean.getPropertyOne());
  // and so on
}

Тогда:

Thing newBean = new Thing(oldBean);

это быстро и грязно, но вы также можете сериализовать и десериализовать объект.

Поскольку я использую SEAM и JPA 1.0, и в моей системе есть функциональность, которая должна регистрировать все изменения полей, я создал объект value или объект передачи данных, если те же поля объекта, которые необходимо зарегистрировать.Конструктором нового pojo является:

    public DocumentoAntigoDTO(Documento documentoAtual) {
    Method[] metodosDocumento = Documento.class.getMethods();
    for(Method metodo:metodosDocumento){
        if(metodo.getName().contains("get")){
            try {
                Object resultadoInvoke = metodo.invoke(documentoAtual,null);
                Method[] metodosDocumentoAntigo = DocumentoAntigoDTO.class.getMethods();
                for(Method metodoAntigo : metodosDocumentoAntigo){
                    String metodSetName = "set" + metodo.getName().substring(3);
                    if(metodoAntigo.getName().equals(metodSetName)){
                        metodoAntigo.invoke(this, resultadoInvoke);
                    }
                }
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }
}

В JPA 1.0 (протестировано с использованием EclipseLink) вы могли бы извлечь объект вне транзакции.Например, с помощью транзакций, управляемых контейнерами, вы могли бы сделать:

public MyEntity myMethod(long id) {
    final MyEntity myEntity = retrieve(id);
    // myEntity is detached here
}

@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public MyEntity retrieve(long id) {
    return entityManager.find(MyEntity.class, id);
}

Я имею дело с подобным случаем, я создал объект DTO, который расширяет постоянный объект entity следующим образом:

class MyEntity
{
   public static class MyEntityDO extends MyEntity {}

}

Наконец, скалярный запрос извлечет нужные неуправляемые атрибуты:

(Hibernate) select p.id, p.name from MyEntity P
(JPA)       select new MyEntity(p.id, p.name) from myEntity P

Если вы попали сюда, потому что на самом деле хотите передать объект через удаленную границу, то вы просто вводите некоторый код, чтобы обмануть hibernazi.

for(RssItem i : result.getChannel().getItem()){
}

Cloneable не будет работать, потому что он фактически копирует PersistantBag поперек.

И забудьте об использовании сериализуемых потоков, потоков bytearray и конвейерных потоков.создание потоков, позволяющих избежать взаимоблокировок, убивает всю концепцию.

Я думаю, что есть способ удалить один объект из EntityManager, вызвав это

EntityManagerFactory emf;
emf.getCache().evict(Entity);

Это приведет к удалению определенного объекта из кэша.

Я думаю, вы также можете использовать метод EntityManager.refresh(объект o), если первичный ключ объекта не был изменен.Этот метод восстановит исходное состояние объекта.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top