У нас есть приложение Java, которое использует Hibernate для сопоставления объектов с базой данных Oracle, и, хотя все нормально работает, есть частный случай, который вызывает проблемы. Сначала краткий обзор сопоставления:
Пользователь класс, который сопоставлен с APP_USER, который является таблицей. Он имеет сопоставление OneToMany с объектами OrgUser:
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "user")
private Set<OrgUser> orgUsers;
OrgUser класс, который сопоставляется с ENTITY_USER, который является таблицей. Он имеет сопоставление ManyToOne с объектами пользователя и сопоставление ManyToOne с объектами организации:
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "APP_USER_FK", nullable = false)
@Fetch(FetchMode.JOIN)
private User user;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "ENTITY_FK", nullable = false)
@JsonProperty(value = "org")
private Organization organization;
Класс организации , который сопоставляется с ENTITY_VW, который является представлением. Он имеет сопоставление OneToMany с OrgUser:
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "organization")
private Set<OrgUser> orgUsers;
При обычном использовании это нормально. Однако организации могут быть удалены из представления ENTITY_VW, если они больше не активны. Что обычно тоже хорошо; связанные записи ENTITY_USER просто игнорируются. Проблема возникает при попытке добавить новую запись ENTITY_USER через новый объект OrgUser:
OrgUser # 1 (уже существует)
- Пользователь: 1327
- Организация: ORG-00001
OrgUser # 2 (уже существует)
- Пользователь: 1327
- Организация: ORG-00002
OrgUser # 3 (новый)
- Пользователь: 1327
- Организация: ORG-00003
Код создает новый объект OrgUser с User = 1327 и Organization = ORG-00003, которая затем сохраняется через merge () в DAO:
public Object saveOrgObject(Object object) {
return entityManager.merge(object);
}
Однако проблема заключается в том, что merge () пытается коснуться всех связанных записей, и поскольку ORG организации -00002 больше не существует, выдается исключение:
Caused by: javax.persistence.EntityNotFoundException: Unable to find Organization with id ORG-00002
at org.hibernate.ejb.Ejb3Configuration$Ejb3EntityNotFoundDelegate.handleEntityNotFound(Ejb3Configuration.java:155) ~[hibernate-entitymanager-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:210) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.event.internal.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:251) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:148) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.internal.SessionImpl.fireLoad(SessionImpl.java:1079) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.internal.SessionImpl.internalLoad(SessionImpl.java:1006) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.type.EntityType.resolveIdentifier(EntityType.java:613) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.type.EntityType.resolve(EntityType.java:441) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.engine.internal.TwoPhaseLoad.doInitializeEntity(TwoPhaseLoad.java:168) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.engine.internal.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:134) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:995) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.loader.Loader.doQuery(Loader.java:874) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:289) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:259) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.loader.Loader.loadEntity(Loader.java:2033) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:82) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:72) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:3719) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.event.internal.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:449) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.event.internal.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:418) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:204) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.event.internal.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:251) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:148) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.internal.SessionImpl.fireLoad(SessionImpl.java:1079) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.internal.SessionImpl.internalLoad(SessionImpl.java:1006) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.type.EntityType.resolveIdentifier(EntityType.java:613) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.type.EntityType.resolve(EntityType.java:441) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.type.EntityType.replace(EntityType.java:298) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.type.AbstractType.replace(AbstractType.java:178) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.type.TypeHelper.replace(TypeHelper.java:211) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.event.internal.DefaultMergeEventListener.copyValues(DefaultMergeEventListener.java:409) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.event.internal.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:214) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:154) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:76) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:904) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:888) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:892) ~[hibernate-core-4.1.4.Final.jar:4.1.4.Final]
at org.hibernate.ejb.AbstractEntityManagerImpl.merge(AbstractEntityManagerImpl.java:879) ~[hibernate-entitymanager-4.1.4.Final.jar:4.1.4.Final]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.6.0_18]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) ~[na:1.6.0_18]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) ~[na:1.6.0_18]
at java.lang.reflect.Method.invoke(Method.java:597) ~[na:1.6.0_18]
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:240) ~[spring-orm-3.1.0.RELEASE.jar:3.1.0.RELEASE]
at $Proxy53.merge(Unknown Source) ~[na:na]
at OrgUserDao.saveOrgObject(OrgUserDao.java:177) ~[classes/:na]
Вопрос в том, есть ли способ настроить вещи, чтобы это не было проблемой? В худшем случае мы можем вручную удалить осиротевшие записи ENTITY_USER из базы данных, но поскольку объекты могут / будут и дальше удаляться из ENTITY_VW, это будет постоянной проблемой.