Tagged: CRUD

Spring Boot CRUD Application
30
Mar
2021

Spring Boot CRUD Application

Learn to build Spring boot web applications supporting CRUD operations through form-based UI built on thymeleaf and spring mvc support.

1. Overview

In this tutorial, we are creating a web application with two views :

  • List all employees view – It display all the employees from database in a tabular format in UI. Additionally, there are links to update or delete any employee. This screen also has a separate option which can navigate to create employee screen.
  • Create/update employee view – This screen is used to add a new employee or edit the details of a existing .Add employee screen

There are two main components in this example to focus – MVC controller and UI views.

2. Spring MVC Controller

The controller class has URL mappings and it’s handler methods. There are handler methods are all CRUD operations including POST operation to handle form submission to create/update an employee.

Notice how given handler methods are binding model data to view; and they return view names in string format which gets resolved by view resolver in HTML files.

import java.util.List;import java.util.Optional; import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod; import com.fusebes.demo.entity.EmployeeEntity;import com.fusebes.demo.exception.RecordNotFoundException;import com.fusebes.demo.service.EmployeeService; @Controller@RequestMapping("/")public class EmployeeMvcController {@AutowiredEmployeeService service; @RequestMappingpublic String getAllEmployees(Model model) {List<EmployeeEntity> list = service.getAllEmployees(); model.addAttribute("employees", list);return "list-employees";} @RequestMapping(path = {"/edit", "/edit/{id}"})public String editEmployeeById(Model model, @PathVariable("id") Optional<Long> id) throws RecordNotFoundException {if (id.isPresent()) {EmployeeEntity entity = service.getEmployeeById(id.get());model.addAttribute("employee", entity);} else {model.addAttribute("employee", new EmployeeEntity());}return "add-edit-employee";} @RequestMapping(path = "/delete/{id}")public String deleteEmployeeById(Model model, @PathVariable("id") Long id) throws RecordNotFoundException {service.deleteEmployeeById(id);return "redirect:/";} @RequestMapping(path = "/createEmployee", method = RequestMethod.POST)public String createOrUpdateEmployee(EmployeeEntity employee) {service.createOrUpdateEmployee(employee);return "redirect:/";}}
  • getAllEmployees() – It returns list of all employees and is mapped to path “/”. It’s default view of the application.
  • editEmployeeById() – It is used to either add new or edit an existing employee. It uses the same HTML view for both operations. If there is an employee Id in context, then that employee is edited – else a new employee is created.
  • deleteEmployeeById() – A simple URL request to delete an employee by it’s Id.
  • createOrUpdateEmployee() – This method handle the HTTP POST requests which are used to either create a new employee or update an employee. Create or update operation depends on presence of employee id in model.

3. Thymeleaf templates

As stated before we are using two views in this example.

<!DOCTYPE html><html xmlns:th="http://www.thymeleaf.org"> <head><meta charset="utf-8"><meta http-equiv="x-ua-compatible" content="ie=edge"><title>All Employees</title><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"><link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.4.1/css/all.css"></head> <body><div class="container my-2"><div class="card"><div class="card-body"><div th:switch="${employees}" class="container my-5"><p class="my-5"><a href="/edit" class="btn btn-primary"><i class="fas fa-user-plus ml-2"> Add Employee </i></a></p><div class="col-md-10"><h2 th:case="null">No record found !!</h2><div th:case="*"><table class="table table-striped table-responsive-md"><thead><tr><th>First Name</th><th>Last Name</th><th>Email</th><th>Edit</th><th>Delete</th></tr></thead><tbody><tr th:each="employee : ${employees}"><td th:text="${employee.firstName}"></td><td th:text="${employee.lastName}"></td><td th:text="${employee.email}"></td><td><a th:href="@{/edit/{id}(id=${employee.id})}"class="btn btn-primary"><i class="fas fa-user-edit ml-2"></i></a></td><td><a th:href="@{/delete/{id}(id=${employee.id})}"class="btn btn-primary"><i class="fas fa-user-times ml-2"></i></a></td></tr></tbody></table></div> </div></div></div></div></div></body> </html>
<!DOCTYPE html><html xmlns:th="http://www.thymeleaf.org"> <head><meta charset="utf-8"><meta http-equiv="x-ua-compatible" content="ie=edge"><title>Add Employee</title><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"><link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.4.1/css/all.css"></head> <body><div class="container my-5"><h3> Add Employee</h3><div class="card"><div class="card-body"><div class="col-md-10"><form action="#" th:action="@{/createEmployee}" th:object="${employee}"method="post"><div class="row"><div class="form-group col-md-8"><label for="name" class="col-form-label">First Name</label> <input type="text" th:field="*{firstName}" class="form-control"id="firstName" placeholder="First Name" /></div><div class="form-group col-md-8"><label for="name" class="col-form-label">Last Name</label> <input type="text" th:field="*{lastName}" class="form-control"id="lastName" placeholder="Last Name" /></div><div class="form-group col-md-8"><label for="email" class="col-form-label">Email</label> <input type="text" th:field="*{email}" class="form-control"id="email" placeholder="Email Id" /></div> <div class="col-md-6"><input type="submit" class="btn btn-primary" value=" Submit "></div> <input type="hidden" id="id" th:field="*{id}"> </div></form></div></div></div></div></body> </html>

4. Entity and repository

We have bound the EmployeeEntity class as model to UI.

import javax.persistence.Column;import javax.persistence.Entity;import javax.persistence.GeneratedValue;import javax.persistence.Id;import javax.persistence.Table; @Entity@Table(name="TBL_EMPLOYEES")public class EmployeeEntity { @Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id; @Column(name="first_name")private String firstName; @Column(name="last_name")private String lastName; @Column(name="email", nullable=false, length=200)private String email; //Setters and getters @Overridepublic String toString() {return "EmployeeEntity [id=" + id + ", firstName=" + firstName + ", lastName=" + lastName + ", email=" + email   + "]";}}

To persist data in database, we are using H2 (In-Memory) Database and using Spring data’s CrudRepository interface. It provides out of the box in-built method for simple CRUD operations.

import org.springframework.data.repository.CrudRepository;import org.springframework.stereotype.Repository; import com.fusebes.demo.entity.EmployeeEntity; @Repositorypublic interface EmployeeRepository extends CrudRepository<EmployeeEntity, Long> { }

Note that repository is initialized with two SQL files which create the database tables and populate default data to it.

DROP TABLE IF EXISTS TBL_EMPLOYEES; CREATE TABLE TBL_EMPLOYEES (id INT AUTO_INCREMENT  PRIMARY KEY,first_name VARCHAR(250) NOT NULL,last_name VARCHAR(250) NOT NULL,email VARCHAR(250) DEFAULT NULL);
INSERT INTO TBL_EMPLOYEES (first_name, last_name, email) VALUES('Lokesh', 'Gupta', 'fusebes@gmail.com'),('John', 'Doe', 'xyz@email.com');

5. Service class

Another important class to see is EmployeeService class through which controller interacts with repository. It contains additional business logic to perform.

import java.util.ArrayList;import java.util.List;import java.util.Optional; import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service; import com.fusebes.demo.entity.EmployeeEntity;import com.fusebesemo.exception.RecordNotFoundException;import com.fusebes.demo.repository.EmployeeRepository; @Servicepublic class EmployeeService { @AutowiredEmployeeRepository repository; public List<EmployeeEntity> getAllEmployees(){List<EmployeeEntity> result = (List<EmployeeEntity>) repository.findAll(); if(result.size() > 0) {return result;} else {return new ArrayList<EmployeeEntity>();}} public EmployeeEntity getEmployeeById(Long id) throws RecordNotFoundException {Optional<EmployeeEntity> employee = repository.findById(id); if(employee.isPresent()) {return employee.get();} else {throw new RecordNotFoundException("No employee record exist for given id");}} public EmployeeEntity createOrUpdateEmployee(EmployeeEntity entity) {if(entity.getId()  == null) {entity = repository.save(entity); return entity;} else{Optional<EmployeeEntity> employee = repository.findById(entity.getId()); if(employee.isPresent()) {EmployeeEntity newEntity = employee.get();newEntity.setEmail(entity.getEmail());newEntity.setFirstName(entity.getFirstName());newEntity.setLastName(entity.getLastName()); newEntity = repository.save(newEntity); return newEntity;} else {entity = repository.save(entity); return entity;}}}  public void deleteEmployeeById(Long id) throws RecordNotFoundException {Optional<EmployeeEntity> employee = repository.findById(id); if(employee.isPresent()) {repository.deleteById(id);} else {throw new RecordNotFoundException("No employee record exist for given id");}} }

6. Add Spring boot and Thymeleaf maven dependencies

In spring boot projects, we only need to add spring-boot-starter-thymeleaf dependency and project itself auto-configures thymeleaf with default configuration. It read the HTML templates from /src/main/resources/templates folder.

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.5.RELEASE</version><relativePath /> <!-- lookup parent from repository --></parent><groupId>com.fusebes</groupId><artifactId>demo</artifactId><version>0.0.1-SNAPSHOT</version><name>demo</name><description>Demo project for Spring Boot</description> <properties><java.version>1.8</java.version></properties> <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>com.h2database</groupId><artifactId>h2</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies></project>

7. spring boot thymeleaf crud tutorial demo

Start this application as Spring boot application which starts the web application in the embedded tomcat server.

Hit the URL : http://localhost:8080/

Verify that screen is rendered with two default employee details from data.sql file.

Play with application. Create few new employees, edit existing employees. Delete some employees.

A Guide to JPA with Spring
26
Mar
2021

A Guide to JPA with Spring

This tutorial shows how to set up Spring with JPA, using Hibernate as a persistence provider.

For a step by step introduction about setting up the Spring context using Java-based configuration and the basic Maven pom for the project, see this article.

We’ll start by setting up JPA in a Spring Boot project, then we’ll look into the full configuration we need if we have a standard Spring project.

JPA in Spring Boot

The Spring Boot project is intended to make creating Spring applications much faster and easier. This is done with the use of starters and auto-configuration for various Spring functionalities, JPA among them.

2.1. Maven Dependencies

To enable JPA in a Spring Boot application, we need the spring-boot-starter and spring-boot-starter-data-jpa dependencies:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <version>2.2.6.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
    <version>2.2.6.RELEASE</version>
</dependency>

The spring-boot-starter contains the necessary auto-configuration for Spring JPA. Also, the spring-boot-starter-jpa project references all the necessary dependencies such as hibernate-core.

2.2. Configuration

Spring Boot configures Hibernate as the default JPA provider, so it’s no longer necessary to define the entityManagerFactory bean unless we want to customize it.

Spring Boot can also auto-configure the dataSource bean, depending on the database we’re using. In the case of an in-memory database of type H2HSQLDB, and Apache Derby, Boot automatically configures the DataSource if the corresponding database dependency is present on the classpath.

For example, if we want to use an in-memory H2 database in a Spring Boot JPA application, we only need to add the h2 dependency to the pom.xml file:

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>1.4.200</version>
</dependency>

This way, we don’t need to define the dataSource bean, but we can do so if we want to customize it.

If we want to use JPA with MySQL database, then we need the mysql-connector-java dependency, as well as to define the DataSource configuration.

We can do this in a @Configuration class, or by using standard Spring Boot properties.

The Java configuration looks the same as it does in a standard Spring project:

@Bean
public DataSource dataSource() {
    DriverManagerDataSource dataSource = new DriverManagerDataSource();

    dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
    dataSource.setUsername("mysqluser");
    dataSource.setPassword("mysqlpass");
    dataSource.setUrl(
      "jdbc:mysql://localhost:3306/myDb?createDatabaseIfNotExist=true"); 
    
    return dataSource;
}

To configure the data source using a properties file, we have to set properties prefixed with spring.datasource:

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=mysqluser
spring.datasource.password=mysqlpass
spring.datasource.url=
  jdbc:mysql://localhost:3306/myDb?createDatabaseIfNotExist=true

Spring Boot will automatically configure a data source based on these properties.

Also in Spring Boot 1, the default connection pool was Tomcat, but with Spring Boot 2 it has been changed to HikariCP.

As we can see, the basic JPA configuration is fairly simple if we’re using Spring Boot.

However, if we have a standard Spring project, then we need more explicit configuration, using either Java or XML. That’s what we’ll focus on in the next sections.

3. The JPA Spring Configuration With Java – in a Non-Boot Project

To use JPA in a Spring project, we need to set up the EntityManager.

This is the main part of the configuration and we can do it via a Spring factory bean. This can be either the simpler LocalEntityManagerFactoryBean or the more flexible LocalContainerEntityManagerFactoryBean.

Let’s see how we can use the latter option:

@Configuration
@EnableTransactionManagement
public class PersistenceJPAConfig{

   @Bean
   public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
      LocalContainerEntityManagerFactoryBean em 
        = new LocalContainerEntityManagerFactoryBean();
      em.setDataSource(dataSource());
      em.setPackagesToScan(new String[] { "com.baeldung.persistence.model" });

      JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
      em.setJpaVendorAdapter(vendorAdapter);
      em.setJpaProperties(additionalProperties());

      return em;
   }
   
   // ...

}

We also need to explicitly define the DataSource bean we’ve used above:

@Bean
public DataSource dataSource(){
    DriverManagerDataSource dataSource = new DriverManagerDataSource();
    dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
    dataSource.setUrl("jdbc:mysql://localhost:3306/spring_jpa");
    dataSource.setUsername( "tutorialuser" );
    dataSource.setPassword( "tutorialmy5ql" );
    return dataSource;
}

The final part of the configuration are the additional Hibernate properties and the TransactionManager and exceptionTranslation beans:

@Bean
public PlatformTransactionManager transactionManager() {
    JpaTransactionManager transactionManager = new JpaTransactionManager();
    transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());

    return transactionManager;
}

@Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
    return new PersistenceExceptionTranslationPostProcessor();
}

Properties additionalProperties() {
    Properties properties = new Properties();
    properties.setProperty("hibernate.hbm2ddl.auto", "create-drop");
    properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
       
    return properties;
}

4. The JPA Spring Configuration With XML

Next, let’s see the same Spring Configuration with XML:

<bean id="myEmf" 
  class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="packagesToScan" value="com.baeldung.persistence.model" />
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
    </property>
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.hbm2ddl.auto">create-drop</prop>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
        </props>
    </property>
</bean>

<bean id="dataSource" 
  class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://localhost:3306/spring_jpa" />
    <property name="username" value="tutorialuser" />
    <property name="password" value="tutorialmy5ql" />
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="myEmf" />
</bean>
<tx:annotation-driven />

<bean id="persistenceExceptionTranslationPostProcessor" class=
  "org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />

There’s a relatively small difference between the XML and the new Java-based configuration. Namely, in XML, a reference to another bean can point to either the bean or a bean factory for that bean.

In Java, however, since the types are different, the compiler doesn’t allow it, and so the EntityManagerFactory is first retrieved from its bean factory and then passed to the transaction manager:

transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());

5. Going Full XML-less

Usually, JPA defines a persistence unit through the META-INF/persistence.xml file. Starting with Spring 3.1, the persistence.xml is no longer necessary. The LocalContainerEntityManagerFactoryBean now supports a packagesToScan property where the packages to scan for @Entity classes can be specified.

This file was the last piece of XML we need to remove. We can now set up JPA fully with no XML.

We would usually specify JPA properties in the persistence.xml file. Alternatively, we can add the properties directly to the entity manager factory bean:

factoryBean.setJpaProperties(this.additionalProperties());

As a side note, if Hibernate would be the persistence provider, then this would be the way to specify Hibernate specific properties as well.

6. The Maven Configuration

In addition to the Spring Core and persistence dependencies – show in detail in the Spring with Maven tutorial – we also need to define JPA and Hibernate in the project, as well as a MySQL connector:

<dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-core</artifactId>
   <version>5.2.17.Final</version>
   <scope>runtime</scope>
</dependency>

<dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <version>8.0.19</version>
   <scope>runtime</scope>
</dependency>

Note that the MySQL dependency is included here as an example. We need a driver to configure the datasource, but any Hibernate-supported database will do.

7. Conclusion

This tutorial illustrated how to configure JPA with Hibernate in Spring in both a Spring Boot and a standard Spring application.