供参考:https://hibernate.atlassian.net/browse/HHH-12104
OneToMany
和Customer
个实体之间存在双向Project
关系。两者都支持SOFT删除。当我加载Project
时,即使已删除SOFT,我也可以检索Customer
。只有在ManyToOne
被EAGERly获取时才会发生。
我写了一个显示问题的测试用例:
Create a Customer and a Project
SOFT Delete only the Customer
Fetch the Project
我除了Project
之外没有Customer
,因为它已被删除。
您可能会认为测试用例是错误的,因为完整性已被破坏。这在逻辑上是真实的但不是物理的(FOREIGN KEY约束仍然有效)。我在一个真实的项目中看到了这种情况。好的实现是删除所有项目,然后删除客户。
测试用例(失败):
@RunWith(SpringRunner.class)
@DataJpaTest
public class SoftDeleteTest {
@Autowired
private EntityManager entityManager;
@Autowired
private ProjectRepository projectRepository;
@Autowired
private CustomerRepository customerRepository;
private void newSession() {
entityManager.flush();
entityManager.clear();
}
@Test
public void testIntegrityConstraintManyToOne() {
Project project = new Project();
project.setName("Framework");
Customer customer = new Customer();
customer.setName("StackOverflow");
customer.addProject(project);
customerRepository.save(customer);
projectRepository.save(project);
Long customerId = customer.getId();
Long projectId = project.getId();
newSession();
customerRepository.delete(customerId);
newSession();
Project orphanProject = projectRepository.findOne(projectId);
try {
Customer deletedCustomer = orphanProject.getCustomer();
deletedCustomer.toString();
fail("EntityNotFoundException expected");
} catch (EntityNotFoundException enfe) {
}
}
Customer
实体(AbstractEntity
有一个Boolean deleted
字段):
@Entity
@Table(name = "CUSTOMER")
@SQLDelete(sql = "UPDATE customer SET deleted = TRUE WHERE id = ?")
@Where(clause = "deleted = false")
public class Customer extends AbstractEntity {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
private String name;
@OneToMany(mappedBy = "customer")
private Set<Project> projects = new HashSet<>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Project> getProjects() {
return projects;
}
public void setProjects(Set<Project> projects) {
this.projects = projects;
}
public void addProject(Project project) {
projects.add(project);
project.setCustomer(this);
}
public void removeProject(Project project) {
projects.remove(project);
project.setCustomer(null);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Customer customer = (Customer) o;
return Objects.equals(id, customer.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
Project
实体:
@Entity
@Table(name = "PROJECT")
@SQLDelete(sql = "UPDATE project SET deleted = true WHERE id = ?")
@Where(clause = "deleted = false")
public class Project extends AbstractEntity {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
private String name;
@ManyToOne(fetch = FetchType.EAGER)
private Customer customer;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Project)) return false;
Project project = (Project) o;
return Objects.equals(id, project.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
答案 0 :(得分:0)
这可能是一个错误,所以随意添加一个Jira问题与复制测试用例。
要解决此问题,请查看this article:
@Entity(name = "PostDetails")
@Table(name = "post_details")
@SQLDelete(sql =
"UPDATE post_details " +
"SET deleted = true " +
"WHERE id = ?")
@Loader(namedQuery = "findPostDetailsById")
@NamedQuery(name = "findPostDetailsById", query =
"SELECT pd " +
"FROM PostDetails pd " +
"WHERE " +
" pd.id = ?1 AND " +
" pd.deleted = false")
@Where(clause = "deleted = false")
public class PostDetails
extends BaseEntity {
...
}
因此,请使用包含@Loaded
列的专用deleted
。