1. Overview
1.概述
While working with databases in our applications, we usually have to deal with deleting records that are no longer useful. However, due to business or regulatory requirements, such as data recovery, audit tracing, or referential integrity purposes, we may need to hide these records instead of deleting them.
在应用程序中使用数据库时,我们通常需要删除不再有用的记录。但是,出于业务或监管要求,如数据恢复、审计跟踪或参照完整性目的,我们可能需要隐藏这些记录,而不是删除它们。
In this tutorial, we’ll take a look at the @SoftDelete annotation from Hibernate and learn how to implement it.
在本教程中,我们将了解 Hibernate 的 @SoftDelete 注解,并学习如何实现它。
2. Understanding the @SoftDelete Annotation
2.了解@SoftDelete注释
The @SoftDelete annotation provides a convenient mechanism to mark any record as active or deleted. It has three different configuration parts:
@SoftDelete注解提供了一种方便的机制,可将任何记录标记为活动或已删除。它有三个不同的配置部分:
- The strategy configures whether to track the rows that are active or which are deleted. We can configure it by setting the strategy as either ACTIVE or DELETED
- The indicator column identifies which column will be used to track the rows. If there are no specified columns, the strategy uses the default columns (active or deleted).
- The converter defines how the indicator column is set in the database. The domain type is a boolean value indicating whether the record is active or deleted. However, by implementing AttributeConverter, we can set the relational type to any type as defined by the converter. The available converters are NumericBooleanConverter, YesNoConverter, and TrueFalseConverter.
3. Implementing @SoftDelete
3.实施 @SoftDelete
Let’s look at a few examples of how we can use @SoftDelete with different configurations.
让我们看几个例子,了解如何在不同配置下使用 @SoftDelete 。
3.1. Models
3.1 模式
Let’s define an entity class SoftDeletePerson which we annotate with @SoftDelete. We don’t provide any additional configuration, and the annotation takes all the default values, such as a strategy of DELETED, a deleted indicator column, and storage as a boolean type.
让我们定义一个实体类 SoftDeletePerson,并用 @SoftDelete 对其进行注解。我们不提供任何额外的配置,注解采用了所有默认值,例如 DELETED 策略、deleted 指示列和 boolean 类型的存储。
The @SoftDelete annotation supports the @ElementCollection, which we’ve defined with a configured strategy as ACTIVE, a default indicator column, and storage as ‘Y’ or ‘N’ using the YesNoConverter:
@SoftDelete 注解支持 @ElementCollection,我们使用 YesNoConverter 将配置策略定义为 ACTIVE、默认指标列,并将存储定义为 ‘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. Data Setup
3.2.数据设置
Let’s create a couple of database entries for the SoftDeletePerson entity and see how Hibernate saves them in the database:
让我们为 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("id1@dummy.com");
emailIds.add("id2@dummy.com");
person1.setEmailIds(emailIds);
SoftDeletePerson person2 = new SoftDeletePerson();
person2.setName("Person2");
List<String> emailIdsPerson2 = new ArrayList<>();
emailIdsPerson2.add("person2Id1@dummy.com");
emailIdsPerson2.add("person2Id2@dummy.com");
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);
}
In the test case above, we’ve persisted two SoftDeletePerson entities and printed the same to visualize what is persisted in the database. The output below shows that Hibernate saves the SoftDeletePerson with the deleted column set to false. Additionally, the collection emailIds has the active column set with the value ‘Y’:
在上面的测试用例中,我们持久化了两个 SoftDeletePerson 实体,并打印了相同的内容,以便直观地显示数据库中持久化的内容。下面的输出显示,Hibernate 在保存 SoftDeletePerson 时,将 deleted 列设置为 false。此外,集合 emailIds 的 active 列的值设置为 ‘Y’:</em
3.3. Testing
3.3.测试
In the previous step, we persisted a few rows in the database. Now, let’s see how @SoftDelete handles the deletion of records:
在上一步中,我们在数据库中持久化了几条记录。现在,让我们看看 @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));
}
First, we fetched the existing rows from the database. Next, we deleted one of the entities while for the other, we updated emailIds.
首先,我们从数据库中获取现有记录。接着,我们删除了其中一个实体,同时更新了另一个实体的 emailIds. 。
Then, when we delete one of the SoftDeletePerson entities, Hibernate sets deleted=true. Similarly, when we remove one of the email ids, Hibernate sets the previous rows to active=’N’, and inserts a new row with active=’Y’.
然后,当我们删除其中一个 SoftDeletePerson 实体时,Hibernate 会设置 deleted=true 。同样,当我们删除其中一个电子邮件 ID 时,Hibernate 会将之前的记录设置为 active=’N’,并插入一条 active=’Y’ 的新记录。
Finally, when we fetch the active and deleted rows, we can see the expected result:
最后,当我们获取活动行和已删除行时,我们可以看到预期的结果:
4. Conclusion
4.结论
In this article, we explored the implementation of the @SoftDelete annotation in Hibernate. The default configuration is with the DELETED strategy and stored as a boolean value in the database column deleted.
在本文中,我们探讨了 Hibernate 中 @SoftDelete 注解的实现。默认配置使用 DELETED 策略,并以 boolean 值的形式存储在数据库列 deleted 中。
We also took a look at how the @ElementCollection is supported by this annotation. Finally, we verified the results with the test cases for the different configurations.
我们还了解了该注解是如何支持 @ElementCollection 的。最后,我们使用不同配置的测试用例验证了结果。
As always, the source code for all the examples can be found over on GitHub.