Отсоединить сущность от контекста персистентности 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.
Использовать EclipseLink
JpaEntityManager
copy()
API to copy the object to the desired depth.
Насколько я знаю, единственные прямые способы сделать это:
- Совершить txn - вероятно, не разумный вариант
- Очистить контекст постоянства - EntityManager.clear() - это жестоко, но вычистило бы это
- Скопируйте объект. В большинстве случаев ваши 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), если первичный ключ сущности не был изменен. Этот метод восстановит исходное состояние объекта.