@Where注释与@ManyToOne关系检索软删除实体

时间:2017-11-11 13:34:25

标签: java hibernate jpa

供参考https://hibernate.atlassian.net/browse/HHH-12104

OneToManyCustomer个实体之间存在双向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);
    }
}

1 个答案:

答案 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