[Thực hành] Ứng dụng quản lý khách hàng
NỘI DUNG BÀI VIẾT
Mục tiêu
Luyện tập việc sử dụng Spring JPA với Hibernate.
Điều kiện
Có kiến thức căn bản về việc sử dụng Spring JPA với Hibernate.
Mô tả
Trong phần này, chúng ta sẽ phát triển một ứng dụng quản lý khách hàng, sử dụng JPA và Hibernate.
Ứng dụng có các chức năng chính:
- Hiển thị danh sách khách hàng
- Thêm một khách hàng mới
- Xoá một khách hàng
- Chỉnh sửa thông tin khách hàng
Hướng dẫn
Bước 1: Tạo dự án mới
New project > Gradle, chọn Java và Web

Đặt GroupId là com.codegym và ArtifactId là customer-management
Bước 2: Thêm các thư viện cần thiết vào file build.gradle
Danh sách các thư viện:
compile group: 'javax.servlet', name: 'javax.servlet-api', version: '3.1.0' compile group: 'org.springframework', name: 'spring-core', version: '4.3.17.RELEASE' compile group: 'org.springframework', name: 'spring-context', version: '4.3.17.RELEASE' compile group: 'org.springframework', name: 'spring-beans', version: '4.3.17.RELEASE' compile group: 'org.springframework', name: 'spring-web', version: '4.3.17.RELEASE' compile group: 'org.hibernate', name: 'hibernate-core', version: '5.3.0.Final' compile group: 'org.hibernate', name: 'hibernate-entitymanager', version: '5.3.0.Final' compile group: 'org.springframework', name: 'spring-orm', version: '4.3.17.RELEASE' compile group: 'org.springframework', name: 'spring-webmvc', version: '4.3.17.RELEASE' compile group: 'org.thymeleaf', name: 'thymeleaf-spring4', version: '3.0.9.RELEASE' compile group: 'mysql', name: 'mysql-connector-java', version: '8.0.11' compile group: 'org.springframework.data', name: 'spring-data-jpa', version: '1.11.12.RELEASE'
Bước 3: Tạo cấu trúc của ứng dụng
Các gói cơ bản bao gồm:
com.codegym.cms.model
com.codegym.cms.repository
com.codegym.cms.service
com.codegym.cms.controller

Lớp ApplicationInitializer là lớp cấu hình khởi tạo cho ứng dụng (thay thế file web.xml nếu dùng cấu hình .xml):
package com.codegym.cms; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; public class ApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { return new Class[]{ApplicationConfig.class}; } @Override protected Class<?>[] getServletConfigClasses() { return new Class[0]; } @Override protected String[] getServletMappings() { return new String[]{"/"}; } }
Bước 4: Tạo lớp cấu hình cho ứng dụng
Lớp ApplicationConfig được dùng để cấu hình cho toàn bộ ứng dụng (thay thế cho file dispatcher-servlet.xml nếu dùng cấu hình .xml)
package com.codegym.cms import com.codegym.cms.repository.CustomerRepository; import com.codegym.cms.repository.impl.CustomerRepositoryImpl; import com.codegym.cms.service.CustomerService; import com.codegym.cms.service.impl.CustomerServiecImpl; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.DriverManagerDataSource; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.JpaVendorAdapter; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import org.thymeleaf.TemplateEngine; import org.thymeleaf.spring4.SpringTemplateEngine; import org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver; import org.thymeleaf.spring4.view.ThymeleafViewResolver; import org.thymeleaf.templatemode.TemplateMode; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.sql.DataSource; import java.util.Properties; @Configuration @EnableWebMvc @EnableTransactionManagement @ComponentScan("com.codegym.cms") public class ApplicationConfig extends WebMvcConfigurerAdapter implements ApplicationContextAware { private ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } @Bean public CustomerRepository customerRepository(){ return new CustomerRepositoryImpl(); } @Bean public CustomerService customerService(){ return new CustomerServiecImpl(); } //Thymeleaf Configuration @Bean public SpringResourceTemplateResolver templateResolver(){ SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver(); templateResolver.setApplicationContext(applicationContext); templateResolver.setPrefix("/WEB-INF/views"); templateResolver.setSuffix(".html"); templateResolver.setTemplateMode(TemplateMode.HTML); return templateResolver; } @Bean public TemplateEngine templateEngine(){ TemplateEngine templateEngine = new SpringTemplateEngine(); templateEngine.setTemplateResolver(templateResolver()); return templateEngine; } @Bean public ThymeleafViewResolver viewResolver(){ ThymeleafViewResolver viewResolver = new ThymeleafViewResolver(); viewResolver.setTemplateEngine(templateEngine()); return viewResolver; } //JPA configuration @Bean @Qualifier(value = "entityManager") public EntityManager entityManager(EntityManagerFactory entityManagerFactory) { return entityManagerFactory.createEntityManager(); } @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory() { LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); em.setDataSource(dataSource()); em.setPackagesToScan(new String[]{"com.codegym.cms.model"}); JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); em.setJpaVendorAdapter(vendorAdapter); em.setJpaProperties(additionalProperties()); return em; } @Bean public DataSource dataSource(){ DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/cms"); dataSource.setUsername( "root" ); dataSource.setPassword( "123456" ); return dataSource; } @Bean public PlatformTransactionManager transactionManager(EntityManagerFactory emf){ JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory(emf); return transactionManager; } Properties additionalProperties() { Properties properties = new Properties(); properties.setProperty("hibernate.hbm2ddl.auto", "update"); properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect"); return properties; } }
Bước 5: Cấu hình Tomcat Server
Chạy thử ứng dụng để đảm bảo các bước cài đặt và cấu hình đã hoạt động tốt.
Lưu ý: Nếu xuất hiện lỗi java.lang.ClassNotFoundException:org.springframework.web.context.ContextLoaderListener thì cần xử lý bằng cách đi vào Project Structure -> Artifactts -> Put into Output Root để thêm tất cả các thư viện vào trong output khi build.
Bước 6: Tạo lớp model Customer
Lớp com.codegym.cms.model.Customer
package com.codegym.cms.model; import javax.persistence.*; @Entity @Table(name = "customers") public class Customer { @Id @GeneratedValue(strategy= GenerationType.AUTO) private Long id; private String firstName; private String lastName; public Customer() {} public Customer(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } @Override public String toString() { return String.format("Customer[id=%d, firstName='%s', lastName='%s']", id, firstName, lastName); } 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; } }
Bước 7: Tạo repository
Interface Repository khai báo các phương thức chung cho tất cả các repository.
package com.codegym.cms.repository; import java.util.List; public interface Repository <T> { List<T> findAll(); T findById(Long id); void save(T model); void remove(Long id); }
Interface CustomerRepository khai báo các phương thức riêng cho Customer, trong trường hợp này, CustomerRepository không có phương thức tuỳ biến nào cả, do đó chỉ đơn giản là kế thừa từ interface Repository.
package com.codegym.cms.repository; import com.codegym.cms.model.Customer; public interface CustomerRepository extends Repository<Customer> { } Lớp com.codegym.cms.repository.impl.CustomerRepositoryImpl: package com.codegym.cms.repository.impl; import com.codegym.cms.model.Customer; import com.codegym.cms.repository.CustomerRepository; import javax.persistence.EntityManager; import javax.persistence.NoResultException; import javax.persistence.PersistenceContext; import javax.persistence.TypedQuery; import javax.transaction.Transactional; import java.util.List; @Transactional public class CustomerRepositoryImpl implements CustomerRepository { @PersistenceContext private EntityManager em; @Override public List<Customer> findAll() { TypedQuery<Customer> query = em.createQuery("select c from Customer c", Customer.class); return query.getResultList(); } @Override public Customer findById(Long id) { TypedQuery<Customer> query = em.createQuery("select c from Customer c where c.id=:id", Customer.class); query.setParameter("id", id); try { return query.getSingleResult(); }catch (NoResultException e){ return null; } } @Override public void save(Customer model) { if(model.getId() != null){ em.merge(model); } else { em.persist(model); } } @Override public void remove(Long id) { Customer customer = findById(id); if(customer != null){ em.remove(customer); } } }
Bước 8: Tạo các service
Interface com.codegym.cms.service.CustomerService
package com.codegym.cms.service; import com.codegym.cms.model.Customer; import java.util.List; public interface CustomerService { List<Customer> findAll(); Customer findById(Long id); void save(Customer customer); void remove(Long id); } Lớp com.codegym.cms.service.impl.CustomerServiceImpl package com.codegym.cms.service.impl; import com.codegym.cms.model.Customer; import com.codegym.cms.repository.CustomerRepository; import com.codegym.cms.service.CustomerService; import org.springframework.beans.factory.annotation.Autowired; import java.util.List; public class CustomerServiecImpl implements CustomerService{ @Autowired private CustomerRepository customerRepository; @Override public List<Customer> findAll() { return customerRepository.findAll(); } @Override public Customer findById(Long id) { return customerRepository.findById(id); } @Override public void save(Customer customer) { customerRepository.save(customer); } @Override public void remove(Long id) { customerRepository.remove(id); } }
Bước 9: Khai báo các service và repository bean trong ApplicationConfig
Bổ sung khai báo 2 bean mới vào trong lớp ApplicationConfig
@Bean public CustomerRepository customerRepository(){ return new CustomerRepositoryImpl(); } @Bean public CustomerService customerService(){ return new CustomerServiecImpl(); }
Bước 10: Tạo CustomerController
Tạo lớp com.codegym.cms.controller.CustomerController với phương thức để hiển thị form tạo Customer và lưu Customer.
package com.codegym.cms.controller; import com.codegym.cms.model.Customer; import com.codegym.cms.service.CustomerService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.servlet.ModelAndView; @Controller public class CustomerController { @Autowired private CustomerService customerService; @GetMapping("/create-customer") public ModelAndView showCreateForm(){ ModelAndView modelAndView = new ModelAndView("/customer/create"); modelAndView.addObject("customer", new Customer()); return modelAndView; } @PostMapping("/create-customer") public ModelAndView saveCustomer(@ModelAttribute("customer") Customer customer){ customerService.save(customer); ModelAndView modelAndView = new ModelAndView("/customer/create"); modelAndView.addObject("customer", new Customer()); modelAndView.addObject("message", "New customer created successfully"); return modelAndView; } }
Bước 11: Tạo view để thêm Customer mới
File /views/customer/create.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Create customer</title> </head> <body> <h1>Create customer</h1> <p> <a href="/customers">Customer list</a> </p> <th:block th:if="${message}"> <p th:text="${message}"></p> </th:block> <form th:action="@{/create-customer}" th:object="${customer}" method="post"> <table> <tr> <td>First name:</td> <td><input type="text" th:field="*{firstName}"/></td> </tr> <tr> <td>Last name:</td> <td><input type="text" th:field="*{lastName}"/></td> </tr> <tr> <td></td> <td><input type="submit" value="Create customer"></td> </tr> </table> </form> </body> </html>
Bước 12: Chạy ứng dụng và quan sát kết quả
Đi đến đường dẫn http://localhost:8080/create-customer và nhập dữ liệu của Customer.
Bước 13: Tạo chức năng hiển thị danh sách khách hàng
Cập nhật CustomerController
@GetMapping("/customers") public ModelAndView listCustomers(){ List<Customer> customers = customerService.findAll(); ModelAndView modelAndView = new ModelAndView("/customer/list"); modelAndView.addObject("customers", customers); return modelAndView; } Tạo file /WEB-INF/views/customer/list.html <!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <a href="/create-customer">Create new customer</a> <h1>Customers</h1> <table border="1"> <tr> <th>First name</th> <th>Last name</th> </tr> <th:block th:each="customer : ${customers}"> <tr> <td th:text="${customer.firstName}"></td> <td th:text="${customer.lastName}"></td> </tr> </th:block> </table> </body> </html>
Chạy ứng dụng và đi đến trang http://localhost:8080/customers để quan sát kết quả.
Bước 14: Tạo chức năng cập nhật Customer
Cập nhật CustomerController
@GetMapping("/edit-customer/{id}") public ModelAndView showEditForm(@PathVariable Long id){ Customer customer = customerService.findById(id); if(customer != null) { ModelAndView modelAndView = new ModelAndView("/customer/edit"); modelAndView.addObject("customer", customer); return modelAndView; }else { ModelAndView modelAndView = new ModelAndView("/error.404"); return modelAndView; } } @PostMapping("/edit-customer") public ModelAndView updateCustomer(@ModelAttribute("customer") Customer customer){ customerService.save(customer); ModelAndView modelAndView = new ModelAndView("/customer/edit"); modelAndView.addObject("customer", customer); modelAndView.addObject("message", "Customer updated successfully"); return modelAndView; }
Tạo file /WEB-INF/views/customer/edit.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Edit customer</title> </head> <body> <h1>Edit customer</h1> <p> <a href="/customers">Customer list</a> </p> <th:block th:if="${message}"> <p th:text="${message}"></p> </th:block> <form th:action="@{/edit-customer}" th:object="${customer}" method="post"> <input th:type="hidden" name="id" th:field="*{id}"> <table> <tr> <td>First name:</td> <td><input type="text" th:field="*{firstName}"/></td> </tr> <tr> <td>Last name:</td> <td><input type="text" th:field="*{lastName}"/></td> </tr> <tr> <td></td> <td><input type="submit" value="Update customer"></td> </tr> </table> </form> </body> </html>
Cập nhật file /WEB-INF/views/customer/list.html, thêm đường link đến trang edit.
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <a href="/create-customer">Create new customer</a> <h1>Customers</h1> <table border="1"> <tr> <th>First name</th> <th>Last name</th> <th>Edit</th> </tr> <th:block th:each="customer : ${customers}"> <tr> <td th:text="${customer.firstName}"></td> <td th:text="${customer.lastName}"></td> <td><a th:href="@{/edit-customer/__${customer.id}__ }">Edit</a></td> </tr> </th:block> </table> </body> </html>
Chạy ứng dụng và quan sát kết quả.
Bước 15: Tạo chức năng xoá Customer
Cập nhật CustomerController
@GetMapping("/delete-customer/{id}") public ModelAndView showDeleteForm(@PathVariable Long id){ Customer customer = customerService.findById(id); if(customer != null) { ModelAndView modelAndView = new ModelAndView("/customer/delete"); modelAndView.addObject("customer", customer); return modelAndView; }else { ModelAndView modelAndView = new ModelAndView("/error.404"); return modelAndView; } } @PostMapping("/delete-customer") public String deleteCustomer(@ModelAttribute("customer") Customer customer){ customerService.remove(customer.getId()); return "redirect:customers"; }
Tạo file /WEB-INF/views/customer/delete.html <!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Delete customer</title> </head> <body> <h1>Delete customer</h1> <h2>Are you sure?</h2> <p> <a href="/customers">Customer list</a> </p> <form th:action="@{/delete-customer}" th:object="${customer}" method="post"> <input th:type="hidden" name="id" th:field="*{id}"> <table> <tr> <td>First name:</td> <td th:text="${customer.firstName}"></td> </tr> <tr> <td>Last name:</td> <td th:text="${customer.lastName}"></td> </tr> <tr> <td></td> <td><input type="submit" value="Delete customer"></td> </tr> </table> </form> </body> </html> Cập nhật file /WEB-INF/views/customer/list.html, thêm đường link đến trang delete. <!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <a href="/create-customer">Create new customer</a> <h1>Customers</h1> <table border="1"> <tr> <th>First name</th> <th>Last name</th> <th>Edit</th> <th>Delete</th> </tr> <th:block th:each="customer : ${customers}"> <tr> <td th:text="${customer.firstName}"></td> <td th:text="${customer.lastName}"></td> <td><a th:href="@{/edit-customer/__${customer.id}__ }">Edit</a></td> <td><a th:href="@{/delete-customer/__${customer.id}__ }">Delete</a></td> </tr> </th:block> </table> </body> </html>
Chạy ứng dụng và quan sát kết quả.
Mã nguồn tham khảo: https://github.com/codegym-vn/spring-jpa-customer-management
Hướng dẫn nộp bài:
Up code lên github
Paste link github vào phần nộp bài và nhấn Submit
Trả lời