Helper classes and utilities
In addition to the bean infrastructure, this module also provides a set of base classes and general helpers to build a persistent domain model.
AcrossSequenceGenerator
This is a TableGenerator
that works cross-database and can be used to work with preset, negative id values.
Uses the ACROSS_SEQUENCES
table created by the AcrossSequencesInstaller
from Across core package.
@Id
@GeneratedValue(generator = "seq_mm_myentity_id")
@GenericGenerator(
name = "seq_mm_myentity_id",
strategy = AcrossSequenceGenerator.STRATEGY,
parameters = {
@org.hibernate.annotations.Parameter(name = "sequenceName", value = "seq_mm_myentity_id"),
@org.hibernate.annotations.Parameter(name = "allocationSize", value = "10")
}
)
private Long id;
Entity base classes
SettableIdBasedEntity
and SettableIdAuditableEntity
classes are base classes for @Entity
allowing an id to be set before persisting, using the newEntityId
property.
The SettableIdAuditableEntity
extension will automatically have auditing information updated when SpringSecurityModule
is active and entity intercepting is enabled (default).
The SettableIdBasedEntity
also implements common interfaces like IdBasedEntity
, Persistable
and EntityWithDto
.
These are used by many other standard modules to automatically hookup functionality.
The base implementation is sufficient for many common use cases.
@Entity
@Table(name = "acl_entity")
public class MyEntity extends SettableIdAuditableEntity<MyEntity>
{
@Id
@GeneratedValue(generator = "seq_mm_myentity.id")
@GenericGenerator(
name = "seq_mm_myentity",
strategy = AcrossSequenceGenerator.STRATEGY,
parameters = {
@org.hibernate.annotations.Parameter(name = "sequenceName", value = "seq_mm_myentity"),
@org.hibernate.annotations.Parameter(name = "allocationSize", value = "1")
}
)
private Long id;
@Override
public Long getId() {
return id;
}
@Override
public void setId( Long id ) {
this.id = id;
}
}
DTO conversion
SettableIdBasedEntity
supports converting an entity to a hibernate detached variant through EntityWithDto#toDto
using DtoUtils
.
The base implementation is a new instance of the object, to which all properties are copied over.
@Entity
@Table(name = "acl_entity")
public class MyEntity extends SettableIdAuditableEntity<MyEntity>
{
@Id
@GeneratedValue(generator = "seq_mm_myentity.id")
@GenericGenerator(
name = "seq_mm_myentity",
strategy = AcrossSequenceGenerator.STRATEGY,
parameters = {
@org.hibernate.annotations.Parameter(name = "sequenceName", value = "seq_mm_myentity"),
@org.hibernate.annotations.Parameter(name = "allocationSize", value = "1")
}
)
private Long id;
@Column(name = "name")
private String name;
@JoinColumn(name = "my-other-entity")
private MyOtherEntity myOtherEntity;
// ...
}
When performing MyEntity#toDto
a new instance of MyEntity
is created, and all of its properties are copied over.
This means that when, for example, the name of the entity is modified, the changes will never be flushed to the database, unless an actual save is called with this new instance.
Depending on how modifications to the related entities are applied, such as myOtherEntity
in the example, unintended changes can still be automatically flushed to the database, as it is copied over from the original instance.
A use case where this could happen is when a property of myOtherEntity
is updated whilst the instance is still hibernate attached, and an application validation error occurs.
Functionally you will have blocked the update, but as the hibernate attached entity has been modified, the changes will still be flushed to the database at a certain point.
If there is no additional validation database wise, you might end up with an incorrect state.
To solve this, we’ve added a more advanced dto conversion using Dozer. When using the advanced dto conversion, a deep clone will be made of the entity, which means that related entities will also be hibernate detached. When a property is configured as lazy loaded, the clone will instead insert a proxy, which will open a session and load the actual property when it is first called, and consequently create a deep clone of said property.
Activating the advanced dto conversion, can be done via the configuration properties.
Spring Data repository interfaces
CommonJpaRepository
and IdBasedEntityJpaRepository
are helper repository interfaces to help reduce boilerplate code.
CommonJpaRepository
extends the basic JpaRepository
with the JpaSpecificationExecutor
interface.
IdBasedEntityJpaRepository
is the extension tailored to SettableIdBasedEntity
implementations that use a Long
as id type.
Using these interfaces will ensure repository integration with other modules (for example EntityModule
).
Auditable entities
Any entity that implements the Auditable<String>
interface will get auditing information set on every save or update.
AuditableEntity
is a @MappedSuperClass
that implements the Auditable<String>
interface.
It adds auditing related columns: creation and last modification information (timestamp and principal).
AuditableSchemaInstaller
The AuditableSchemaInstaller
is a base installer class that can easily be used to add the relevant auditing columns to multiple tables.
@Entity
@Table(name = "customers")
public class Customer extends SettableIdAuditableEntity<Customer>
{
@Id
private Long id;
@Column(name = "customerName")
private String customerName;
}
@Order(2)
@Installer(description = "Adds auditable columns to specific tables", version = 1)
public class MyAuditableInstaller extends AuditableSchemaInstaller
{
@Override
protected Collection<String> getTableNames() {
return Collections.singleton( "customers" );
}
}
Note the use of @Order(2)
which might be necessary to ensure that your AuditableSchemaInstaller
is run after running your base schema installer.
After starting your application, Across will automatically add the following columns to your entities:
Column | Type | Description |
---|---|---|
created_by |
|
The name of the principal that was creating the entity |
created_date |
|
The time at which the object was created |
modified_by |
|
The name of the principal that was modifying the entity |
modified_date |
|
The time at which the object was modified |