首页 > 解决方案 > Spring 数据映射无法按预期工作

问题描述

我有一个带有两个外键和多对一关联(到表检查点设置)的表选项db schema

从 JPA 方面,我有 2 个双向关联:一侧是Cascade.ALLFetchType.EAGER,另一侧是Cascade.REMOVE。这是相应的 JPA 映射:

@Entity
@Table(name = "checkpoint")
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Checkpoint {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private long id;

  private long location;

  @OneToMany(mappedBy = "checkpoint",
        cascade = CascadeType.ALL,
        orphanRemoval = true,
        fetch = FetchType.EAGER)
  private List<RewardOption> rewardOptions = new ArrayList<>();
}

@Entity
@Table(name = "setting")
@AllArgsConstructor
@NoArgsConstructor
@Data
public class RewardSetting {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private long id;

  @Column(name = "base_line")
  private long baseLine;

  @OneToMany(mappedBy = "rewardSetting",
        cascade = CascadeType.REMOVE,
        orphanRemoval = true)
  private List<RewardOption> rewardOptions = new ArrayList<>();
}

@Entity
@Table(name = "option")
@AllArgsConstructor
@NoArgsConstructor
@Data
public class RewardOption {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private long id;

  private long weight;

  @ManyToOne
  @JoinColumn(name = "setting_id")
  private RewardSetting rewardSetting;

  @ManyToOne
  @JoinColumn(name = "checkpoint_id")
  private Checkpoint checkpoint;
} 

此外,我正在使用 Spring Data 进行实体操作。对我来说,这似乎是一个有效的例子,但在我表演时的实践中:

...
rewardSettingRepository.delete(rewardSetting);

或者:

...
rewardSettingRepository.delete(id);

我让 JPA 尝试在选项表中的相应行之前从设置表中删除一行。

有谁知道为什么它会这样?为什么这种映射会导致这种行为?如何以正确的方式做到这一点?

完整的堆栈跟踪是:

org.springframework.dao.DataIntegrityViolationException:无法执行语句;SQL [不适用];约束[空];嵌套异常是 org.hibernate.exception.ConstraintViolationException: could not execute statement at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:278) at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible (HibernateJpaDialect.java:244) 在 org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:765) 在 org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:521) 在 org.springframework。 org.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:734)

引起:org.hibernate.exception.ConstraintViolationException:无法在 org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java) 处执行语句 org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:59) :42) 在 org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:95) 在 org.hibernate. engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:207) 在 org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:45) 在 org.hibernate.persister.entity.AbstractEntityPersister。在 org.hibernate 删除(AbstractEntityPersister.java:3261)。persister.entity.AbstractEntityPersister.delete(AbstractEntityPersister.java:3498) 在 org.hibernate.action.internal.EntityDeleteAction.execute(EntityDeleteAction.java:98) 在 org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java: 582) 在 org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:456) 在 org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:337) 在 org.hibernate.event.internal.DefaultFlushEventListener org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:465) 的 org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1282) 的 .onFlush(DefaultFlushEventListener.java:39)。 org.hibernate.internal 的 SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:2963)。在 org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback( JdbcResourceLocalTransactionCoordinatorImpl.java:147) 在 org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$100(JdbcResourceLocalTransactionCoordinatorImpl.java:38) 在 org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceDriverControlImpl$TransactionCoordinatorImpl .commit(JdbcResourceLocalTransactionCoordinatorImpl.java:231) 在 org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:65) 在 org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:61) at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:517) ... 18 更多

引起:java.sql.SQLIntegrityConstraintViolationException:无法删除或更新父行:外键约束失败(demo. option,CONSTRAINT FKdhs5wopt13o6b9gl4wydr0l9oFOREIGN KEY(setting_id)REFERENCES settingid)) 在 com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:95) 在 com.mysql.cj.SQLError.createSQLException(SQLError.java:115) 在 com.mysql.cj .jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122) 在 com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:960) 在 com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java :1116) 在 com.mysql.cj.jdbc.ClientPreparedStatement.executeLargeUpdate(ClientPreparedStatement.java:1396) 在 com.mysql.cj.jdbc 的 com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1066)。 Sun.reflect.NativeMethodAccessorImpl 处的 ClientPreparedStatement.executeUpdate(ClientPreparedStatement.java:1051)。在 sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 在 sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 在 java.lang.reflect.Method.invoke(Method.java: 43) 的 invoke0(Native Method) 498) at org.apache.tomcat.jdbc.pool.StatementFacade$StatementProxy.invoke(StatementFacade.java:114) at com.sun.proxy.$Proxy89.executeUpdate(Unknown Source) at org.hibernate.engine.jdbc.internal .ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:204) ... 37 更多StatementFacade$StatementProxy.invoke(StatementFacade.java:114) at com.sun.proxy.$Proxy89.executeUpdate(Unknown Source) at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:204) .. . 37 更多StatementFacade$StatementProxy.invoke(StatementFacade.java:114) at com.sun.proxy.$Proxy89.executeUpdate(Unknown Source) at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:204) .. . 37 更多

另外,在玩了之后,我发现了一些奇怪的东西,我无法解释:

  1. 在RewardOption类中为Checkpoint添加@ManyToOne(fetch = FetchType.LAZY)可以解决问题,但是 fetch 类型如何影响级联?
  2. 在类Checkpoint中将cascade = CascadeType.ALL更改为cascade = CascadeType.REMOVE可以解决问题,但我需要能够保存级联...

如果有人可以对这些“修复”有所了解,那就太好了。

这是玩的代码

标签: javajpaspring-data-jpa

解决方案


要使其工作,您需要同步双向关联的两端

这意味着,在调用 delete 之前,您需要确保将父实体与所有子实体解除关联。


推荐阅读