1. Introduction
The GORM Logical Delete plugin provides support for utilizing logical deletes for GORM managed entities in a Grails 3 app.
A "logical" delete (sometimes referred to as a "soft" delete) is a delete that doesn’t actually delete the relevant data but instead marks the data as deleted. Marking the data as deleted has the benefit of excluding the data from queries by default while still maintaining the ability to retrieve the data when/if necessary. Essentially the plugin offers a mechanism to "hide" domain objects from retrieval. This is useful for retaining data without having it clutter the current set of active data.
2. Installation
To add the GORM Logical Delete plugin to an application add the following dependency to the dependencies block of your build.gradle:
compile "org.grails.plugins:gorm-logical-delete:2.0.0.BUILD-SNAPSHOT"
Snapshots are now published automatically to Artifactory OSS on every successful build. You can use them by defining this
Maven repository inside the repositories block in your build.gradle
:
maven { url "https://oss.jfrog.org/artifactory/oss-snapshot-local" }
3. LogicalDelete Trait
The plugin provides the gorm.logical.delete.LogicalDelete
trait which can applied to any domain class to indicate that the domain class should
participate in logical deletes. The trait adds a boolean persistent property named deleted
to the domain class.
When this property has a value of true
, it indicates that the record has been logically deleted and as such
will be excluded from query results by default.
import gorm.logical.delete.LogicalDelete
class Person implements LogicalDelete<Person> {
String userName
static mapping = {
// the deleted property may be configured
// like any other persistent property...
deleted column:"delFlag"
}
}
4. Deleting Objects
Deleting a domain object that implements the gorm.logical.delete.LogicalDelete trait
is simple as calling delete()
. This will mark the deleted
field to true
and all queries for that domain object will
hide the item from its result set.
Person p = new Person(userName: "Nirav").save(flush: true)
p.delete(flush:true)
In order to reverse the deleted
field use unDelete()
. . .
p.unDelete(flush: true)
If you would like to physically delete the record from persistence, use the attribute hard: true
:
p.delete(hard: true)
5. Queries
Most queries will by default automatically exclude logically deleted records from their results.
A dynamic finder query authored like this…
results = Person.findAll {
userName == 'Robert'
}
Is executed as if it had been written like this…
results = Person.findAll {
userName == 'Robert'
deleted == false
}
For cases where you would like logically deleted records to be included in query results, queries may be
wrapped in a call to withDeleted
as shown below. Any query can be wrapped with the withDeleted
closure.
results = Person.withDeleted { Person.findAll() }
The plugin supports a variety of query types in addition to dynamic finders, see examples below:
5.1. Criteria Query
def criteria = Person.createCriteria()
def results = criteria {
or {
eq("userName", "Ben")
eq("userName", "Nirav")
}
}
5.2. Detached Criteria Query
DetachedCriteria<Person> query = Person.where {
userName == "Ben" || userName == "Nirav"
}
def results = query.list()
5.3. GORM methods
The standard GormEntity<D>
methods of get
, load
, proxy
, read
are also supported for logical delete.
Hibernate Criteria and HQL queries are NOT supported by this plugin. |
5.4. GORM Dataservices
GORM Dataservices are supported for logical delete.
6. WithDeleted Annotation
The @WithDeleted
annotation allows you to mark a method on a class so that queries within the method will retrieve logically
deleted entities. This is an alternate solution to using the withDeleted
closure. Ths is useful in conjuction for Gorm Data Services, or
any classes that act as DAOs.
@Service(Person)
interface PersonService {
List<Person> listPeople()
@WithDeleted
List<Person> listPeopleWithDeleted()
void delete(Serializable id)
}
7. Implementation Details
This is a brief overview on the implementation of the gorm-logical-delete plugin.
7.1. LogicalDelete Trait
The gorm.logical.delete.LogicalDelete trait establishes the deleted
field
and also overrides methods that will implement the query and delete functionality.
7.2. PreQueryListener
The gorm.logical.delete.PreQueryListener listens for a query event and appropriately adds to the existing query in order to hide deleted items.
7.3. Spock Tests
The spock tests contained in the plugin are a good resource to reference examples and usages of the plugin API.
8. API Documentation
API documentation may be found here.