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


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

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

14 52

14 ответов:

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

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

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

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

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

проще говоря. Используйте Hibernate или дождитесь JPA 2.0.

в Hibernate, вы можете использовать сессии.выселить(объект), чтобы удалить один объект из сессии. В JPA 2.0,в проекте прямо сейчас, есть ' EntityManager.метод отсоединения (объекта)' чтобы отделить один объект от контекста персистентности.

независимо от того, какую реализацию 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 - все возвращенные объекты будут отсоединены.

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

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

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

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

затем:

Thing newBean = new Thing(oldBean);

Маурисио Канада, спасибо вам за этот совет, метод выселения() работает хорошо. Я использую JPA из SEAM, там встроена поддержка JPA Entity Manager, и можно получить доступ к сеансу делегирования hibernate и таким образом этот метод "выселить".

большое спасибо, Zmicer

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

поскольку я использую 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, который расширяет объект persistent 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.обновление(объект o) если первичный ключ сущности не изменился. Этот метод восстановит исходное состояние объекта.