The first part of my Spring Data JPA tutorial described, how you configure Spring Data JPA. This blog entry goes a bit deeper and describes how you can use Spring Data JPA for creating a simple CRUD application. The requirements of the application are following:
- The person must have a first name and last name. Both of these properties are mandatory.
- It must be possible to list persons.
- It must be possible to add new persons.
- It must be possible to edit the information of existing persons.
- It must be possible to delete persons.
I have now described the requirements of the created application. Now it is time to get to work and start implementing it.
Required Steps
The implementation of CRUD application can be divided to following steps:
- Implementing the Person model object
- Creating a repository for the Person object
- Using the created repository
Each of these steps is explained with more details in following.
Implementing the Model Object
The implementation of the Person class is pretty simple. However, there are few issues which I would like to point out:
- A builder is used to to create new instances of Person class. This might seem like over engineering but I like this approach for two reasons: First, it makes code easier to read than using the telescopic constructor pattern. Second, it ensures that you cannot create an object which in an inconsistent state during its construction (This is something that the common JavaBeans pattern cannot guarantee).
- The only way to change the information stored in a Person object is to call the update() method. I am fan of putting as much logic to the model objects as possible. This approach does not clutter the service layer with domain logic and and ensures that you do not end up with an anemic domain model.
The source code of my Person class is given in following:
* An entity class which contains the information of a single person.
* @author Petri Kainulainen
*/
@Entity
@Table(name = "persons")
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(name = "creation_time", nullable = false)
private Date creationTime;
@Column(name = "first_name", nullable = false)
private String firstName;
@Column(name = "last_name", nullable = false)
private String lastName;
@Column(name = "modification_time", nullable = false)
private Date modificationTime;
@Version
private long version = 0;
public Long getId() {
return id;
}
/**
* Gets a builder which is used to create Person objects.
* @param firstName The first name of the created user.
* @param lastName The last name of the created user.
* @return A new Builder instance.
*/
public static Builder getBuilder(String firstName, String lastName) {
return new Builder(firstName, lastName);
}
public Date getCreationTime() {
return creationTime;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
/**
* Gets the full name of the person.
* @return The full name of the person.
*/
@Transient
public String getName() {
StringBuilder name = new StringBuilder();
name.append(firstName);
name.append(" ");
name.append(lastName);
return name.toString();
}
public Date getModificationTime() {
return modificationTime;
}
public long getVersion() {
return version;
}
public void update(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
@PreUpdate
public void preUpdate() {
modificationTime = new Date();
}
@PrePersist
public void prePersist() {
Date now = new Date();
creationTime = now;
modificationTime = now;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
/**
* A Builder class used to create new Person objects.
*/
public static class Builder {
Person built;
/**
* Creates a new Builder instance.
* @param firstName The first name of the created Person object.
* @param lastName The last name of the created Person object.
*/
Builder(String firstName, String lastName) {
built = new Person();
built.firstName = firstName;
built.lastName = lastName;
}
/**
* Builds the new Person object.
* @return The created Person object.
*/
public Person build() {
return built;
}
}
/**
* This setter method should only be used by unit tests.
* @param id
*/
protected void setId(Long id) {
this.id = id;
}
}
Creating the Repository
Implementing a repository which provides CRUD operations for the Person model object is pretty straightforward. All you have to do is to create an interface which extends the JpaRepository interface. The JpaRepository interface is a JPA specific extension to the Repository interface and it gives you the access to following methods which are used to implement the CRUD application:
- delete(T entity) which deletes the entity given as a parameter.
- findAll() which returns a list of entities.
- findOne(ID id) which returns the entity using the id given a parameter as a search criteria.
- save(T entity) which saves the entity given as a parameter.
The source code of my PersonRepository interface is given in following:
* Specifies methods used to obtain and modify person related information
* which is stored in the database.
* @author Petri Kainulainen
*/
public interface PersonRepository extends JpaRepository<Person, Long> {
}
Using the Created Repository
You have now created both the model object and the repository which is needed to communicate with the database. The next step is to implement a service class which acts as an intermediary between the controllers and the implemented repository. The structure of the service layer is described next.
The PersonDTO is a simple DTO object which is used as a form object in my example application. Its source code is given in following:
* A DTO object which is used as a form object
* in create person and edit person forms.
* @author Petri Kainulainen
*/
public class PersonDTO {
private Long id;
@NotEmpty
private String firstName;
@NotEmpty
private String lastName;
public PersonDTO() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
The PersonService interface specifies the methods which are provided by the actual implementation. Its source code is given in following:
* Declares methods used to obtain and modify person information.
* @author Petri Kainulainen
*/
public interface PersonService {
/**
* Creates a new person.
* @param created The information of the created person.
* @return The created person.
*/
public Person create(PersonDTO created);
/**
* Deletes a person.
* @param personId The id of the deleted person.
* @return The deleted person.
* @throws PersonNotFoundException if no person is found with the given id.
*/
public Person delete(Long personId) throws PersonNotFoundException;
/**
* Finds all persons.
* @return A list of persons.
*/
public List<Person> findAll();
/**
* Finds person by id.
* @param id The id of the wanted person.
* @return The found person. If no person is found, this method returns null.
*/
public Person findById(Long id);
/**
* Updates the information of a person.
* @param updated The information of the updated person.
* @return The updated person.
* @throws PersonNotFoundException if no person is found with given id.
*/
public Person update(PersonDTO updated) throws PersonNotFoundException;
}
The source code of the RepositoryPersonService class which implements the PersonService interface is given in following:
* This implementation of the PersonService interface communicates with
* the database by using a Spring Data JPA repository.
* @author Petri Kainulainen
*/
@Service
public class RepositoryPersonService implements PersonService {
private static final Logger LOGGER = LoggerFactory.getLogger(RepositoryPersonService.class);
@Resource
private PersonRepository personRepository;
@Transactional
@Override
public Person create(PersonDTO created) {
LOGGER.debug("Creating a new person with information: " + created);
Person person = Person.getBuilder(created.getFirstName(), created.getLastName()).build();
return personRepository.save(person);
}
@Transactional(rollbackFor = PersonNotFoundException.class)
@Override
public Person delete(Long personId) throws PersonNotFoundException {
LOGGER.debug("Deleting person with id: " + personId);
Person deleted = personRepository.findOne(personId);
if (deleted == null) {
LOGGER.debug("No person found with id: " + personId);
throw new PersonNotFoundException();
}
personRepository.delete(deleted);
return deleted;
}
@Transactional(readOnly = true)
@Override
public List<Person> findAll() {
LOGGER.debug("Finding all persons");
return personRepository.findAll();
}
@Transactional(readOnly = true)
@Override
public Person findById(Long id) {
LOGGER.debug("Finding person by id: " + id);
return personRepository.findOne(id);
}
@Transactional(rollbackFor = PersonNotFoundException.class)
@Override
public Person update(PersonDTO updated) throws PersonNotFoundException {
LOGGER.debug("Updating person with information: " + updated);
Person person = personRepository.findOne(updated.getId());
if (person == null) {
LOGGER.debug("No person found with id: " + updated.getId());
throw new PersonNotFoundException();
}
person.update(updated.getFirstName(), updated.getLastName());
return person;
}
/**
* This setter method should be used only by unit tests.
* @param personRepository
*/
protected void setPersonRepository(PersonRepository personRepository) {
this.personRepository = personRepository;
}
}
What is Next?
I have now demonstrated to you how you can implement a simple CRUD application with Spring Data JPA. If you are interested of seeing my fully functional example in action, you can get it from Github. The third part of my Spring Data JPA tutorial describes how you can create custom queries with query methods.
Related Posts
- Spring Data JPA Tutorial Part Four: JPA Criteria Queries
- Spring Data JPA Tutorial Part Three: Custom Queries with Query Methods
- Spring Data JPA Tutorial Part One: Configuration
- Creating RESTful Urls with Spring MVC 3.1 Part Two: Dispatcher Servlet Url Mappings
- Creating RESTful Urls with Spring MVC 3.1 Part One: default-servlet-handler



{ 1 comment… read it below or add one }
Great stuff.