1. 概述
在应用程序中处理数据库时,我们通常需要处理不再需要的记录。然而,由于业务或监管要求,如数据恢复、审计追踪或参照完整性目的,我们可能需要隐藏这些记录,而不是删除它们。
在这个教程中,我们将研究Hibernate中的@SoftDelete
注解,并学习如何实现它。
2. @SoftDelete
注解的理解
@SoftDelete
注解提供了一种方便的机制,用于标记任何记录为活动或已删除。它有三个不同的配置部分:
- 策略配置了是跟踪活动行还是已删除行。我们可以通过设置
strategy
为ACTIVE
或DELETED
来配置。 - 指示列标识将用于跟踪行的列。如果没有指定列,则策略会使用默认列(
active
或deleted
)。 - 转换器定义了如何在数据库中设置指示列。域类型是一个
boolean
值,表示记录是否活跃或已删除。然而,通过实现AttributeConverter
,我们可以将关系类型设置为转换器定义的任何类型。可用的转换器包括NumericBooleanConverter
、YesNoConverter
和TrueFalseConverter
。
3. 实现@SoftDelete
让我们看看如何在不同配置下使用@SoftDelete
的一些示例。
3.1. 模型
首先定义一个实体类SoftDeletePerson
,并为其添加@SoftDelete
注解。我们不提供额外配置,注解使用所有默认值,如策略为DELETED
,deleted
指示列,以及存储为boolean
类型。
@SoftDelete
注解支持@ElementCollection
,我们在其中配置了策略为ACTIVE
,默认指示列,并使用YesNoConverter
将存储类型设置为'Y'
或'N'
:
@SoftDelete
public class SoftDeletePerson {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
private String name;
@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(name = "Emails", joinColumns = @JoinColumn(name = "id"))
@Column(name = "emailId")
@SoftDelete(strategy = SoftDeleteType.ACTIVE,converter = YesNoConverter.class)
private List<String> emailIds;
// standard getters and setters
}
3.2. 数据设置
现在创建一些SoftDeletePerson
实体的数据库条目,看看Hibernate如何将它们保存到数据库中:
@BeforeEach
public void setup() {
session = sessionFactory.openSession();
session.beginTransaction();
SoftDeletePerson person1 = new SoftDeletePerson();
person1.setName("Person1");
List<String> emailIds = new ArrayList<>();
emailIds.add("[email protected]");
emailIds.add("[email protected]");
person1.setEmailIds(emailIds);
SoftDeletePerson person2 = new SoftDeletePerson();
person2.setName("Person2");
List<String> emailIdsPerson2 = new ArrayList<>();
emailIdsPerson2.add("[email protected]");
emailIdsPerson2.add("[email protected]");
person2.setEmailIds(emailIdsPerson2);
session.save(person1);
session.save(person2);
session.getTransaction()
.commit();
assertNotNull(person1.getName());
assertNotNull(person2.getName());
System.out.println(person1);
System.out.println(person2);
}
在上述测试用例中,我们持久化了两个SoftDeletePerson
实体,并打印出来以可视化数据库中保存的内容。输出显示Hibernate将SoftDeletePerson
保存为deleted
列设置为false
。此外,emailIds
集合的active
列设置为'Y':
3.3. 测试
在上一步中,我们在数据库中持久化了一些行。现在,让我们看看@SoftDelete
如何处理记录的删除:
@Test
void whenDeletingUsingSoftDelete_ThenEntityAndCollectionAreDeleted() {
session.beginTransaction();
person1 = session.createQuery("from SoftDeletePerson where name='Person1'", SoftDeletePerson.class)
.getSingleResult();
person2 = session.createQuery("from SoftDeletePerson where name='Person2'", SoftDeletePerson.class)
.getSingleResult();
assertNotNull(person1);
assertNotNull(person2);
session.delete(person2);
List<String> emailIds = person1.getEmailIds();
emailIds.remove(0);
person1.setEmailIds(emailIds);
session.save(person1);
session.getTransaction()
.commit();
List<SoftDeletePerson> activeRows = session.createQuery("from SoftDeletePerson")
.list();
List<SoftDeletePerson> deletedRows = session.createNamedQuery("getDeletedPerson", SoftDeletePerson.class)
.getResultList();
session.close();
assertNotNull(person1.getName());
System.out.println("-------------Active Rows-----------");
activeRows.forEach(row -> System.out.println(row));
System.out.println("-------------Deleted Rows-----------");
deletedRows.forEach(row -> System.out.println(row));
}
首先,我们从数据库中获取现有行。接下来,我们删除了一个实体,而另一个实体更新了emailIds
。
然后,当我们删除一个SoftDeletePerson
实体时,Hibernate设置deleted=true
。类似地,当我们移除其中一个电子邮件ID时,Hibernate将之前的行设置为active='N'
,并在数据库中插入一个新的行,其active='Y'
。
最后,当我们检索活动和已删除的行时,可以看到预期的结果:
4. 总结
在这篇文章中,我们探讨了Hibernate中@SoftDelete
注解的实现。默认配置是使用DELETED
策略,并在数据库的deleted
列中以boolean
值存储。
我们还查看了此注解如何支持@ElementCollection
。最后,我们通过测试用例验证了不同配置下的结果。
如往常一样,所有示例的源代码可以在GitHub上的此处找到。