我在产品和功能之间存在双向关系,用户将添加具有某些功能的产品,并且需要将它们保存在同一笔交易中。在更新时,我需要删除所有旧功能并保存新添加的功能。
@Entity @Table(name = "product") public class Product{ @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "mySeq") @SequenceGenerator(name = "mySeq") private Long id; @OneToMany(cascade = CascadeType.ALL) @JoinColumn(name = "product_id") private List<Feature> features = new ArrayList<Feature>(); ... public Product addFeatures(Feature feature) { this.features.add(feature); feature.setProduct(this); return this; } } @Entity @Table(name = "feature") public class Feature{ @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "mySeq") @SequenceGenerator(name = "mySeq") private Long id; @ManyToOne @JoinColumn(name="product_id") @JsonIgnoreProperties("features") private Product product; ... }
下面是服务
@Service @Transactional public class ProductService { ... public ProductDTO addNew(ProductDTO productDTO){ Product product = productMapper.toEntity(productDTO); product.getFeatures().clear(); for(FeatureDTO featureDTO : productDTO.getFeatures()){ Feature feature = featureMapper.toEntity(featureDTO); product.addFeatures(feature); } entityManager.persist(product); ... } public ProductDTO update(ProductDTO productDTO){ Product product = productMapper.toEntity(productDTO); //Need to delete all old features and save the newly added featureRepository.deleteByProductId(productDTO.getId()); product.getFeatures().clear(); for(FeatureDTO featureDTO : productDTO.getFeatures()){ Feature feature = featureMapper.toEntity(featureDTO); product.addFeatures(feature); } featureRepository.save(product); ... } }
但是当我尝试添加新产品或更新它时,偶尔出现以下错误(有时工作正常!)
org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:257)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:223)
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:540)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:746)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:714)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:532)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:304)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
...
Caused by: org.hibernate.exception.ConstraintViolationException: could not execute statement
at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:112)
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:111)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:97)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:178)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3032)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3547)
at org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:89)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:600)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:474)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:337)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39)
at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1437)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:494)
at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3245)
at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2451)
at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:473)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:156)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$100(JdbcResourceLocalTransactionCoordinatorImpl.java:38)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:231)
at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:68)
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:536)
... 120 common frames omitted
Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: Violation of PRIMARY KEY constraint 'PK_FEATURE'. Cannot insert duplicate key in object 'dbo.feature'. The duplicate key value is (1451).
at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDatabaseError(SQLServerException.java:254)
at com.microsoft.sqlserver.jdbc.SQLServerStatement.getNextResult(SQLServerStatement.java:1608)
at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement.doExecutePreparedStatement(SQLServerPreparedStatement.java:578)
at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement$PrepStmtExecCmd.doExecute(SQLServerPreparedStatement.java:508)
at com.microsoft.sqlserver.jdbc.TDSCommand.execute(IOBuffer.java:7240)
我在产品和功能之间存在双向关系,用户将添加具有某些功能的产品,并且需要将它们保存在同一笔交易中。更新时,我需要删除所有旧的...
这里可能存在多个问题。
[如果您希望能够通过父实体集合来保留子实体,则需要设置orphanRemoval = true,而不是先调用clear()
,您可以先执行setFeatures(null)
,然后再执行setFeatures(productDTO.getFeatures())