当前位置: 首页 > 知识库问答 >
问题:

删除不使用JPararePository

汪迪
2023-03-14

我有一个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
}

我定义了一个JPararePository接口:

public interface TokenRepository extends JpaRepository<Token, Long> {

    Token findByValue(@Param("value") String value);

}

我有一个与内存数据库(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));
}

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

还有这个问题。通过将其添加到我的TokenRepository接口中,我能够将删除内容持久保存到数据库中:

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

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

共有2个答案

史昊焱
2023-03-14

我也有同样的问题

也许您的UserAccount实体有一个@OneTomany,在某个属性上带有级联。

我刚刚移除了级联,而它在移除时却无法持久存在...

阎功
2023-03-14

这种行为很可能发生在具有双向关系并且在保持父级和子级(附加到当前会话)的同时没有同步双方的情况下。

这很棘手,我将用下面的例子来解释这一点。

@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<Child> children = new HashSet<>(0);

    public void setChildren(Set<Child> 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;
    }
}

让我们写一个测试(一个事务性的,顺便说一句)

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
    }
}

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

这里的删除没有起作用,因为我们没有同步当前会话中持久化的关系的其他部分。如果父级未与当前会话关联,我们的测试将通过,即。

@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!
}

当然,这只是证明了我的观点,并解释了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;
    }
}

显然,这里可以使用@preremove

 类似资料:
  • -我发现shiftRows函数存在一个导致excel中断的错误。https://bz.apache.org/bugzilla/show_bug.cgi?id=57423我不确定这是否已经修复。我已经在bugzilla上添加了一个评论来进一步了解这一点。 如果满足特定的单元格值条件,我正尝试从excel文件中删除行(而不是删除的内容)。 我成功地实现了这一点,但问题是它破坏了我的excel并且当我重

  • 我有一个Laravel项目,我试图用Storage::delete删除图像,但它似乎不起作用。我的图像位于storage\app\public\uploads文件夹中。我可以上传和编辑图像,但删除不起作用。我使用的是Laravel 8。 产品控制器。PHP PHP 网状物PHP 配置/文件系统。PHP

  • 我有一个艰难的时间与dexGuard使它不删除我访问这种方式的资源: int resourceId=getResources()。getIdentifier(“图片”、“数组”、getActivity()。getPackageName()); 当我执行应用程序时,会出现以下异常: JAVAlang.RuntimeException:无法启动android活动。所容纳之物res.Resources$

  • 问题内容: 去除口音的最佳方法是什么? ÈâuÑ”成为“ Eaun” 不 使用 问题答案: 完整的工作代码。 我知道这很长,但是这是Wordpress使用的肯定方式。

  • 我正在我们的项目中实施DynamoDB。我们必须将大型数据字符串放入数据库中,所以我们将数据拆分为小块,并插入多行,其中只有一个属性值发生了更改—这是字符串的一部分。一列(范围键)包含多个零件。插入和选择数据对于大小字符串都非常有效。问题是删除项目。我了解到,当您想要删除某个项时,需要为该项指定主键(哈希键或哈希键和范围键-取决于表)。但如果我想删除对其中一个属性具有特定值的项目,该怎么办?我是否

  • 问题内容: 我想根据引用了第一个表的另一个表上存在的数据从表中删除,但是,当我将其作为SELECT语句运行时,我具有可以正常工作并显示要删除的值的代码,但是当我将其更改为删除它给了我错误,我不明白为什么它们在那里。 错误: 我正在使用postgres 问题答案: 从我看到的地方,您实际上不需要联接即可执行此操作… 它将删除leadcustomer中customerID为:1)与8不同的所有记录2)