Spring MVC CRUD 与示例
Spring MVC 是一个用于构建 Web 应用程序的 Web MVC 框架。它是一个与 spring boot、spring-security 等相同的 spring 模块。术语 MVC 代表 Model-View-Controller 架构。在本文中,我们将构建一个简单的课程跟踪 CRUD 应用程序,该应用程序将专注于 Spring MVC 模块。 CRUD 代表创建、读取、更新和删除。
项目结构
依赖项
如果还没有,请将以下依赖项添加到build.gradle文件中。
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.projectlombok:lombok:1.18.20'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.h2database:h2'
runtimeOnly 'mysql:mysql-connector-java'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
模型层
使用一些 JPA 和 Lombok 注释创建一个简单的 POJO(普通旧Java对象)。
- @NoArgsConstructor –此注解生成不带参数的构造函数。
- @AllArgsConstructor –此注释生成具有所有字段参数的构造函数。
- @Data –此注解生成 getter、setter、toString、必需的参数构造函数、equals 和 hashCode。
- @Entity——这个注解定义了一个类可以映射到一个表。
- @Table –此注释指定用于映射的数据库表的名称。
- @Id –此注释标记实体的主键。
- @GeneratedValue –此注解提供了主键值的生成策略规范。
- @Column –此注释标记表中该特定属性的列名。
Java
import lombok.*;
import javax.persistence.*;
@NoArgsConstructor
@AllArgsConstructor
@Data
@Entity
@Table(name = "courses")
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "course_name")
private String courseName;
@Column(name = "instructor")
private String instructor;
@Column(name = "email")
private String email;
}
Java
import com.example.testing_001.model.Course;
import org.springframework.data.jpa.repository.JpaRepository;
@Repository
public interface CourseRepository extends JpaRepository {
}
Java
import com.example.testing_001.model.Course;
import java.util.List;
import org.springframework.data.domain.Page;
public interface CourseService {
List getAllCourses();
void saveCourse(Course course);
Course getCourseById(long id);
void deleteCourseById(long id);
Page findPaginated(int pageNum, int pageSize,
String sortField,
String sortDirection);
}
Java
import com.example.testing_001.model.Course;
import com.example.testing_001.repository.CourseRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
@Service
public class CourseServiceImpl implements CourseService{
@Autowired
private CourseRepository courseRepository;
@Override
public List getAllCourses() {
return courseRepository.findAll();
}
@Override
public void saveCourse(Course course) {
this.courseRepository.save(course);
}
@Override
public Course getCourseById(long id) {
Optional optionalCourse = courseRepository.findById(id);
Course course = null;
if (optionalCourse.isPresent()) {
course = optionalCourse.get();
} else {
throw new RuntimeException("Course not found for id : " + id);
}
return course;
}
@Override
public void deleteCourseById(long id) {
this.courseRepository.deleteById(id);
}
@Override
public Page findPaginated(int pageNum, int pageSize, String sortField, String sortDirection) {
Sort sort = sortDirection.equalsIgnoreCase(Sort.Direction.ASC.name()) ? Sort.by(sortField).ascending() :
Sort.by(sortField).descending();
Pageable pageable = PageRequest.of(pageNum - 1, pageSize, sort);
return this.courseRepository.findAll(pageable);
}
}
Java
import com.example.testing_001.model.Course;
import com.example.testing_001.service.CourseService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Controller
public class CourseController {
@Autowired
private CourseService courseService;
@GetMapping("/")
public String viewHomePage(Model model) {
return findPaginated(1, "courseName", "asc", model);
}
@GetMapping("/add")
public String showNewCourseForm(Model model) {
Course Course = new Course();
model.addAttribute("course", Course);
return "new_course";
}
@PostMapping("/save")
public String saveCourse(@ModelAttribute("course") Course course) {
// save Course to database
courseService.saveCourse(course);
return "redirect:/";
}
@GetMapping("/update/{id}")
public String showFormForUpdate(@PathVariable( value = "id") long id, Model model) {
Course course = courseService.getCourseById(id);
model.addAttribute("course", course);
return "update_course";
}
@GetMapping("/delete/{id}")
public String deleteCourse(@PathVariable (value = "id") long id) {
this.courseService.deleteCourseById(id);
return "redirect:/";
}
@GetMapping("/page/{pageNo}")
public String findPaginated(@PathVariable (value = "pageNo") int pageNo,
@RequestParam("sortField") String sortField,
@RequestParam("sortDir") String sortDir,
Model model) {
int pageSize = 5;
Page page = courseService.findPaginated(pageNo, pageSize, sortField, sortDir);
List listCourses = page.getContent();
model.addAttribute("currentPage", pageNo);
model.addAttribute("totalPages", page.getTotalPages());
model.addAttribute("totalItems", page.getTotalElements());
model.addAttribute("sortField", sortField);
model.addAttribute("sortDir", sortDir);
model.addAttribute("reverseSortDir", sortDir.equals("asc") ? "desc" : "asc");
model.addAttribute("listCourses", listCourses);
return "index";
}
}
HTML
Course Tracker
HTML
Course
HTML
Course Tracker
DAO(数据访问对象)/存储库层
- @Repository:这个注解是一个标记存储库类的构造型。它表明带注释的类充当可以在其上进行 CRUD 操作的数据库。
- JpaRepository< Course, Long > : JpaRepository 是存储库的特定于 JPA 的扩展。它包含 CrudRepository 和 PagingAndSortingRepository 的完整 API。因此它包含用于基本 CRUD 操作的 API 以及用于分页和排序的 API。在这里,我们为员工启用数据库操作。要了解有关 Spring Data Jpa 的更多信息,请参阅本文。
Java
import com.example.testing_001.model.Course;
import org.springframework.data.jpa.repository.JpaRepository;
@Repository
public interface CourseRepository extends JpaRepository {
}
服务层
服务层提供了对数据层的抽象。其主要目的是满足业务需求。将业务逻辑与应用程序逻辑分开总是更好。
Java
import com.example.testing_001.model.Course;
import java.util.List;
import org.springframework.data.domain.Page;
public interface CourseService {
List getAllCourses();
void saveCourse(Course course);
Course getCourseById(long id);
void deleteCourseById(long id);
Page findPaginated(int pageNum, int pageSize,
String sortField,
String sortDirection);
}
类(如下所述)CourseServiceImpl 实现了 CourseService 接口,并为我们提供了所有 CRUD 操作逻辑,也就是我们这里的业务逻辑。
使用的注释:
- @Service –此注解是一个原型,用于在服务层注解类。
- @Autowired –此注解用于将一个 bean 自动装配到另一个 bean。
Java
import com.example.testing_001.model.Course;
import com.example.testing_001.repository.CourseRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
@Service
public class CourseServiceImpl implements CourseService{
@Autowired
private CourseRepository courseRepository;
@Override
public List getAllCourses() {
return courseRepository.findAll();
}
@Override
public void saveCourse(Course course) {
this.courseRepository.save(course);
}
@Override
public Course getCourseById(long id) {
Optional optionalCourse = courseRepository.findById(id);
Course course = null;
if (optionalCourse.isPresent()) {
course = optionalCourse.get();
} else {
throw new RuntimeException("Course not found for id : " + id);
}
return course;
}
@Override
public void deleteCourseById(long id) {
this.courseRepository.deleteById(id);
}
@Override
public Page findPaginated(int pageNum, int pageSize, String sortField, String sortDirection) {
Sort sort = sortDirection.equalsIgnoreCase(Sort.Direction.ASC.name()) ? Sort.by(sortField).ascending() :
Sort.by(sortField).descending();
Pageable pageable = PageRequest.of(pageNum - 1, pageSize, sort);
return this.courseRepository.findAll(pageable);
}
}
控制器层
在 Spring MVC 中,控制器层是用于处理 Web 请求的架构的顶层。然后根据请求的类型,将其传递给相应的层。
使用的注释:
- @Controller –此注释标记特定类充当控制器的角色。
- @GetMapping –此注释标记 HTTP GET 请求到特定处理程序方法的映射。
- @PostMapping –此注释标记 HTTP POST 请求到特定方法处理程序的映射。
Java
import com.example.testing_001.model.Course;
import com.example.testing_001.service.CourseService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Controller
public class CourseController {
@Autowired
private CourseService courseService;
@GetMapping("/")
public String viewHomePage(Model model) {
return findPaginated(1, "courseName", "asc", model);
}
@GetMapping("/add")
public String showNewCourseForm(Model model) {
Course Course = new Course();
model.addAttribute("course", Course);
return "new_course";
}
@PostMapping("/save")
public String saveCourse(@ModelAttribute("course") Course course) {
// save Course to database
courseService.saveCourse(course);
return "redirect:/";
}
@GetMapping("/update/{id}")
public String showFormForUpdate(@PathVariable( value = "id") long id, Model model) {
Course course = courseService.getCourseById(id);
model.addAttribute("course", course);
return "update_course";
}
@GetMapping("/delete/{id}")
public String deleteCourse(@PathVariable (value = "id") long id) {
this.courseService.deleteCourseById(id);
return "redirect:/";
}
@GetMapping("/page/{pageNo}")
public String findPaginated(@PathVariable (value = "pageNo") int pageNo,
@RequestParam("sortField") String sortField,
@RequestParam("sortDir") String sortDir,
Model model) {
int pageSize = 5;
Page page = courseService.findPaginated(pageNo, pageSize, sortField, sortDir);
List listCourses = page.getContent();
model.addAttribute("currentPage", pageNo);
model.addAttribute("totalPages", page.getTotalPages());
model.addAttribute("totalItems", page.getTotalElements());
model.addAttribute("sortField", sortField);
model.addAttribute("sortDir", sortDir);
model.addAttribute("reverseSortDir", sortDir.equals("asc") ? "desc" : "asc");
model.addAttribute("listCourses", listCourses);
return "index";
}
}
TRY: Change the GET mapping for course update and delete course to PUT mapping and DELETE mapping respectively. It is considered best practice to use mappings based on the functionality of the api.
HTML 模板
我们使用 thymleaf 作为我们的模板引擎,而不是传统的 jsps。
主页
HTML
Course Tracker
添加课程页面
HTML
Course
更新课程页面
HTML
Course Tracker
CRUD 操作
创建课程
阅读课程
更新课程
课程更新成功
删除课程