JPA - OneToMany 坚持 - EntityExistsException
Posted
技术标签:
【中文标题】JPA - OneToMany 坚持 - EntityExistsException【英文标题】:JPA - OneToMany persist - EntityExistsException 【发布时间】:2017-01-02 01:09:19 【问题描述】:通过 SO 搜索,但我找不到我的案例。
有一个实体卡,像这样:
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ForeignKey;
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.NotEmpty;
@Entity
@ToString(exclude="playRequirements")
@Table(name = "CARD",
indexes = @Index(name = "CARD_IDX_1", columnList="ID", unique = true))
public class Card implements Serializable
@Getter
@Setter
@NotEmpty
@NotBlank
@Id
@Column(name = "ID", unique = true, nullable = false)
private String id;
@Getter
@Setter
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy="card")
private List<CardPlayRequirementsRel> playRequirements;
作为与实体 CardPlayRequirementsRel 的反比关系:
import java.io.Serializable;
import hscli.entities.domain.PlayRequirement;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ForeignKey;
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.validator.constraints.NotEmpty;
@Entity
@ToString
@EqualsAndHashCode(of="card", "playRequirement")
@Table(name = "CARD_PLAY_REQUIREMENT_REL",
indexes=@Index(name="CARD_PLAY_REQ_REL_IDX_1", unique=true, columnList="CARD,PLAY_REQUIREMENT"))
public class CardPlayRequirementsRel implements Serializable
@Id
@Getter
@Setter
@NotEmpty
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "CARD", nullable = false, foreignKey = @ForeignKey(name = "FK_CARD_PLAY_REQ_REL_CARD"))
private Card card;
@Id
@Getter
@Setter
@NotEmpty
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "PLAY_REQUIREMENT", nullable = false, foreignKey = @ForeignKey(name = "FK_CARD_PLAY_REQ_REL_PLAY_REQ"))
private PlayRequirement playRequirement;
@Getter
@Setter
@NotEmpty
@Column(name = "VALUE", nullable = false)
private Long value;
我想保留一张卡片列表:
[...]
List<CardDTO> dtoList = connectionService.getCardDTO();
List<Card> toSave = new ArrayList<Card>();
for(CardDTO dto : dtoList)
log.debug("----------------> id: [" + dto.getId() + "]");
Card entity = new Card();
dtoToCardEntity(dto, entity);
toSave.add(entity);
cardRepository.save(toSave);
[...]
private void dtoToCardEntity(CardDTO dto, Card entity)
entity.setId(dto.getId());
if(dto.getPlayRequirements() != null && CollectionUtils.isNotEmpty(dto.getPlayRequirements().keySet()))
entity.setPlayRequirements(new ArrayList<CardPlayRequirementsRel>());
for(String playReq : dto.getPlayRequirements().keySet())
CardPlayRequirementsRel rel = new CardPlayRequirementsRel();
rel.setCard(entity);
rel.setPlayRequirement(domainBaseService.findOneByCodice(playReq, PlayRequirement.class));
rel.setValue(dto.getPlayRequirements().get(playReq));
entity.getPlayRequirements().add(rel);
但是在保存时我得到了这个异常:
13398 02:04:41,679 [main] DEBUG [org.hibernate.SQL:92] -
select
cardplayre0_.PLAY_REQUIREMENT as PLAY_REQUIREMENT2_7_0_,
cardplayre0_.CARD as CARD3_7_0_,
cardplayre0_.VALUE as VALUE1_7_0_
from
CARD_PLAY_REQUIREMENT_REL cardplayre0_
where
cardplayre0_.PLAY_REQUIREMENT=?
and cardplayre0_.CARD=?
13398 02:04:41,679 [main] TRACE [org.hibernate.type.descriptor.sql.BasicBinder:65] - binding parameter [1] as [VARCHAR] - [REQ_MINION_TARGET]
13399 02:04:41,680 [main] TRACE [org.hibernate.type.descriptor.sql.BasicBinder:65] - binding parameter [2] as [VARCHAR] - [AT_024]
13404 02:04:41,685 [main] ERROR [hscli.HearthStoneCardListImporter:56] - A different object with the same identifier value was already associated with the session : [hscli.entities.cards.CardPlayRequirementsRel#CardPlayRequirementsRel(card=null, playRequirement=null, value=0)]; nested exception is javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [hscli.entities.cards.CardPlayRequirementsRel#CardPlayRequirementsRel(card=null, playRequirement=null, value=0)]
org.springframework.dao.DataIntegrityViolationException: A different object with the same identifier value was already associated with the session : [hscli.entities.cards.CardPlayRequirementsRel#CardPlayRequirementsRel(card=null, playRequirement=null, value=0)]; nested exception is javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [hscli.entities.cards.CardPlayRequirementsRel#CardPlayRequirementsRel(card=null, playRequirement=null, value=0)]
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:410)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:246)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:491)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:147)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:133)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
at com.sun.proxy.$Proxy41.save(Unknown Source)
at hscli.services.impl.CardServiceImpl.updateCards(CardServiceImpl.java:52)
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:333)
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:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
at com.sun.proxy.$Proxy44.updateCards(Unknown Source)
at hscli.HearthStoneCardListImporter.updateCards(HearthStoneCardListImporter.java:54)
at hscli.HearthStoneCardListImporter.main(HearthStoneCardListImporter.java:29)
Caused by: javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [hscli.entities.cards.CardPlayRequirementsRel#CardPlayRequirementsRel(card=null, playRequirement=null, value=0)]
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1664)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1602)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1608)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.merge(AbstractEntityManagerImpl.java:1171)
at sun.reflect.GeneratedMethodAccessor36.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:298)
at com.sun.proxy.$Proxy39.merge(Unknown Source)
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:509)
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:540)
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:72)
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.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:503)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:488)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:460)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
... 23 more
【问题讨论】:
您确认CardPlayRequirementsRel
实例的两个PK字段不为空吗?
【参考方案1】:
如果您的实体中有多个使用@Id
注释的列,则必须使用@IdClass
注释或将其替换为嵌入的@EmbeddedId
。请参阅How to map a composite key with Hibernate? 以获得很好的解释,这似乎与休眠无关。
我不完全确定这实际上是您现在看到的问题,因为异常似乎抱怨 id 字段为 null
的多个实例,所以由于您没有指定任何生成器,您应该确保 id 列设置为实际值并且不为空。
【讨论】:
非常感谢。使用@EmbeddedId
注解解决。以上是关于JPA - OneToMany 坚持 - EntityExistsException的主要内容,如果未能解决你的问题,请参考以下文章
JPA,打开 JPA OneToMany - FailedObject