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.

Minimal implementation of SettableIdAuditableEntity
@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.

Example entity with relationships
@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.

Example entity with Auditable properties added
@Entity
@Table(name = "customers")
public class Customer extends SettableIdAuditableEntity<Customer>
{
	@Id
	private Long id;

	@Column(name = "customerName")
	private String customerName;

}
Equivalent AuditableSchemaInstaller to add the auditing columns in database
@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

String

The name of the principal that was creating the entity

created_date

Date

The time at which the object was created

modified_by

String

The name of the principal that was modifying the entity

modified_date

Date

The time at which the object was modified