Model Mapping
Once you have added ScrudBeans in your project, you can have it process your entity models to
create SCRUD components and services everytime your build runs.
At the moment, only JPA entities are supported. Future versions may support additional Spring Data sub-projects.
Mapping Prerequisites
Model driven services are enabled per entity provided two conditions are met:
- It must be annotated with
@ScrudBean
. - It must have a non-primitive id.
- Java entities must either implement
org.springframework.data.domain.Persistable
or subclass one of the mapped superclasses available, see Java Base Models - Kotlin entities must implement
com.github.manosbatsis.scrudbeans.api.domain.KPersistable
, an custom equivalent of Spring'sPersistable
. For Kotlin users of Hibernate JPA, extendingcom.github.manosbatsis.scrudbeans.model.AbstractHibernateKPersistable
just works, even fordata
classes.
Mapping Examples
As an example, consider an Order
entity in
Java or
Kotlin.
Java example:
@Entity
@Table(name = "product_orders")
@ScrudBean
public class Order extends AbstractSystemUuidPersistableModel {
// ScrudBeans will automatically pick-up Bean Validation and Column annotations
// to validate e.g. for both not-null and unique values
@NotNull
@Column(nullable = false, unique = true)
private String email;
// other members...
}
Kotlin example:
@Entity
@Table(name = "product_orders")
@ScrudBean
data class Order(
@field:Id
@field:GeneratedValue(generator = "system-uuid")
@field:GenericGenerator(name = "system-uuid", strategy = "uuid2")
var id: String? = null,
@field:NotNull
@field:Column(nullable = false)
@field:Schema(title = "The client's email", required = true)
var email: String? = null,
// other constructor params...
) : AbstractHibernateKPersistable<String>()
That is enough for ScrudBeans to create the appropriate components and expose RESTful services for SCRUD, meaning Search, Create, Update and Delete operations for the entity type. This is explained further in the next chapter.
Relevant Annotations
ScrudBean
The @ScrudBean
annotation marks the entity model for annotation processing and, optionally, provides relevant metadata.
The annotation processors have a reasonable strategy for creating those metadata themselves when missing. The following shorthand
for example:
@ScrudBean
public class DiscountCode {//...
Is equivalent to:
@ScrudBean(
pathFragment = "discountCodes",
apiName = "Discount Codes",
apiDescription = "Search or manage Discount Code entries")
public class DiscountCode { //...
About the annotation members:
pathFragment
is the path fragment appended to the API base path (by defaultapi/rest/
), thus forming the baseRequestMapping
path for this entity's REST controller.apiName
andapiDescription
is used to annotate generated controllers withio.swagger.v3.oas.annotations.tags.Tag
.apiDescription
is equivalent toio.swagger.annotations.Api#description
.dtoTypes
an array of DTO classes to generate mappers for.dtoTypeNames
an array of DTO (canonical) class names to generate mappers for.
Validation
javax.persistence.Column
for not-null and unique valuesjavax.validation.constraints
annotations- Any custom Java Bean Validation annotation. See
Unique
for an example, or create your own.
Documentation
Annotating your classes properly per SpringDoc and Swagger 3
will increase the quality of documentation, e.g. for Swagger UI.
Java Base Models
Java only: As an alternative to implementing PersistableModel
, you may instead extend the
@MappedSuperclass
annotated type within the com.github.manosbatsis.scrudbeans.jpa.model
package
that matches the target @Id
type, see bellow.
Hibernate
Extending AbstractHibernateModel
with your Java entity is the simplest way
for Hibernate users to implement Spring's Persistable
.
@Entity
@Table(name = "product_orders")
@ScrudBean
public class Order extends AbstractHibernateModel<String> {
@Id
@GeneratedValue(generator = "system-uuid")
@GenericGenerator(name = "system-uuid", strategy = "uuid2")
private String id;
//...
}
UUID
To use automatically generated UUIDs as a primary key you can extend AbstractSystemUuidPersistableModel
:
@Entity
@Table(name = "products")
@ScrudBean
public class Product extends AbstractSystemUuidPersistableModel {/* ...*/ }
Auto Increment
You can auto-increment long IDs (i.e. javax.persistence.GenerationType.AUTO
) you can extend
AbstractAutoGeneratedLongIdPersistableModel
:
@Entity
@Table(name = "discount_code")
@ScrudBean
public class DiscountCode extends AbstractAutoGeneratedLongIdPersistableModel {/* ...*/ }
Assigned IDs
To use assigned (i.e. manual) IDs, you can extend AbstractAssignedIdPersistableModel
. It's generic,
so you can specify the ID type required. e.g. for Long
:
@Entity
@ScrudBean
@Table(name = "slot")
public class Slot extends AbstractAssignedIdPersistableModel<Long> {/* ...*/ }
You can use @AttributeOverrides
to apply more specific restrictions, for example a string with a
maximum length of two characters:
@Entity
@ScrudBean
@Table(name = "country")
@AttributeOverrides({
@AttributeOverride(name = "id", column = @Column(unique = true, nullable = false, length = 2)),
})
public class Country extends AbstractAssignedIdPersistableModel<String> {/* ...*/ }
Composite IDs
Some times you need to use composite IDs in your models, e.g. when working with a legacy database design. This introduces some complexity in a number of areas where the ID must be handled in a regular RESTful way, including request mapping bindings of path or query parameters.
This requires using an @EmbeddedId
with your entity, providing your (@Embeddable) implementation of an
EmbeddableCompositeIdentifier`.
To help making this easier out of the boxfor Java, ScrudBeans provides a number of embeddable ID
implementations you can extend from, depending on the number of columns/fields needed for your composite ID,
namely AbstractEmbeddableManyToManyIdentifier
, AbstractEmbeddableTripleIdentifier
,
AbstractEmbeddableQuadrupleIdentifier
and AbstractEmbeddableQuintupleIdentifier
.
You can create custom composite IDs by implementing
EmbeddableCompositeIdentifier
.
For an example, consider a case where you need to assign additional attributes to a many-to-many
relationship between models, like a Friendship
between two User
s, along with the requirement
of using their two user keys as a composite ID. For cases like these, you can easily create a mapping
that "just works" in two steps.
First, begin by extending AbstractEmbeddableManyToManyIdentifier
to create a new @Embeddable
ID type:
@Embeddable
public class FriendshipIdentifier
extends AbstractEmbeddableManyToManyIdentifier<User, String, User, String>
implements Serializable {
@Override
public User buildLeft(Serializable left) {
User u = new User();
u.setId(left.toString());
return u;
}
@Override
public User buildRight(Serializable right) {
User u = new User();
u.setId(right.toString());
return u;
}
}
Depending on the identifier superclass, you'll need to implement a number of buildX
methods as above.
Then, use the new @Embeddable
identifier as the entity ID type of a PersistableModel
:
@ScrudBean
@Entity
@Table(name = "friendship")
public class Friendship implements PersistableModel<FriendshipIdentifier> {
@NotNull
@ApiModelProperty(required = true)
@EmbeddedId
private FriendshipIdentifier id;
// Other members...
}