A CRUD Interface for Your Spring Data Repository Using Across
Across Framework tutorial: part 1
In this first part, we’ll show you how to generate a CRUD administration interface to manage a Spring data repository in less than thirty minutes, using the Across EntityModule. Across is a free, open source Java framework built on top of Spring.
This is the first part of a three part Across tutorial based on our talk at Devoxx Belgium 2017. In the second part, we’ll focus on customizing the CRUD interface; in the third part we’ll take a look at WebCmsModule, which provides basic webcms functionality.
You can find part 2 right here; the last part of the tutorial is right here. Follow us on Facebook or Twitter so you don’t miss them.
Introduction
In this post, we’ll show how easy it is to set up a decent CRUD backend in no time to manage entities of a domain model with the use of EntityModule. In addition, we’ll also add an additional entity to show the ease of expanding the domain of a project.
Building the application
Just like Spring has a project initializer to quickly start developing an application, there’s also one for Across. The Across Initializr provides several options to quick-start your development, as well as some documented example code.
Setup
We’ll start off our development with the following configuration:
Four modules
AcrossWebModule
Activates Spring MVC Configuration
AdminWebModule
Provides a secure section for an administration interface
AcrossHibernateJpaModule (including sample entities)
Provides a shared entity manager
Provides our blogpost and author entities
EntityModule
Provides us with a CRUD administration interface
A default message source
The Across Initializr will generate a Maven project based on the chosen settings, which we’ll be able to start up immediately. Don’t forget to activate the ‘dev’ profile in your run configuration, as this will enable devtools and speed up the development process. During the following sections, we’ll primarily focus on the added value that EntityModule provides.
When we start the application, we’ll see that all our modules are being bootstrapped, and once the startup has completed, we’ll be able to navigate to our website, which by default can be found on http://localhost:8080. Since we’re going to develop a CRUD application, we’ll be spending our time in the secured section that AdminWebModule provides, which is by default configured to be on /admin. When we log in with the oh-so-secure credentials (username and password: admin), we’ll receive a CRUD administration interface for our sample entities out of the box. The default credentials are provided by the AdminWebUserConfiguration, which sets up a Spring Security configuration for that user. Should you include UserModule, these credentials will correspond to an actual user.
The first thing we notice is that there are two sections available: Entity management and Developer tools. The former contains the various modules and the entities they provide, the latter provides us with information about the registered entities and is available because development mode is activated.
The sample entities provide a default domain model, which is based on JpaRepositories and Persistable entities as we are used to from Spring Framework, including some test data for us to experiment with.
What happened thus far? We have a domain model that was picked up and some test data was created through the use of installers. Installers are simply classes that will be executed during the startup phase of the application. EntityModule (in combination with HibernateJpaModule and AdminWebModule) then provided us with a useable administration interface, giving us the possibility to manage these entities.
We haven’t written any code yet, and we already got a decent backend with some entities, cool! However, how much work would it be to extend the domain model and add another entity?
Adding an entity
Let’s assume that in our application, we’ll be paying the authors for the blogposts they write, so how about we add an Invoice entity?
Let’s create a package invoice under domain.blog where we put our Invoice entity. Why would we put it here? Because for EntityModule to be able to detect our entities, we first need to tell it where to look. This happens in the EntityScanConfiguration class, which will scan for entities inside of the domain.blog package.
The invoice entity will be implemented using the Persistable interface, is linked to an author and a blogpost, has an amount and a status, and can optionally contain a note. We’ve also added a getLabel() method, which will be used by entity module to provide us with a more readable label for invoices. Aside from that, we’ve added some annotations for validation, found in javax.persistance and javax.validation.constraints, and @Data from Lombok which implements various methods (setters, getters, toString ...).
The invoice entity:
@Data
@Entity
public class Invoice implements Persistable<Long> {
@Id
@GeneratedValue
private Long id;
@ManyToOne
@NotNull
private Author author;
@ManyToOne
@NotNull
private BlogPost blogPost;
@Temporal( TemporalType.DATE )
private Date receivedDate;
@NotNull
@Min( value = 0 )
private BigDecimal amount;
@NotNull
private Status status = Status.RECEIVED;
enum Status {
RECEIVED,
APPROVED,
PAID
}
private String note;
@Override
public boolean isNew() {
return id == null;
}
public String getLabel() {
return "Invoice-" + getId();
}
}
Besides the entity, we’ll also need a JpaRepository so that we can actually persist changes to the database. We’ll create an InvoiceRepository, which simply extends JpaRepository and JpaSpecificationExecutor.
public interface InvoiceRepository extends JpaSpecificationExecutor<Invoice>, JpaRepository<Invoice, Long> {
}
Normally we’d also manually map the entity to a database table, but since this is just a demo application, we’ll let Hibernate do the heavy lifting. We’ll simply add the following property to our yaml configuration:
acrossHibernate:
hibernate-properties:
hibernate.hbm2ddl.auto: update
Once we reload the application, we’ll notice that we have an additional entity in our administration UI. Awesome! All we did was add an entity and a repository, and now we can manage our invoices from the backend.
We can also see that there are several annotations present on the properties of our Invoice. Ranging from persistence to validation annotations, EntityModule will do its best to find them and take them into account when generating the administration interface for the entity. When we try to create a new invoice, we’ll get the following:
img
It’s clear that the validation annotations have been detected and that the form is rendered accordingly, as well as that our entity is validated when we try to persist it.
Our NotNull annotations were picked up: our fields annotated with @NotNull now have an asterisk (*) next to them and have to be filled in.
Status was prefilled with ‘Received’. Here, we also immediately notice that once a @NotNull property was selected, the empty option is no longer available.
The @Min annotation was picked up, we can not enter an amount less than 0.
@Temporal(TemporalType.DATE) tells JPA to only persist the date and not the timestamp, so our interface no longer shows a timestamp either.
We can now fill in the form correctly and create an invoice for ‘The Across Team’ author.
Let’s take a closer look at the relationships between our entities. Our Invoice entity refers to both an Author and a Blogpost. If we go take a look at those entities, we’ll notice there’s an additional tab generated on their update form!
It’s clear that the validation annotations have been detected and that the form is rendered accordingly, as well as that our entity is validated when we try to persist it.
Our NotNull annotations were picked up: our fields annotated with @NotNull now have an asterisk (*) next to them and have to be filled in.
Status was prefilled with ‘Received’. Here, we also immediately notice that once a @NotNull property was selected, the empty option is no longer available.
The @Min annotation was picked up, we can not enter an amount less than 0.
@Temporal(TemporalType.DATE) tells JPA to only persist the date and not the timestamp, so our interface no longer shows a timestamp either.
We can now fill in the form correctly and create an invoice for ‘The Across Team’ author.
Let’s take a closer look at the relationships between our entities. Our Invoice entity refers to both an Author and a Blogpost. If we go take a look at those entities, we’ll notice there’s an additional tab generated on their update form!
EntityModule will try to work out the relations defined by the entities and will list the ones related to the entity we’re currently viewing.
Conclusion
What have we done thus far? We generated a simple application using Across Initializr. This included several modules, which generated a decent administration interface based on a simple domain model. Then, we proceeded to add an additional entity named Invoice, along with a repository for persistence, as well as annotations to provide some validation.
In this part we’ve focused on the added value that EntityModule brings to an application - starting from a domain model - as well as how it integrates with existing Spring features like JpaRepository and Persistable. In the next part, we’ll be focusing on how we can further customize this interface.