java - Spring 数据映射无法按预期工作
问题描述
我有一个带有两个外键和多对一关联(到表检查点和设置)的表选项:db schema。
从 JPA 方面,我有 2 个双向关联:一侧是Cascade.ALL和FetchType.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
,CONSTRAINTFKdhs5wopt13o6b9gl4wydr0l9o
FOREIGN KEY(setting_id
)REFERENCESsetting
(id
)) 在 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 更多
另外,在玩了之后,我发现了一些奇怪的东西,我无法解释:
- 在RewardOption类中为Checkpoint添加@ManyToOne(fetch = FetchType.LAZY)可以解决问题,但是 fetch 类型如何影响级联?
- 在类Checkpoint中将cascade = CascadeType.ALL更改为cascade = CascadeType.REMOVE可以解决问题,但我需要能够保存级联...
如果有人可以对这些“修复”有所了解,那就太好了。
解决方案
要使其工作,您需要同步双向关联的两端。
这意味着,在调用 delete 之前,您需要确保将父实体与所有子实体解除关联。
推荐阅读
- php - Laravel 5.8 通知独立目标 [Illuminate\Contracts\Notifications\Dispatcher] 不可实例化
- java - 在运行时修改 jarfile
- javascript - 在 React Native 中为 SVG 设置高度时宽度不会改变
- sql - 返回过去 12 个月内没有活动但当月有活动的所有行?
- javascript - 打字稿:将类型定义为具有部分存在对象的类型的联合
- firebase - 如何根据 Flutter 中的订阅向用户发送 Firebase 推送通知?
- swiftui - 如何通过另一个可观察的 0bject 观察已发布的属性 - Swift Combine
- javascript - 如何将对象时间戳转换为 Firebase 时间戳?
- css - 允许自定义 CSS 的危险
- javascript - 添加数据表分页