Отсоединить объект от контекста сохранения JPA / EJB3
Вопрос
Каков был бы самый простой способ отсоединить конкретный компонент сущности JPA, который был приобретен через EntityManager?В качестве альтернативы, могу ли я заставить запрос возвращать отдельные объекты в первую очередь, чтобы они, по сути, действовали как "только для чтения"?
Причина, по которой я хочу это сделать, заключается в том, что я хочу изменить данные внутри компонента только в моем приложении, но никогда не сохранять их в базе данных.В моей программе мне в конечном итоге приходится вызывать flush() в EntityManager, который сохранит все изменения от подключенных объектов к базовой базе данных, но я хочу исключить определенные объекты.
Решение
К сожалению, в текущей реализации JPA, AFAIR, нет способа отключить один объект от entity manager.
EntityManager.clear() отключит ВСЕ объекты JPA, так что это может быть неподходящим решением не во всех случаях, если у вас есть другие объекты, которые вы планируете поддерживать подключенными.
Поэтому лучше всего было бы клонировать объекты и передавать клоны в код, который изменяет объекты.Поскольку примитивные и неизменяемые поля объектов обрабатываются механизмом клонирования по умолчанию надлежащим образом, вам не придется писать много программного кода (кроме глубокого клонирования любых агрегированных структур, которые у вас могут быть).
Другие советы
(возможно, уже слишком поздно отвечать, но может быть полезно для других)
Прямо сейчас я разрабатываю свою первую систему с JPA.К сожалению, я столкнулся с этой проблемой, когда эта система была почти завершена.
Проще говоря.Используйте режим гибернации или дождитесь JPA 2.0.
В режиме гибернации вы можете использовать 'session.evict(object)', чтобы удалить один объект из сеанса.В JPA 2.0, в черновике прямо сейчас, существует метод 'EntityManager.detach(object)' для отделения одного объекта от контекста сохранения.
Независимо от того, какую реализацию 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);
это быстро и грязно, но вы также можете сериализовать и десериализовать объект.
Поскольку я использую 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, который расширяет постоянный объект 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, вызвав это
EntityManagerFactory emf;
emf.getCache().evict(Entity);
Это приведет к удалению определенного объекта из кэша.
Я думаю, вы также можете использовать метод EntityManager.refresh(объект o), если первичный ключ объекта не был изменен.Этот метод восстановит исходное состояние объекта.