REST CRUD application with JPA | A complete example

This tutorial will teach you how to create an example REST (JAX-RS) CRUD application (to be run on the top of WildFly application server) which uses JPA to store your Resources.

Building the CRUD JAX-RS Service

Our JAX-RS Service will use the following HTTP methods to send and retrieve data from/to the server:

  • GET: The GET method is used to retrieve data from the server. This is a read-only method,
  • POST: This  method sends data to the server and creates a new resource. The resource it creates is subordinate to some other parent resource.
  • PUT:  This method is most often used to update an existing resource.
  • DELETE: This method is used to delete a resource specified by its URI.

Firstly, let’s create a Web project using a Maven archetype:

mvn archetype:generate -DgroupId=com.mastertheboss.jaxrs -DartifactId=jaxrs-DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false

Then, we will add the Model object named Customer:

@Entity
@NamedQuery(name = "Customers.findAll",
        query = "SELECT c FROM Customer c ORDER BY c.id")
public class Customer {
    @Id
    @SequenceGenerator(
            name = "customerSequence",
            sequenceName = "customerId_seq",
            allocationSize = 1,
            initialValue = 1)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "customerSequence")
    private Long id;
    @Column(length = 40)
    private String name;
    @Column(length = 40)
    private String surname;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSurname() {
        return surname;
    }

    public void setSurname(String surname) {
        this.surname = surname;
    }

}

The interaction with the Customer object is done through a CustomerRepository Class which follows here:

@ApplicationScoped
public class CustomerRepository {

    @PersistenceContext
    private EntityManager entityManager;

    public List<Customer> findAll() {
        return entityManager.createNamedQuery("Customers.findAll", Customer.class)
                .getResultList();
    }

    public Customer findCustomerById(Long id) {

        Customer customer = entityManager.find(Customer.class, id);

        if (customer == null) {
            throw new WebApplicationException("Customer with id of " + id + " does not exist.", 404);
        }
        return customer;
    }
    @Transactional
    public void updateCustomer(Long id, String name, String surname) {

        Customer customerToUpdate = findCustomerById(id);
        customerToUpdate.setName(name);
        customerToUpdate.setSurname(surname);
    }
    @Transactional
    public void createCustomer(Customer customer) {

        entityManager.persist(customer);

    }
    @Transactional
    public void deleteCustomer(Long customerId) {

        Customer c = findCustomerById(customerId);
        entityManager.remove(c);
    }


}

It is worth notice that, since the Repository is a CDI bean and not an EJB (which is by default a Transactional component), we need to specify the @Transactional annotation on the top of methods that execute a Transaction. Next on the list is the CustomerEndpoint class, which exposes to the Front-End a set of GET/POST/PUT and DELETE HTTP methods:

@Path("customers")
@ApplicationScoped
@Produces("application/json")
@Consumes("application/json")
public class CustomerEndpoint {

    @Inject CustomerRepository customerRepository;

    @GET
    public List<Customer> getAll() {
        return customerRepository.findAll();
    }

    @POST
    public Response create(Customer customer) {
        customerRepository.createCustomer(customer);
        return Response.status(201).build();
    }

    @PUT
    public Response update(Customer customer) {
        customerRepository.updateCustomer(customer);
        return Response.status(204).build();
    }
    @DELETE
    public Response delete(@QueryParam("id") Long customerId) {
        customerRepository.deleteCustomer(customerId);
        return Response.status(204).build();
    }

}

The last Class we need to add is a JaxRsActivator which sets the Web context for the REST Service, activating it as well:

@ApplicationPath("/rest")
public class JaxRsActivator extends Application {
   /* class body intentionally left blank */
}

Our example application will run against the default H2 Database which is provided in WildFly, so our persistence.xml will reference the ExampleDS Datasource for this purpose:

<persistence version="2.1"
             xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="
        http://xmlns.jcp.org/xml/ns/persistence
        http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
    <persistence-unit name="primary">
        <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
        <properties>
            <!-- Properties for Hibernate -->
            <property name="hibernate.hbm2ddl.auto" value="create-drop" />
            <property name="hibernate.show_sql" value="false" />
        </properties>
    </persistence-unit>
</persistence>

Within the folder resources we can add as well an import.sql script to have some Customer objects already available:

INSERT INTO customer (id, name, surname) VALUES ( nextval('customerId_seq'), 'Homer','Simpson');
INSERT INTO customer (id, name, surname) VALUES ( nextval('customerId_seq'), 'Bart','Simpson');

In order to build our projects, we can use the super-lean Jakarta EE dependency, which follows here:

<dependencies>
    <dependency>
      <groupId>jakarta.platform</groupId>
      <artifactId>jakarta.jakartaee-api</artifactId>
      <version>10.0.0</version>
      <scope>provided</scope>
    </dependency>
</dependencies>

Here is the Project tree:

src
└── main
    ├── java
    │   └── com
    │       └── mastertheboss
    │           └── jaxrs
    │               ├── CustomerEndpoint.java
    │               ├── CustomerException.java
    │               ├── Customer.java
    │               ├── CustomerRepository.java
    │               └── JaxRsActivator.java
    ├── resources
    │   ├── import.sql
    │   └── META-INF
    │       └── persistence.xml
    └── webapp
        ├── index.html
        └── WEB-INF
            └── beans.xml

Running the project

Start WildFly application server using any available configuration:

$ ./standalone.sh

Then, as our project includes the WildFly Maven plugin (check the source code below), we can simply run it as follows:

mvn install wildfly:deploy

Let’s test the GET method:

curl -s http://localhost:8080/jaxrs-demo/rest/customers | jq
[
  {
    "id": 1,
    "name": "Homer",
    "surname": "Simpson"
  },
  {
    "id": 2,
    "name": "Bart",
    "surname": "Simpson"
  }
]

Next, let’s add one more Customer:

curl -d '{"name":"John", "surname":"Smith"}' -H "Content-Type: application/json" -X POST http://localhost:8080/jaxrs-demo/rest/customers

To verify that the Customer has been added:

curl -s http://localhost:8080/jaxrs-demo/rest/customers | jq
[
  {
    "id": 1,
    "name": "Homer",
    "surname": "Simpson"
  },
  {
    "id": 2,
    "name": "Bart",
    "surname": "Simpson"
  },
  {
    "id": 3,
    "name": "John",
    "surname": "Smith"
  }
]

Next, we will modify the Customer with id “3”:

curl -d '{"id":"3", "name":"Will", "surname":"Smith"}' -H "Content-Type: application/json" -X PUT http://localhost:8080/jaxrs-demo/rest/customers

Finally, to delete the Customer with id “3”:

curl  -H "Content-Type: application/json" -X DELETE http://localhost:8080/jaxrs-demo/rest/customers?id=3

Conclusion

We have learned how to create a JAX-RS application to be deployed on a Jakarta EE application server.

Source code: https://github.com/fmarchioni/mastertheboss/tree/master/jax-rs/crud