我有一个 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 接口(interface):
public interface TokenRepository extends JpaRepository<Token, Long> {
Token findByValue(@Param("value") String value);
}
我有一个与内存数据库 (H2) 一起使用的单元测试设置,我正在使用两个 token 预填充数据库:
@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));
}
第一个断言通过,第二个失败。我尝试了另一个更改 token 值并将其保存到数据库的测试,它确实有效,所以我不确定为什么删除不起作用。它也不会抛出任何异常,只是不会将其持久化到数据库中。它也不适用于我的 oracle 数据库。
编辑
还是有这个问题。通过将其添加到我的 TokenRepository 接口(interface),我能够将删除持久化到数据库中:
@Modifying
@Query("delete from Token t where t.id = ?1")
void delete(Long entityId);
但是,这不是一个理想的解决方案。如果没有这种额外的方法,我需要做什么才能让它工作有什么想法吗?
最佳答案
当你有双向关系并且你没有同步双方同时 parent 和 child 都持久存在(附加到当前 session )时,很可能会发生这种行为。
这很棘手,我将通过以下示例进行解释。
@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
}
}
很简单的测试吧?我们正在创建父项和子项,将其保存到数据库,然后从数据库中获取一个子项,将其删除,最后确保一切都按预期工作。而事实并非如此。
这里的删除不起作用,因为我们没有同步在当前 session 中持久的关系的另一部分。如果 Parent 没有与当前 session 关联,我们的测试将通过,即
@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
可以在这里使用。
关于java - 删除不使用 JpaRepository,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22688402/