Отсоединить сущность от контекста персистентности JPA/EJB3

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

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

16 ответов

Решение

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

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

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

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

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

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

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

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

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

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

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

При использовании EclipseLink you also have the options,

Use the Query hint, eclipselink.maintain-cache"="false - all returned objects will be detached.

Использовать EclipseLinkJpaEntityManagercopy() API to copy the object to the desired depth.

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

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

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

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

Поскольку я использую SEAM и JPA 1.0, и моя система имеет функциональность, которая должна регистрировать все изменения полей, я создал объект значения или объект передачи данных, если те же поля объекта, которые должны быть зарегистрированы. Конструктор нового 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();
            }
        }
    }
}

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

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

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

Затем:

Thing newBean = new Thing(oldBean);

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

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

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

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

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

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

Это удалит конкретную сущность из кэша.

Я использую entityManager.detach(returnObject); который сработал для меня.

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

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

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

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

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

Другие вопросы по тегам