删除不使用JpaRepository

我有一个spring 4应用程序,我试图从我的数据库中删除一个实体的实例。 我有以下实体:

@Entity public class Token implements Serializable { @Id @SequenceGenerator(name = "seqToken", sequenceName = "SEQ_TOKEN", initialValue = 500, allocationSize = 1) @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seqToken") @Column(name = "TOKEN_ID", nullable = false, precision = 19, scale = 0) private Long id; @NotNull @Column(name = "VALUE", unique = true) private String value; @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "USER_ACCOUNT_ID", nullable = false) private UserAccount userAccount; @Temporal(TemporalType.TIMESTAMP) @Column(name = "EXPIRES", length = 11) private Date expires; ... // getters and setters omitted to keep it simple } 

我定义了一个JpaRepository接口:

 public interface TokenRepository extends JpaRepository { Token findByValue(@Param("value") String value); } 

我有一个unit testing设置,与内存数据库(H2)一起使用,我用两个标记预填充数据库:

 @Test public void testDeleteToken() { assertThat(tokenRepository.findAll().size(), is(2)); Token deleted = tokenRepository.findOne(1L); tokenRepository.delete(deleted); tokenRepository.flush(); assertThat(tokenRepository.findAll().size(), is(1)); } 

第一个断言通过,第二个断言失败。 我尝试了另一个更改令牌值并将其保存到数据库的测试,它确实有效,所以我不确定为什么删除不起作用。 它也不会抛出任何exception,只是不会将它持久化到数据库中。 它对我的oracle数据库也不起作用。


编辑

还有这个问题。 通过将此删除添加到我的TokenRepository接口,我能够将删除保留到数据库:

 @Modifying @Query("delete from Token t where t.id = ?1") void delete(Long entityId); 

然而,这不是一个理想的解决方案。 如果没有这个额外的方法,我需要做些什么来让它工作?

我有同样的问题

也许你的UserAccount实体在某些属性上有一个带有Cascade的@OneToMany。

我只是删除了级​​联,而不是删除时可能会持续…

我也刚刚经历过这个。 在我的情况下,我必须使子表具有可空的外键字段,然后通过设置null从关系中删除父,然后调用save和delete并刷新。

在执行此操作之前,我没有在日志中看到删除或任何exception。

您需要在具有多个对象作为属性的类中添加PreRemove函数,例如在与ClassProfile Education.java有关系的Education Class中

 private Set userProfiles = new HashSet(0); @ManyToMany(fetch = FetchType.EAGER, mappedBy = "educations") public Set getUserProfiles() { return this.userProfiles; } @PreRemove private void removeEducationFromUsersProfile() { for (UsersProfile u : usersProfiles) { u.getEducationses().remove(this); } } 

如果您使用较新版本的Spring Data,则可以使用deleteBy语法…因此您可以删除其中一个注释:P

接下来的事情是,这个行为已经由Jira票证管理: https : //jira.spring.io/browse/DATAJPA-727

您的初始值为500.这意味着您的ID以500开头

 @SequenceGenerator(name = "seqToken", sequenceName = "SEQ_TOKEN", initialValue = 500, allocationSize = 1) 

你在这里选择一个id为1的项目

  Token deleted = tokenRepository.findOne(1L); 

因此,请检查您的数据库以澄清这一点

很可能这种行为发生在你有双向关系并且你没有同步双方的同时父和子都是持久的(附加到当前会话)。

这很棘手,我将通过以下示例解释这一点。

 @Entity public class Parent { @Id @GeneratedValue(strategy = IDENTITY) @Column(name = "id", unique = true, nullable = false) private Long id; @OneToMany(cascade = CascadeType.PERSIST, mappedBy = "parent") private Set children = new HashSet<>(0); public void setChildren(Set children) { this.children = children; this.children.forEach(child -> child.setParent(this)); } } @Entity public class Child { @Id @GeneratedValue(strategy = IDENTITY) @Column(name = "id", unique = true, nullable = false) private Long id; @ManyToOne @JoinColumn(name = "parent_id") private Parent parent; public void setParent(Parent parent) { this.parent = parent; } } 

让我们写一个测试(交易一个btw)

 public class ParentTest extends IntegrationTestSpec { @Autowired private ParentRepository parentRepository; @Autowired private ChildRepository childRepository; @Autowired private ParentFixture parentFixture; @Test public void test() { Parent parent = new Parent(); Child child = new Child(); parent.setChildren(Set.of(child)); parentRepository.save(parent); Child fetchedChild = childRepository.findAll().get(0); childRepository.delete(fetchedChild); assertEquals(1, parentRepository.count()); assertEquals(0, childRepository.count()); // FAILS!!! childRepostitory.counts() returns 1 } } 

很简单的测试吧? 我们正在创建父和子,将其保存到数据库,然后从数据库中取出一个子项,将其删除,最后确保一切正常工作。 事实并非如此。

这里的删除不起作用,因为我们没有同步当前会话中持久存在的其他关系部分。 如果Parent与当前会话无关,则我们的测试将通过,即

 @Component public class ParentFixture { ... @Transactional(propagation = Propagation.REQUIRES_NEW) public void thereIsParentWithChildren() { Parent parent = new Parent(); Child child = new Child(); parent.setChildren(Set.of(child)); parentRepository.save(parent); } } 

 @Test public void test() { parentFixture.thereIsParentWithChildren(); // we're saving Child and Parent in seperate transaction Child fetchedChild = childRepository.findAll().get(0); childRepository.delete(fetchedChild); assertEquals(1, parentRepository.count()); assertEquals(0, childRepository.count()); // WORKS! } 

当然,这只能certificate我的观点并解释OP所面临的行为。 正确的方法显然是保持两部分关系同步,这意味着:

 class Parent { ... public void dismissChild(Child child) { this.children.remove(child); } public void dismissChildren() { this.children.forEach(child -> child.dismissParent()); // SYNCHRONIZING THE OTHER SIDE OF RELATIONSHIP this.children.clear(); } } class Child { ... public void dismissParent() { this.parent.dismissChild(this); //SYNCHRONIZING THE OTHER SIDE OF RELATIONSHIP this.parent = null; } }