Spring REST CRUD Application
In this article, we are going to create a CRUD(Create Read Update and Delete) Spring application. This application uses the REST(REpresentational State Transfer) approach to perform CRUD operations. It uses @RestController annotation to create a controller that handles requests and sends the response back in JSON format.
REST is an architectural style that contains a set of constraints to create web services. You can refer to our article on REST API. We can perform CRUD operations with this approach as well. Let's understand by example and create a maven based project that contains several files and the structure looks like below.
Project Structure
// DemoAppConfig.java
This is a configuration file in Java which is an alternate of the applicationContext.xml file that we created for the XML-based configuration example. The @Configuration
annotation indicates that this is not a simple class but a configuration class and the @ComponentScan
annotation is used to indicate the component location in our spring project.
The @PropertySource
annotation is used to read the property file. Here, we have set database configuration and session factory to work with the database.
package com.studytonight.springdemo.config;
import java.beans.PropertyVetoException;
import java.util.Properties;
import java.util.logging.Logger;
import javax.sql.DataSource;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import com.mchange.v2.c3p0.ComboPooledDataSource;
@Configuration
@EnableWebMvc
@EnableTransactionManagement
@ComponentScan("com.studytonight.springdemo")
@PropertySource({ "classpath:persistence-mysql.properties" })
public class DemoAppConfig implements WebMvcConfigurer {
@Autowired
private Environment env;
private Logger logger = Logger.getLogger(getClass().getName());
// define a bean for ViewResolver
@Bean
public DataSource myDataSource() {
ComboPooledDataSource myDataSource = new ComboPooledDataSource();
try {
myDataSource.setDriverClass("com.mysql.jdbc.Driver");
}
catch (PropertyVetoException exc) {
throw new RuntimeException(exc);
}
logger.info("jdbc.url=" + env.getProperty("jdbc.url"));
logger.info("jdbc.user=" + env.getProperty("jdbc.user"));
myDataSource.setJdbcUrl(env.getProperty("jdbc.url"));
myDataSource.setUser(env.getProperty("jdbc.user"));
myDataSource.setPassword(env.getProperty("jdbc.password"));
myDataSource.setInitialPoolSize(getIntProperty("connection.pool.initialPoolSize"));
myDataSource.setMinPoolSize(getIntProperty("connection.pool.minPoolSize"));
myDataSource.setMaxPoolSize(getIntProperty("connection.pool.maxPoolSize"));
myDataSource.setMaxIdleTime(getIntProperty("connection.pool.maxIdleTime"));
return myDataSource;
}
private Properties getHibernateProperties() {
// set hibernate properties
Properties props = new Properties();
props.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));
props.setProperty("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
return props;
}
private int getIntProperty(String propName) {
String propVal = env.getProperty(propName);
int intPropVal = Integer.parseInt(propVal);
return intPropVal;
}
@Bean
public LocalSessionFactoryBean sessionFactory(){
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(myDataSource());
sessionFactory.setPackagesToScan(env.getProperty("hibernate.packagesToScan"));
sessionFactory.setHibernateProperties(getHibernateProperties());
return sessionFactory;
}
@Bean
@Autowired
public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(sessionFactory);
return txManager;
}
}
// MySpringMvcDispatcherServletInitializer.java
This is the servlet dispatcher initializer class that reads configurations from the DemoAppConfig file and starts the application.
package com.studytonight.springdemo.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class MySpringMvcDispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return null;
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { DemoAppConfig.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
// CustomerDAO.java
This is a DAO(Data Access Object) interface that contains a couple of methods to get implemented in the implementation class.
package com.studytonight.springdemo.dao;
import java.util.List;
import com.studytonight.springdemo.entity.Customer;
public interface CustomerDAO {
public List<Customer> getCustomers();
public void saveCustomer(Customer theCustomer);
public Customer getCustomer(int theId);
public void deleteCustomer(int theId);
}
// CustomerDAOImpl.java
This is a DAO(Data Access Object) implementation class that implements the CutomerDAO interface and implements its methods.
package com.studytonight.springdemo.dao;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.query.Query;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.studytonight.springdemo.entity.Customer;
@Repository
public class CustomerDAOImpl implements CustomerDAO {
@Autowired
private SessionFactory sessionFactory;
@Override
public List<Customer> getCustomers() {
Session currentSession = sessionFactory.getCurrentSession();
Query<Customer> theQuery =
currentSession.createQuery("from Customer order by lastName",
Customer.class);
List<Customer> customers = theQuery.getResultList();
return customers;
}
@Override
public void saveCustomer(Customer theCustomer) {
Session currentSession = sessionFactory.getCurrentSession();
currentSession.saveOrUpdate(theCustomer);
}
@Override
public Customer getCustomer(int theId) {
Session currentSession = sessionFactory.getCurrentSession();
Customer theCustomer = currentSession.get(Customer.class, theId);
return theCustomer;
}
@Override
public void deleteCustomer(int theId) {
Session currentSession = sessionFactory.getCurrentSession();
Query theQuery =
currentSession.createQuery("delete from Customer where id=:customerId");
theQuery.setParameter("customerId", theId);
theQuery.executeUpdate();
}
}
// Customer.java
It is an Entity class used to represent Customer details.
package com.studytonight.springdemo.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="customer")
public class Customer {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="id")
private int id;
@Column(name="first_name")
private String firstName;
@Column(name="last_name")
private String lastName;
@Column(name="email")
private String email;
public Customer() {
}
public int getId() {
return id;
}
public void setId(int 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;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "Customer [id=" + id + ", firstName=" + firstName + ", lastName=" + lastName + ", email=" + email + "]";
}
}
// CustomerRestController.java
This is our controller class that is marked with @RestController and performs CRUD operations. It returns the output in JSON format to the user.
package com.studytonight.springdemo.rest;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.studytonight.springdemo.entity.Customer;
import com.studytonight.springdemo.service.CustomerService;
@RestController
public class CustomerRestController {
@Autowired
private CustomerService customerService;
@GetMapping("/customers")
public List<Customer> getCustomers(){
return customerService.getCustomers();
}
@GetMapping("/customers/{customerId}")
public Customer getCustomer(@PathVariable int customerId){
return customerService.getCustomer(customerId);
}
@PostMapping("/customers")
public Customer addCustomer(@RequestBody Customer customer){
customer.setId(0);
customerService.saveCustomer(customer);
return customer;
}
@PutMapping("/customers")
public Customer updateCustomer(@RequestBody Customer customer){
customerService.saveCustomer(customer);
return customer;
}
@DeleteMapping("/customers/{customerId}")
public String deleteCustomer(@PathVariable int customerId){
customerService.deleteCustomer(customerId);
return "Deleted Customer Id: "+customerId;
}
}
// CustomerService.java
package com.studytonight.springdemo.service;
import java.util.List;
import com.studytonight.springdemo.entity.Customer;
public interface CustomerService {
public List<Customer> getCustomers();
public void saveCustomer(Customer theCustomer);
public Customer getCustomer(int theId);
public void deleteCustomer(int theId);
}
// CustomerServiceImpl.java
package com.studytonight.springdemo.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.studytonight.springdemo.dao.CustomerDAO;
import com.studytonight.springdemo.entity.Customer;
@Service
public class CustomerServiceImpl implements CustomerService {
// need to inject customer dao
@Autowired
private CustomerDAO customerDAO;
@Override
@Transactional
public List<Customer> getCustomers() {
return customerDAO.getCustomers();
}
@Override
@Transactional
public void saveCustomer(Customer theCustomer) {
customerDAO.saveCustomer(theCustomer);
}
@Override
@Transactional
public Customer getCustomer(int theId) {
return customerDAO.getCustomer(theId);
}
@Override
@Transactional
public void deleteCustomer(int theId) {
customerDAO.deleteCustomer(theId);
}
}
// persistence-mysql.properties
This is a property file that contains database related configurations and is accessed in the config file using the @PropertySource
annotation.
#
# JDBC connection properties
#
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/springwithdb?useSSL=false
jdbc.user=user_name
jdbc.password=user_password
#
# Connection pool properties
#
connection.pool.initialPoolSize=5
connection.pool.minPoolSize=5
connection.pool.maxPoolSize=20
connection.pool.maxIdleTime=3000
#
# Hibernate properties
#
hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.show_sql=true
hibernate.packagesToScan=com.studytonight.springdemo.entity
Run the Application
After successfully completing the project and adding the dependencies run the application using the server and you will get the output as below.
To test the application, we used a Postman application. It is a good application to check web requests. You can install it from the official site and then use it.
Access All the Customers
Add New Customer
Show all Customers after Adding new Customer
Fetch Single Customer