【问题标题】:@Where annotation with @ManyToOne relationship retrieves soft deleted entity具有@ManyToOne 关系的@Where 注释检索软删除的实体
【发布时间】:2017-11-11 13:34:25
【问题描述】:

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

我在CustomerProject 实体之间有一个双向OneToMany 关系。两者都支持软删除。当我加载Project 时,我可以检索Customer,即使它被软删除。只有当 ManyToOne 被 EAGERly 获取时才会发生。

我写了一个测试用例来说明问题:

Create a Customer and a Project
SOFT Delete only the Customer
Fetch the Project

我除了Project 之外没有Customer,因为它被删除了。

您可能会争辩说测试用例是错误的,因为完整性被破坏了。这在逻辑上是正确的,但在物理上是错误的( FOREIGN KEY 约束仍然有效)。我在一个真实的项目中看到过这种情况。好的实现是删除所有项目,然后删除客户。

测试用例(FAIL):

@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);
    }
}

【问题讨论】:

标签: java hibernate jpa


【解决方案1】:

这可能是一个错误,因此请随意添加带有复制测试用例的 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 列。

【讨论】:

  • 使用Loader 不能解决问题。我将打开一个 Jira 问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多