Spring Data to the rescue!
Creating repositories to use the Java persistence API can be a complicated process. It takes not only a lot of time, but also lots of boilerplate code.
So when I met Spring Data for the first time, it felt like a blessing from heaven.
We read in Spring Data home page project:"Spring Data's mission is to provide a familiar and consistent, Spring-based programming model for data access."
Spring Data is an umbrella project that contains many subprojects that are specific to a given database (ie. Spring Data JPA, Spring Data MongoDB, Spring Data REST, etc.). It makes it easy to use data access technologies, relational and non-relational databases, map-reduce frameworks and cloud-based data services.
In this example I will use Spring Data JPA and will show you how easy is to map your relational objects to your DB using Spring Data's powerful repository and custom object-mapping abstractions.
We will be creating an application with Spring Boot, H2 as the in-memory database and Maven.
If this is your first time with Spring Boot this post will explain to you how to create your application.
You will need JDK 1.8+ and Maven 3.0+.
This is the POM file you will need to build and deploy the app.
The first thing worth mentioning is the
As I mentioned before, we will be using H2 as the in-memory database, meaning that we can only access the data while the application is running and it will disappear at shutdown.
Here is the project structure:
These are our domain classes. First the Musician class, annotated as a JPA entity.
The @Data annotation is from Lombok, a library that can help you save lots of boilerplate code, you can check the details in this post.
Our entity has an @Entity annotation, which means it is a JPA entity. Since we don't have a @Table annotation, it is assumed this entity will be mapped to a table on your database named Musician .
The @Id annotation will help JPA recognize it as this object's id. We also tell JPA this id will be generated automatically with the @GeneratedValue annotation.
We also have the firstName and lastName attributes: because they don't have any annotation on them, it is assumed they'll be mapped to columns with the same name.
The last property is another entity class: Instrument . With the @ManyToOne annotation we let JPA knows the many-to-one association between the two entities, meaning many musicians can play the same instrument. The cascade type option added is just for the sake of the example, it will let us propagate the persisting of our main entity to the related entities.
Here is the Instrument entity with the two attributes I added for the example: id and type .
It gives us the following methods just by extending it:
Now let's try our repositories capabilities. We will use, for the sake of the example, a controller, and we will create methods to save the information, to query the data and delete an entity.
@Autowired annotation.
The save method receives three String parameters - musician last name, first name and the instrument name, to persist the entities in the database, using the save method from the repository.
Can you spot how easy is to create these entities ?
We also have the findAll method, where we call the method of the same name in the repository of the correspondant entity. You don't need to write any SQL or HQL (Hibernate query language).
The guys from Pivotal (the company that brings us Spring) are determined to improve our (developer) lives.
The Spring Data repository infrastructure comes with a query builder mechanism built in and is very useful for building constraining queries over entities of the repository. This mechanism takes off the prefixes find…By, read…By, query…By , count…By and get…By and starts parsing the rest of it.
For example the findByLastName method will look for the lastName attribute of the Musician entity using the parameter to search in the database.
You can also concatenate entity properties: in our findByInstrumentType method the repository mechanism will look for the instrument property of the Musician entity, and since this attribute maps to another entity, it will look for the attribute type of that Instrument , again, using the parameter as an input.
At a very basic level you can define conditions on entity properties and concatenate them with And and Or.
Enough chit-chat ! Let's try our work of art.
You can now start your Spring Boot application, running this maven command on the command line, in the directory where your pom file resides:
Then we can call the first endpoint declared in our controller to persist our entities:
http://localhost:8080/musicianApp/save?firstName=David&lastName=Lebon&instrument=Guitar
http://localhost:8080/musicianApp/save?firstName=Charly&lastName=Garcia&instrument=Piano
http://localhost:8080/musicianApp/save?firstName=Indio&lastName=Solari&instrument=Voice
http://localhost:8080/musicianApp/save?firstName=Billy-Joe&lastName=Armstrong&instrument=Vocals
Let's see what's in the database:
http://localhost:8080/musicianApp/findAll
Now, let's look for one id:
http://localhost:8080/musicianApp/findOne/2
And if we want to find the musicians that plays the guitar
http://localhost:8080/musicianApp/findByInstrumentType/Guitar
That's it for today! As I always say this is just the tip of the iceberg, there's a whole lot more functionality on the Spring Data reference documentation.
Enjoy!
So when I met Spring Data for the first time, it felt like a blessing from heaven.
We read in Spring Data home page project:"Spring Data's mission is to provide a familiar and consistent, Spring-based programming model for data access."
Spring Data is an umbrella project that contains many subprojects that are specific to a given database (ie. Spring Data JPA, Spring Data MongoDB, Spring Data REST, etc.). It makes it easy to use data access technologies, relational and non-relational databases, map-reduce frameworks and cloud-based data services.
In this example I will use Spring Data JPA and will show you how easy is to map your relational objects to your DB using Spring Data's powerful repository and custom object-mapping abstractions.
We will be creating an application with Spring Boot, H2 as the in-memory database and Maven.
If this is your first time with Spring Boot this post will explain to you how to create your application.
You will need JDK 1.8+ and Maven 3.0+.
This is the POM file you will need to build and deploy the app.
The first thing worth mentioning is the
spring-boot-starter-data-jpa
dependency: it provides a quick way to get started and it includes Hibernate 5.0 by default (one of the most popular JPA implementations).As I mentioned before, we will be using H2 as the in-memory database, meaning that we can only access the data while the application is running and it will disappear at shutdown.
Here is the project structure:
└── src └── main └── java └── com.example -+ Application.java └── controller -+ MusicianController └── entity -+ Instrument -+ Musician └── repository -+ InstrumentRepository -+ MusicianRepositorySpring recommends to locate your main application class in a root package above all other classes (in our case is Application ).
These are our domain classes. First the Musician class, annotated as a JPA entity.
package com.example.entity;
import lombok.Data;
import javax.persistence.*;
@Data
@Entity
public class Musician {
protected Musician(){}
public Musician(String firstName, String lastName){
this.firstName = firstName;
this.lastName = lastName;
}
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
@ManyToOne(cascade = CascadeType.ALL)
private Instrument instrument;
@Override
public String toString(){
return "Id: " + id + ", firstName: " + firstName + ", lastName: " + lastName;
}
}
The Musician class has 3 attributes: the id , the firstName and the lastName . There are also 2 constructors in the class: the first one, the default one, is needed by JPA. The second will be used to persist the data in the database.The @Data annotation is from Lombok, a library that can help you save lots of boilerplate code, you can check the details in this post.
Our entity has an @Entity annotation, which means it is a JPA entity. Since we don't have a @Table annotation, it is assumed this entity will be mapped to a table on your database named Musician .
The @Id annotation will help JPA recognize it as this object's id. We also tell JPA this id will be generated automatically with the @GeneratedValue annotation.
We also have the firstName and lastName attributes: because they don't have any annotation on them, it is assumed they'll be mapped to columns with the same name.
The last property is another entity class: Instrument . With the @ManyToOne annotation we let JPA knows the many-to-one association between the two entities, meaning many musicians can play the same instrument. The cascade type option added is just for the sake of the example, it will let us propagate the persisting of our main entity to the related entities.
Here is the Instrument entity with the two attributes I added for the example: id and type .
package com.example.entity;
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Data
@Entity
public class Instrument {
protected Instrument(){}
public Instrument(String type){
this.type = type;
}
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String type;
}
The central interface in Spring Data abstraction is Repository . It takes the domain class as well as the id type of the domain class as type arguments. This interface acts primarily as a marker interface to capture the types to work with and to help you discover interfaces that extend this one. The CrudRepository provides sophisticated CRUD functionality for the entity class that is being managed. (Spring Data reference documentation).It gives us the following methods just by extending it:
- save (S entity); - it saves the given entity.
- T findOne (ID primaryKey); - returns the entity identified by the given id.
- Iterable<T> findAll (); - returns all entities.
- void delete (T entity); - deletes the given entity.
- boolean exists (ID primaryKey); - tells us if the entity with the given id exists.
Spring Data also provides persistence technology-specific abstractions like JpaRepository or MongoRepository. Those interfaces extend the CrudRepository and expose the capabilities of the underlying persistence technology.
Having this in mind we will use the repository functionality (including the underlying queries on the datastore) by creating an interface that extends the JpaRepository .
Here are our two repositories for our domain classes: MusicianRepository & InstrumentRepository .
Having this in mind we will use the repository functionality (including the underlying queries on the datastore) by creating an interface that extends the JpaRepository .
Here are our two repositories for our domain classes: MusicianRepository & InstrumentRepository .
package com.example.repository;
import com.example.entity.Musician;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface MusicianRepository extends JpaRepository<Musician, Long> {
public Musician findByLastName(String lastName);
public List findByInstrumentType(String type);
public List findAllByOrderByLastName();
}
package com.example.repository;
import com.example.entity.Instrument;
import org.springframework.data.jpa.repository.JpaRepository;
public interface InstrumentRepository extends JpaRepository<Instrument, Long> {
}
Now let's try our repositories capabilities. We will use, for the sake of the example, a controller, and we will create methods to save the information, to query the data and delete an entity.
@RestController
@RequestMapping("/musicianApp")
public class MusicianController {
private final static Logger log = LoggerFactory.getLogger(MusicianController.class);
@Autowired
MusicianRepository musicianRepository;
@Autowired
InstrumentRepository instrumentRepository;
@RequestMapping("/save")
public void save(@RequestParam(value="firstName") String firstName,
@RequestParam(value="lastName") String lastName,
@RequestParam(value = "instrument") String instrument){
Musician musician = new Musician(firstName, lastName);
musician.setInstrument(new Instrument(instrument));
musicianRepository.save(musician);
}
@RequestMapping("/findAll")
public List findAll(){
List musicians = new ArrayList();
musicianRepository.findAll().forEach(musicians::add);
return musicians;
}
@RequestMapping("/findByLastName/{lastName}")
public Musician findByLastName(@PathVariable("lastName") String lastname){
return musicianRepository.findByLastName(lastname);
}
@RequestMapping("/findOne/{id}")
public Musician findOne(@PathVariable("id") String id)
{
return musicianRepository.findOne(Long.parseLong(id));
}
@RequestMapping("/findByInstrumentType/{type}")
public List findByInstrumentType(@PathVariable("type") String type){
return musicianRepository.findByInstrumentType(type);
}
@RequestMapping("/findAllOrderByLastName")
public List findAllOrderByLastName(){
List musicians = new ArrayList();
musicianRepository.findAllByOrderByLastName().forEach(musicians::add);
return musicians;
}
@RequestMapping("/delete/{id}")
public void deleteMusician(@PathVariable("id") Long id){
musicianRepository.delete(id);
}
}
In order to use the repositories we need to inject them in the controller, we do this with the@Autowired annotation.
The save method receives three String parameters - musician last name, first name and the instrument name, to persist the entities in the database, using the save method from the repository.
Can you spot how easy is to create these entities ?
We also have the findAll method, where we call the method of the same name in the repository of the correspondant entity. You don't need to write any SQL or HQL (Hibernate query language).
The guys from Pivotal (the company that brings us Spring) are determined to improve our (developer) lives.
The Spring Data repository infrastructure comes with a query builder mechanism built in and is very useful for building constraining queries over entities of the repository. This mechanism takes off the prefixes find…By, read…By, query…By , count…By and get…By and starts parsing the rest of it.
For example the findByLastName method will look for the lastName attribute of the Musician entity using the parameter to search in the database.
You can also concatenate entity properties: in our findByInstrumentType method the repository mechanism will look for the instrument property of the Musician entity, and since this attribute maps to another entity, it will look for the attribute type of that Instrument , again, using the parameter as an input.
At a very basic level you can define conditions on entity properties and concatenate them with And and Or.
Enough chit-chat ! Let's try our work of art.
You can now start your Spring Boot application, running this maven command on the command line, in the directory where your pom file resides:
$ mvn spring-boot:run
Then we can call the first endpoint declared in our controller to persist our entities:
http://localhost:8080/musicianApp/save?firstName=David&lastName=Lebon&instrument=Guitar
http://localhost:8080/musicianApp/save?firstName=Charly&lastName=Garcia&instrument=Piano
http://localhost:8080/musicianApp/save?firstName=Indio&lastName=Solari&instrument=Voice
http://localhost:8080/musicianApp/save?firstName=Billy-Joe&lastName=Armstrong&instrument=Vocals
Let's see what's in the database:
http://localhost:8080/musicianApp/findAll
[
- id: 1,
- firstName: "David",
- lastName: "Lebon",
- instrument:
- id: 1,
- type: "Guitar"
- id: 2,
- firstName: "Charly",
- lastName: "Garcia",
- instrument:
- id: 2,
- type: "Piano"
- id: 3,
- firstName: "Indio",
- lastName: "Solari",
- instrument:
- id: 3,
- type: "Voice"
- id: 4,
- firstName: "Billy-Joe",
- lastName: "Armstrong",
- instrument:
- id: 4,
- type: "Vocals"
Now, let's look for one id:
http://localhost:8080/musicianApp/findOne/2
{
- id: 2,
- firstName: "Charly",
- lastName: "Garcia",
- instrument:
- id: 2,
- type: "Piano"
And if we want to find the musicians that plays the guitar
http://localhost:8080/musicianApp/findByInstrumentType/Guitar
[
- id: 1,
- firstName: "David",
- lastName: "Lebon",
- instrument:
- id: 1,
- type: "Guitar"
That's it for today! As I always say this is just the tip of the iceberg, there's a whole lot more functionality on the Spring Data reference documentation.
Enjoy!
Comments
Post a Comment