Hibernate: обнаружена ошибка более чем в одной строке с заданным идентификатором
Я использую spring 4.0.5 и hibernate 4.3.5; Я столкнулся с ошибкой в спящем режиме, и я не могу понять, где я ошибаюсь (потому что я уверен, что ошибаюсь). У меня есть таблица, связанная с самим собой; он представляет веб-дерево, где каждый корень node может иметь несколько дочерних элементов; поэтому я создал этот класс:
@DynamicUpdate
@Cache(region = "it.eng.angelo.spring.dao.hibernate.models.WebTree", usage = CacheConcurrencyStrategy.READ_WRITE)
@Entity
@Table(name = "MEDIA_GALL_TREE", indexes = {@Index(name = "NOME_FOLDER_IDX", columnList = "NOME_FOLDER")})
public class WebTree extends AbstractModel
{
private static final long serialVersionUID = -4572195412018767502L;
private long id;
private String text;
private boolean opened;
private boolean disabled;
private boolean selected;
private Set<WebTree> children = new HashSet<WebTree>(0);
private Set<Media> media = new HashSet<Media>(0);
private WebTree father;
private WcmDomain dominio;
public WebTree()
{
super();
}
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID_FOLDER", unique = true, nullable = false)
public long getId()
{
return id;
}
public void setId(long id)
{
this.id = id;
}
@Column(name = "NOME_FOLDER", nullable = false, unique=false)
public String getText()
{
return text;
}
public void setText(String text)
{
this.text = text;
}
@Column(name = "OPENED_FOLDER")
public boolean isOpened()
{
return opened;
}
public void setOpened(boolean opened)
{
this.opened = opened;
}
@Column(name = "DISABLED_FOLDER")
public boolean isDisabled()
{
return disabled;
}
public void setDisabled(boolean disabled)
{
this.disabled = disabled;
}
@Column(name = "SELECTED_FOLDER")
public boolean isSelected()
{
return selected;
}
public void setSelected(boolean selected)
{
this.selected = selected;
}
@OneToMany(mappedBy = "father", orphanRemoval = true, targetEntity = WebTree.class)
public Set<WebTree> getChildren()
{
return children;
}
public void setChildren(Set<WebTree> children)
{
this.children = children;
}
@ManyToOne(targetEntity = WebTree.class)
@JoinColumn(name = "ID_PADRE", nullable = true)
public WebTree getFather()
{
return father;
}
public void setFather(WebTree father)
{
this.father = father;
}
@OneToOne
@JoinColumn(name="ID_DOMINIO", nullable=false)
public WcmDomain getDominio()
{
return dominio;
}
public void setDominio(WcmDomain dominio)
{
this.dominio = dominio;
}
@OneToMany( mappedBy = "folder", orphanRemoval = true, targetEntity = Media.class, cascade = { CascadeType.ALL })
public Set<Media> getMedia()
{
return media;
}
public void setMedia(Set<Media> media)
{
this.media = media;
}
}
Как вы можете видеть... это довольно простой класс POJO; теперь я создал этот unit test:
@Test
public void testLoadModifyTree()
{
try
{
DetachedCriteria dc = DetachedCriteria.forClass(MediaGalleryTree.class);
dc.setFetchMode("father", FetchMode.JOIN);
dc.add(Property.forName("id").eq(4l));
List<MediaGalleryTree> result = hibSvc.search(dc, IConstants.NO_PAGINATION, IConstants.NO_PAGINATION);
for (MediaGalleryTree mediaGalleryTree : result)
{
logger.info(mediaGalleryTree.getId());
}
}
catch (Exception e)
{
logger.error(e.getMessage(), e);
}
}
Ну, я проверил, и в БД я имею только 1 запись с ID 4; хорошо, когда я выполняю этот запрос, я получил следующую ошибку:
18:48:43,123 ERROR [WcmHibernateDao] Errore nella ricerca con detached criteria DetachableCriteria(CriteriaImpl(it.eng.comi.spring.dao.hibernate.models.MediaGalleryTree:this[][id=4])); More than one row with the given identifier was found: 2, for class: it.eng.comi.spring.dao.hibernate.models.MediaGalleryTree
org.hibernate.HibernateException: More than one row with the given identifier was found: 2, for class: it.eng.comi.spring.dao.hibernate.models.MediaGalleryTree
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:100)
at org.hibernate.loader.entity.EntityLoader.loadByUniqueKey(EntityLoader.java:161)
at org.hibernate.persister.entity.AbstractEntityPersister.loadByUniqueKey(AbstractEntityPersister.java:2385)
at org.hibernate.type.EntityType.loadByUniqueKey(EntityType.java:767)
at org.hibernate.type.EntityType.resolve(EntityType.java:505)
at org.hibernate.engine.internal.TwoPhaseLoad.doInitializeEntity(TwoPhaseLoad.java:170)
at org.hibernate.engine.internal.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:144)
at org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:1114)
at org.hibernate.loader.Loader.processResultSet(Loader.java:972)
at org.hibernate.loader.Loader.doQuery(Loader.java:920)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:354)
at org.hibernate.loader.Loader.doList(Loader.java:2553)
at org.hibernate.loader.Loader.doList(Loader.java:2539)
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2369)
at org.hibernate.loader.Loader.list(Loader.java:2364)
at org.hibernate.loader.criteria.CriteriaLoader.list(CriteriaLoader.java:126)
at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1682)
at org.hibernate.internal.CriteriaImpl.list(CriteriaImpl.java:380)
at it.eng.comi.spring.dao.WcmHibernateDao.searchEntity(WcmHibernateDao.java:140)
at it.eng.comi.spring.service.impl.WcmRdbmsExtSvcImpl.search(WcmRdbmsExtSvcImpl.java:237)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:98)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy45.search(Unknown Source)
at it.eng.comi.test.ComiTests.testLoadModifyTree(ComiTests.java:578)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:233)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:87)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:176)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
18:48:43,135 ERROR [WcmRdbmsExtSvcImpl] Errore nella ricerca con deatchedCriteria DetachableCriteria(CriteriaImpl(it.eng.comi.spring.dao.hibernate.models.MediaGalleryTree:this[][id=4])); Errore nella ricerca con detached criteria DetachableCriteria(CriteriaImpl(it.eng.comi.spring.dao.hibernate.models.MediaGalleryTree:this[][id=4])); More than one row with the given identifier was found: 2, for class: it.eng.comi.spring.dao.hibernate.models.MediaGalleryTree
it.eng.comi.exception.CoMiDbException: Errore nella ricerca con detached criteria DetachableCriteria(CriteriaImpl(it.eng.comi.spring.dao.hibernate.models.MediaGalleryTree:this[][id=4])); More than one row with the given identifier was found: 2, for class: it.eng.comi.spring.dao.hibernate.models.MediaGalleryTree
at it.eng.comi.spring.dao.WcmHibernateDao.searchEntity(WcmHibernateDao.java:146)
at it.eng.comi.spring.service.impl.WcmRdbmsExtSvcImpl.search(WcmRdbmsExtSvcImpl.java:237)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:98)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy45.search(Unknown Source)
at it.eng.comi.test.ComiTests.testLoadModifyTree(ComiTests.java:578)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:233)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:87)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:176)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: org.hibernate.HibernateException: More than one row with the given identifier was found: 2, for class: it.eng.comi.spring.dao.hibernate.models.MediaGalleryTree
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:100)
at org.hibernate.loader.entity.EntityLoader.loadByUniqueKey(EntityLoader.java:161)
at org.hibernate.persister.entity.AbstractEntityPersister.loadByUniqueKey(AbstractEntityPersister.java:2385)
at org.hibernate.type.EntityType.loadByUniqueKey(EntityType.java:767)
at org.hibernate.type.EntityType.resolve(EntityType.java:505)
at org.hibernate.engine.internal.TwoPhaseLoad.doInitializeEntity(TwoPhaseLoad.java:170)
at org.hibernate.engine.internal.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:144)
at org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:1114)
at org.hibernate.loader.Loader.processResultSet(Loader.java:972)
at org.hibernate.loader.Loader.doQuery(Loader.java:920)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:354)
at org.hibernate.loader.Loader.doList(Loader.java:2553)
at org.hibernate.loader.Loader.doList(Loader.java:2539)
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2369)
at org.hibernate.loader.Loader.list(Loader.java:2364)
at org.hibernate.loader.criteria.CriteriaLoader.list(CriteriaLoader.java:126)
at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1682)
at org.hibernate.internal.CriteriaImpl.list(CriteriaImpl.java:380)
at it.eng.comi.spring.dao.WcmHibernateDao.searchEntity(WcmHibernateDao.java:140)
... 43 more
Когда код выполняется, hibernate распечатывает эти запросы:
Hibernate:
select
this_.ID_FOLDER as ID_FOLDE1_7_2_,
this_.UT_INS as UT_INS2_7_2_,
this_.DT_INS as DT_INS3_7_2_,
this_.DT_UPD as DT_UPD4_7_2_,
this_.UT_UPD as UT_UPD5_7_2_,
this_.DISABLED_FOLDER as DISABLED6_7_2_,
this_.ID_DOMINIO as ID_DOMI10_7_2_,
this_.ID_PADRE as ID_PADR11_7_2_,
this_.OPENED_FOLDER as OPENED_F7_7_2_,
this_.SELECTED_FOLDER as SELECTED8_7_2_,
this_.NOME_FOLDER as NOME_FOL9_7_2_,
wcmdomain2_.ID_DOMINIO as ID_DOMIN1_8_0_,
wcmdomain2_.UT_INS as UT_INS2_8_0_,
wcmdomain2_.DT_INS as DT_INS3_8_0_,
wcmdomain2_.DT_UPD as DT_UPD4_8_0_,
wcmdomain2_.UT_UPD as UT_UPD5_8_0_,
wcmdomain2_.WCM_NOME_DOMINIO as WCM_NOME6_8_0_,
mediagalle3_.ID_FOLDER as ID_FOLDE1_7_1_,
mediagalle3_.UT_INS as UT_INS2_7_1_,
mediagalle3_.DT_INS as DT_INS3_7_1_,
mediagalle3_.DT_UPD as DT_UPD4_7_1_,
mediagalle3_.UT_UPD as UT_UPD5_7_1_,
mediagalle3_.DISABLED_FOLDER as DISABLED6_7_1_,
mediagalle3_.ID_DOMINIO as ID_DOMI10_7_1_,
mediagalle3_.ID_PADRE as ID_PADR11_7_1_,
mediagalle3_.OPENED_FOLDER as OPENED_F7_7_1_,
mediagalle3_.SELECTED_FOLDER as SELECTED8_7_1_,
mediagalle3_.NOME_FOLDER as NOME_FOL9_7_1_
from
MEDIA_GALL_TREE this_
inner join
WCM_DOMAIN wcmdomain2_
on this_.ID_DOMINIO=wcmdomain2_.ID_DOMINIO
left outer join
MEDIA_GALL_TREE mediagalle3_
on this_.ID_PADRE=mediagalle3_.ID_FOLDER
where
this_.ID_FOLDER=?
Hibernate:
select
mediagalle0_.ID_FOLDER as ID_FOLDE1_7_2_,
mediagalle0_.UT_INS as UT_INS2_7_2_,
mediagalle0_.DT_INS as DT_INS3_7_2_,
mediagalle0_.DT_UPD as DT_UPD4_7_2_,
mediagalle0_.UT_UPD as UT_UPD5_7_2_,
mediagalle0_.DISABLED_FOLDER as DISABLED6_7_2_,
mediagalle0_.ID_DOMINIO as ID_DOMI10_7_2_,
mediagalle0_.ID_PADRE as ID_PADR11_7_2_,
mediagalle0_.OPENED_FOLDER as OPENED_F7_7_2_,
mediagalle0_.SELECTED_FOLDER as SELECTED8_7_2_,
mediagalle0_.NOME_FOLDER as NOME_FOL9_7_2_,
wcmdomain1_.ID_DOMINIO as ID_DOMIN1_8_0_,
wcmdomain1_.UT_INS as UT_INS2_8_0_,
wcmdomain1_.DT_INS as DT_INS3_8_0_,
wcmdomain1_.DT_UPD as DT_UPD4_8_0_,
wcmdomain1_.UT_UPD as UT_UPD5_8_0_,
wcmdomain1_.WCM_NOME_DOMINIO as WCM_NOME6_8_0_,
mediagalle2_.ID_FOLDER as ID_FOLDE1_7_1_,
mediagalle2_.UT_INS as UT_INS2_7_1_,
mediagalle2_.DT_INS as DT_INS3_7_1_,
mediagalle2_.DT_UPD as DT_UPD4_7_1_,
mediagalle2_.UT_UPD as UT_UPD5_7_1_,
mediagalle2_.DISABLED_FOLDER as DISABLED6_7_1_,
mediagalle2_.ID_DOMINIO as ID_DOMI10_7_1_,
mediagalle2_.ID_PADRE as ID_PADR11_7_1_,
mediagalle2_.OPENED_FOLDER as OPENED_F7_7_1_,
mediagalle2_.SELECTED_FOLDER as SELECTED8_7_1_,
mediagalle2_.NOME_FOLDER as NOME_FOL9_7_1_
from
MEDIA_GALL_TREE mediagalle0_
inner join
WCM_DOMAIN wcmdomain1_
on mediagalle0_.ID_DOMINIO=wcmdomain1_.ID_DOMINIO
left outer join
MEDIA_GALL_TREE mediagalle2_
on mediagalle0_.ID_PADRE=mediagalle2_.ID_FOLDER
where
mediagalle0_.ID_DOMINIO=?
Это мои записи в таблице:
id_folder; ut_ins; dt_ins; dt_upd; ut_upd; disabled_folder; opened_folder; selected_folder; nome_folder; id_dominio; id_padre
"1";"system";"2014-06-12 18:23:16.649";"2014-06-12 18:23:16.649";"system";FALSE;FALSE;FALSE;"Root 1";1;
"2";"system";"2014-06-12 18:23:16.662";"2014-06-12 18:23:16.662";"system";FALSE;FALSE;FALSE;"Root 2";2;
"4";"wpsAdmin";"2014-06-13 16:18:01.428";"2014-06-13 18:12:14.228";"wpsAdmin";FALSE;FALSE;FALSE;"Testina";2;2
"7";"wpsAdmin";"2014-06-13 17:33:05.575";"2014-06-13 17:33:10.275";"wpsAdmin";FALSE;FALSE;FALSE;"Angelo";2;2
Кто-нибудь скажет мне, где я ошибаюсь? Мне кажется, все правильно.... любые советы приветствуются
Спасибо.
Angelo
Просто примечание: все работает очень хорошо, если я chenge мой метод POJO класса getChildern следующим образом:
@OneToMany( mappedBy = "father", targetEntity = WebTree.class, fetch=FetchType.EAGER)
public Set<WebTree> getChildren()
{
return children;
}
Но мне интересно, почему это поведение?
Спасибо.
Angelo
Ответы
Ответ 1
Я получил бит org.hibernate.HibernateException: More than one row with the given identifier was found:
и не нашел помощи в StackOverflow. Потребовалось некоторое время, чтобы разобраться с проблемой, поэтому я документирую решение здесь. Я использовал JPA/Hibernate и Spring Data.
Во-первых, это не было вызвано дублирующимися строками в базе данных, так как, очевидно, не возможно иметь повторяющиеся первичные ключи. Вместо этого это было вызвано тем, что Hibernate просматривает объект и с готовностью заполняет отношения "один-к-одному" с LEFT OUTER JOIN. Hibernate предположил, что одна строка вернется, но два вернулись, потому что было два объекта, связанных с отношением "один к одному".
Здесь упрощенная версия моих объектов:
@Entity
@Table(name = "plate")
public class Plate {
private static final long serialVersionUID = 1L;
@Id
@SequenceGenerator(name="test_seq", sequenceName="test_seq")
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "test_seq")
@Column(name = "id")
private Long id;
@Version
@Column(name = "object_version")
private long objectVersion;
@Column(name = "name")
private String name;
@OneToOne(mappedBy = "plate")
private Sheet sheet;
public Sheet getSheet() {
return sheet;
}
public void setSheet(Sheet sheet) {
if (this.sheet != null) {
this.sheet.setPlate(null);
}
this.sheet = sheet;
sheet.setPlate(this);
}
}
@Entity
@Table(name = "sheet")
public class Sheet {
private static final long serialVersionUID = 1L;
@Id
@SequenceGenerator(name="test_seq", sequenceName="test_seq")
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "test_seq")
@Column(name = "id")
private Long id;
@Version
@Column(name = "object_version")
private long objectVersion;
@Column(name = "sheet_name")
private String sheetName;
@OneToOne
@JoinColumn(name = "plate_id")
private Plate plate;
public Plate getPlate() {
return plate;
}
// Do not use. Use Plate.setSheet() instead
void setPlate(Plate plate) {
this.plate = plate;
}
}
Проблема связана с отношением @OneToOne между табличкой и листом. Я изначально не хотел удалять сиротские листы. В моем коде я поднял Plate по id и добавил новый лист. Это устранило отношение Plate-to-Sheet от исходного листа. Когда я совершаю транзакцию, я предположил, что JPA сохранит все измененные объекты (Plate, оригинальный лист и новый лист).
Это неправда! JPA, по-видимому, находит измененные объекты, идя вниз по иерархии с объекта, который был первоначально загружен (Plate), и он пропускает тот факт, что исходный Лист, который теперь осиротел, был изменен. Это означало, что исходный столбец Sheet.plate_id не был очищен в базе данных. Другими словами, я нарушил их отношения в модели данных, но он не смог сохранить базу данных. Поэтому в следующий раз, когда я попытаюсь загрузить табличку, Hibernate запускает такой запрос:
select
plate1_.id as id1_19_12_,
plate1_.object_version as object_v2_19_12_,
plate1_.name as name3_19_12_,
sheet2_.id as id1_39_12_,
sheet2_.object_version as object_v2_39_12_,
sheet2_.sheet_name as sheet_nam2_39_12_,
sheet2_.plate_id as plate_id4_39_12_,
from
plate plate1_
left outer join sheet sheet2_ on plate1_.id = sheet2_.plate_id
where
plate1_.id=?
Что возвращает 2 строки в результирующем наборе и вызывает эту ошибку:
org.hibernate.HibernateException: More than one row with the given identifier was found: 10045, for class: com.example.Plate
Это обманчиво: в базе данных есть только одна строка Plate с этим id, но есть две строки, связанные с ней.
Решение:
Кажется, у меня есть два варианта: либо каскадировать все, либо удалять сироту по взаимно-однозначным отношениям, либо явно вызвать класс моего репозитория для поиска и сохранения исходного листа каждый раз, когда я удаляю его связь с Plate. Я выбрал первый вариант и добавил это к классу пластин:
@OneToOne(mappedBy = "plate", cascade = CascadeType.ALL, orphanRemoval = true)
private Sheet sheet;
Это устранило проблему.
Ответ 2
Извините; это была моя ошибка... в моем классе сущности я неправильно сопоставил отношение как oneToOne; это было, вместо, одноТоменное:)
Теперь все работает неплохо.....; это мой новый класс сущностей:
@DynamicUpdate
@Cache(region = "it.eng.angelo.spring.dao.hibernate.models.MediaGalleryTree", usage = CacheConcurrencyStrategy.READ_WRITE)
@Entity
@Table(name = "MEDIA_GALL_TREE", indexes = {@Index(name = "NOME_FOLDER_IDX", columnList = "NOME_FOLDER")})
public class MediaGalleryTree extends AbstractModel
{
private static final long serialVersionUID = -4572195412018767502L;
private long id;
private String text;
private boolean opened;
private boolean disabled;
private boolean selected;
private Set<MediaGalleryTree> children = new HashSet<MediaGalleryTree>(0);
private Set<FedoraCommonsEntity> media = new HashSet<FedoraCommonsEntity>(0);
private MediaGalleryTree father;
private WcmDomain dominio;
public MediaGalleryTree()
{
super();
}
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID_FOLDER", unique = true, nullable = false)
public long getId()
{
return id;
}
public void setId(long id)
{
this.id = id;
}
@Column(name = "NOME_FOLDER", nullable = false, unique=false)
public String getText()
{
return text;
}
public void setText(String text)
{
this.text = text;
}
@Column(name = "OPENED_FOLDER")
public boolean isOpened()
{
return opened;
}
public void setOpened(boolean opened)
{
this.opened = opened;
}
@Column(name = "DISABLED_FOLDER")
public boolean isDisabled()
{
return disabled;
}
public void setDisabled(boolean disabled)
{
this.disabled = disabled;
}
@Column(name = "SELECTED_FOLDER")
public boolean isSelected()
{
return selected;
}
public void setSelected(boolean selected)
{
this.selected = selected;
}
@OneToMany( mappedBy = "father", orphanRemoval = true,
targetEntity = MediaGalleryTree.class)
public Set<MediaGalleryTree> getChildren()
{
return children;
}
public void setChildren(Set<MediaGalleryTree> children)
{
this.children = children;
}
@ManyToOne(targetEntity = MediaGalleryTree.class)
@JoinColumn(name = "ID_PADRE", nullable = true)
public MediaGalleryTree getFather()
{
return father;
}
public void setFather(MediaGalleryTree father)
{
this.father = father;
}
@ManyToOne(targetEntity = WcmDomain.class, cascade={CascadeType.ALL})
@JoinColumn(name="ID_DOMINIO", nullable=false)
public WcmDomain getDominio()
{
return dominio;
}
public void setDominio(WcmDomain dominio)
{
this.dominio = dominio;
}
@OneToMany( mappedBy = "folder", orphanRemoval = true,
targetEntity = Media.class, cascade = { CascadeType.ALL })
public Set<FedoraCommonsEntity> getMedia()
{
return media;
}
public void setMedia(Set<FedoraCommonsEntity> media)
{
this.media = media;
}
}
Анжело
Ответ 3
Попробуйте проверить отношение Entity @OneToOne, в вашем случае проверьте объект WcmDomain, он будет содержать две записи, относящиеся к WebTree. поэтому, как только вы загрузите WebTree с помощью findById, он будет терпеть неудачу, поскольку он выполняет внутреннее соединение, а конечным результатом будет две записи.
Так что удалите эту дополнительную запись из WcmDomain, она будет работать
Ответ 4
У меня была та же проблема, и я также изменяю отображение отображения OneToOne на отображение OneToMany. Раньше я ошибочно определил отображение как OneToOne. Теперь он работает правильно.
Старый
@OneToOne(cascade = CascadeType.ALL,fetch = FetchType.LAZY)
@JoinColumn(name="appointmentId", nullable=false, insertable=false, updatable=false)
private Appointment appointment;
Текущее
@ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.LAZY)
@JoinColumn(name="appointmentId", nullable=false, insertable=false, updatable=false)
private Appointment appointment;
Ответ 5
Найти в hibernate-сопоставление отношения один-на-один, после чего найти в базе данных в связанной таблице, возможно, что таблица отношений получает более одной строки