This annotation facilitates auto generation of boilerplate
Trackable
lifecycle components
based on new persistence domain. In many cases, default CRUD behavior is all that's required for
a new persistence domain class, and overall implementation simplicity will benefit from not
having to implement multiple support components with common behavior. This annotations helps with
that by engaging auto-generation of
JpaTrackableRepository
,
RsqlCrudEntityService
, and
ProjectionReferencedApi
components that comprise the
main plumbing for interacting with a domain class via the admin. Note, metadata configuration and
possibly permission configuration (i.e. auth service) are still required to complete a full admin
experience.
This process benefits from full autoconfiguration behavior. This means that you may explicitly
declare one or all of these components (presumably with custom behavior), and the system will
back off and not auto-generate those one or more components.
Note that auto-generation of projections for a persistence domain class is outside the scope of
influence of this annotation. Auto-generation of projections is handled automatically by the
system and does not require an annotation to drive that behavior. However, aspects of how
projections are generated can be customized, or suppressed. See
ProjectionManager
,
AutoConfigureProjectionMapping
, and
ExplicitProjectionFieldConfiguration
for more
information on auto-generated projections.
The basic use case would resemble the following in code when introducing a new persistence domain
class as an addition to the Broadleaf catalog service.
The configuration:
@Configuration
@EnableJpaTrackableFlow(entityClass = JpaWidget.class, routeKey = CATALOG_ROUTE_KEY, permissionRoots = "CATALOG", rootPath = "/widgets", projectionName = "Widget")
@JpaEntityScan(basePackages = "com.mycompany.catalog.domain", routePackage = CATALOG_ROUTE_PACKAGE)
public class MyConfig {
...
}
And, the persistence domain class:
package com.mycompany.catalog.domain;
...
@Entity
@Table(name = "WIDGET")
@Data
@EqualsAndHashCode(exclude = "id")
@EntityListeners(TrackingListener.class)
public class JpaWidget implements Serializable, CatalogTrackable<CatalogJpaTracking>
{
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(generator = "blcid")
@GenericGenerator(name = "blcid", strategy = "blcid")
@Type(type = "com.broadleafcommerce.data.tracking.jpa.hibernate.ULidType")
@Column(name = "ID", nullable = false)
private String id;
@Column(name = "CONTEXT_ID")
@Convert(converter = UlidConverter.class)
@FilterAndSortAlias("id")
private String contextId;
@Embedded
private CatalogJpaTracking tracking;
@Column(name = "NAME")
private String name;
@Column(name = "DESCRIPTION", length = JpaConstants.MEDIUM_TEXT_LENGTH)
private String description;
}
Note, in the case above, the system would skip auto-generation of the projection class if the
"Widget" class implemented
BusinessTypeAware
and
ModelMapperMappable
, which
allows for more custom mapping from the persistence domain.
Custom component registration is straightforward and allows for further refinement of behavior at
one or more points in the CRUD lifecycle. Take, for example, the registration of a new
BaseRsqlCrudEntityService
component. The configuration:
@Configuration
@EnableJpaTrackableFlow(entityClass = JpaWidget.class, routeKey = CATALOG_ROUTE_KEY, permissionRoots = "CATALOG", rootPath = "/widgets", projectionName = "Widget")
@JpaEntityScan(basePackages = "com.mycompany.catalog.domain", routePackage = CATALOG_ROUTE_PACKAGE)
public class MyConfig {
...
@Bean
public MyProjectionService projectionService(TrackableRepository<JpaWidget> repository,
RsqlCrudEntityHelper helper) {
return new MyProjectionService(repository, helper);
}
}
And, the custom service class:
public class MyProjectionService extends BaseRsqlCrudEntityService<Projection<JpaWidget>>
{
public MyProjectionService(TrackableRepository<JpaWidget> repository,RsqlCrudEntityHelper helper)
{
super(repository, helper);
}
@Override
public Projection<Widget> update(String id, Projection<JpaWidget> projection, ContextInfo context)
{
projection.expose().setName("override");
return super.update(id, businessInstance, context);
}
}
The key revelation here is that the auto-generated components are typed against your
auto-generated projection (or your explicit projection if you're implementing BusinessTypeAware
and ModelMapperMappable). This takes the form of
Projection<T>
where 'T' is the
persistence domain class. If you need to retrieve or manipulate the values in the projection, you
may call
Projection.expose()
and exercise the API of 'T' against the Projection instance.