Подтвердить что ты не робот

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

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

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

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

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

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

Ответ 3

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

Ответ 4

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

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

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

Ответ 5

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

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

Ответ 6

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

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

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

Ответ 7

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

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

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

Тогда:

Thing newBean = new Thing(oldBean);

Ответ 8

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

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

Ответ 9

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

Ответ 10

Поскольку я использую 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();
            }
        }
    }
}

Ответ 11

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

Ответ 12

Имею дело с подобным случаем, я создал объект 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

Ответ 13

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

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

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

И забудьте об использовании потоков serializable и bytearray и потоковых потоков. создание потоков во избежание тупиков убивает всю концепцию.

Ответ 14

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