See how the Jakarta Data specification simplifies persistence.
Many applications, especially in the enterprise domain, persist or access data in some form. Relational databases are still by far the most used persistence mechanism even though they are being challenged by technologies such as NoSQL databases. This article explores some concepts for data access and looks at how the new Jakarta Data specification makes data access simpler than ever for application developers.
Data persistence
I begin this discussion by reviewing persistence concepts. If you are familiar with these concepts, feel free to skip this part and dive right into the next section, which contains sample code.
CRUD. The most common operations used in applications that persist data are create, read, update, and delete (CRUD) operations. CRUD operations are commonly associated with relational databases but can be applied to any persistence mechanisms. Writing code for these operations is usually repetitive work consisting mostly of boilerplate code.
ORM. Object-relational mapping (ORM), as the name suggests, takes care of mapping objects in an object-oriented language to data in a relational database. There are numerous ORM frameworks available to help developers with this task. Jakarta Persistence, previously referred to as JPA, is a specification that standardizes persistence management and object-relational mapping for Java applications.
The Repository pattern. There are several patterns and strategies—such as Data Access Object (DAO), Repository, Active Record, and others—that are commonly used for structuring the code associated with CRUD operations. In this article, I use the Repository pattern.
The intention of the Repository pattern (which you can read about in Martin Fowler’s Patterns of Enterprise Application Architecture) is to keep the specifics regarding persistence outside the application’s domain model. The repositories are classes that encapsulate the data access logic, thus decoupling the persistence mechanism from the domain model.
The Repository pattern has become popular and is widely used due to technologies such as Spring Data; it is no secret that Spring Data is the inspiration for Jakarta Data.
Jakarta Data. Jakarta Data is a new specification proposed to be included in Jakarta EE 11, which is planned to be released in the first half of 2024. By implementing the Repository pattern, Jakarta Data simplifies data access and reduces the amount of boilerplate code needed. Developers only need to define an interface representing the repository and an entity representing the database table. The implementation of Jakarta Data will supply the actual implementation of the repository.
A simple example with MySQL
This very simple example shows how Jakarta Data simplifies persistence for developers by eliminating the need for boilerplate code. The technologies used in this example are
You will also need to have Apache Maven and a JDK installed on your computer. This code has been verified on Java 20, but other versions may work as well.
The example in this code uses Open Liberty as the runtime. However, when there is another implementation available, you should be able to replace Open Liberty with the other implementation without changing any code.
Step 1. Verify that Apache Maven and a JDK are installed. You should see something such as the following:
$ mvn --version
Apache Maven 3.8.2 (ea98e05a04480131370aa0c110b8c54cf726c06f)
Maven home: /home/ivar/.sdkman/candidates/maven/current
Java version: 20.0.1, vendor: Eclipse Adoptium, runtime: /home/ivar/.sdkman/candidates/java/20.0.1-tem
Default locale: en_US, platform encoding: UTF-8
Step 2. Install and set up MySQL after downloading it directly from https://www.mysql.com/ or by using your favorite package manager. Here is an example of how to do it if you are using Ubuntu.
$ sudo apt-get install mysql-server
Step 3. Log in to MySQL Shell.
sudo mysql -u root
Step 4. Create a database and user.
mysql> create database dukes_data;
mysql> use dukes_data;
mysql> create user 'duke'@'localhost' identified by 'duke';
mysql> grant all privileges on dukes_data to 'duke'@'localhost';
Step 5. Get the code from my GitHub repository, and then compile and run it with Maven.
$ mvn liberty:run
The application is now ready to try out.
Step 6. Three endpoints are available.
- List all greetings (GET)
- Search for a greeting by greeter name (GET)
- Add a greeting (POST)
Here’s how they work.
To list all greetings, use the following:
http://localhost:9080/dukes-data/api/greetings/
The following is the expected response because there’s no data yet:
[]
To search for Duke’s greeting, use the following:
http://localhost:9080/dukes-data/api/greetings/duke
Similarly, because there’s no data yet, here’s the response.
duke not found
To add Duke’s greeting, use this.
$ echo -n '{"message":"Hello from Duke", "name":"Duke"}' | http post :9080/dukes-data/api/greetings
To list all greetings again, use this.
http://localhost:9080/dukes-data/api/greetings/
Here’s the expected response.
[
{
id: 1,
message: "Hello from Duke",
name: "Duke"
}
]
Finally, to search for Duke’s greeting again, use the following:
http://localhost:9080/dukes-data/api/greetings/duke
The expected response is
Hello from Duke
The example code
The application consists of four classes: GreetingApplication, GreetingResource, Greeting, and GreetingRepository.
GreetingApplication configures the Jakarta REST application. In this case, the only thing needed is the application path.
package dukes.data;
import jakarta.ws.rs.ApplicationPath;
import jakarta.ws.rs.core.Application;
@ApplicationPath("/api")
public class GreetingApplication extends Application {
}
GreetingResource exposes the three API methods for retrieving all greetings, retrieving one greeting, and adding a greeting.
package dukes.data;
import jakarta.inject.Inject;
import jakarta.validation.Valid;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.util.List;
@Path("/greetings")
public class GreetingResource {
@Inject
private GreetingRepository greetingRepository;
@GET
@Path("/{name}")
@Produces(MediaType.TEXT_PLAIN)
public String findOne(@PathParam("name") String name) {
return greetingRepository.findByNameIgnoreCase(name)
.map(Greeting::getMessage)
.orElse(name + " not found");
}
@GET
@Produces(MediaType.APPLICATION_JSON)
public List<Greeting> findAll() {
return greetingRepository.findAll()
.toList();
}
@POST()
@Consumes(MediaType.APPLICATION_JSON)
public Response addGreeting(Greeting greeting) {
Greeting saved = greetingRepository.save(greeting);
return Response.ok("Created greeting: " + greeting.getId()).build();
}
}
The Greeting class defines the entity that is being persisted in the database. It is a Jakarta Persistence entity with three fields. The @Entity annotation identifies it as a Jakarta Persistence entity, and the @Id and @GeneratedValue annotations define the primary key as well as how it should be generated. Other than that, it is just a plain old Java object.
package dukes.data;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
@Entity
public class Greeting {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String message;
// constructor/getters/setters
}
The GreetingRepository is where things get interesting. It is a simple interface that extends CrudRepository and is annotated with @Repository. This information is enough for the Jakarta Data implementation to generate methods for all the CRUD operations as well as a couple of other convenience methods, such as count, existsById, and various finders.
package dukes.data;
import jakarta.data.repository.CrudRepository;
import jakarta.data.repository.Repository;
import java.util.Optional;
@Repository
public interface GreetingRepository extends CrudRepository<Greeting, Long> {
Optional<Greeting> findByNameIgnoreCase(String name);
}
The only method defined by the developer is findByNameIgnoreCase. As the name implies, this method searches the database for rows with the provided name. Jakarta Data will generate a method that does exactly that.
Source: oracle.com
0 comments:
Post a Comment