Отсоединить объект от контекста сохранения JPA/EJB3
Что было бы самым простым способом отсоединить определенный компонент сущности JPA, который был получен через EntityManager. Кроме того, могу ли я получить запрос, возвращающий отсоединенные объекты в первую очередь, чтобы они по существу действовали как "только для чтения"?
причина, по которой я хочу это сделать, заключается в том, что я хочу изменить данные в bean - with только в своем приложении, но никогда не сохранял их в базе данных. В моей программе, я в конечном итоге должен вызвать flush() на EntityManager, который сохранит все изменения из вложенных объектов в базу данных underyling, но я хочу исключить определенные объекты.
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, но я думаю, что это предпочтительно, чтобы попытаться сделать глубокую копию.
насколько я знаю, единственные прямые способы сделать это:
- фиксация txn-вероятно, не разумный вариант
- очистить контекст сохранения-EntityManager.clear () - это жестоко, но очистит его
- копировать объект-большую часть времени ваши объекты 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);
Маурисио Канада, спасибо вам за этот совет, метод выселения() работает хорошо. Я использую 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 потоков и конвейерных потоков. создание потоков во избежание взаимоблокировок убивает всю концепцию.