Практика системы управления библиотекой на основе Spring Boot и JPA

Оглавление

Предисловие

Выбор технологии

Функциональные требования

Системный дизайн

Дисплей интерфейса

Этапы реализации 

1. Создайте проект Spring Boot.

2. Многоуровневое кодирование проекта

3. Проектируйте и создавайте классы сущностей

4. Создайте уровень доступа к данным (репозиторий).

5. Создайте сервисный слой (Service)

5. Создаем контроллер (Контроллер) 

 6. Создайте шаблоны страниц.

7. Настройте подключение к базе данных

8. Запустите автозагрузочное приложение.

Подведем итог


Предисловие

В таких местах, как библиотеки и книжные магазины, важной задачей является управление большим количеством книг. Чтобы повысить эффективность и точность управления библиотекой, мы можем разработать систему управления библиотекой на основе Spring Boot и JPA. В этом блоге будет описано, как использовать эти технологии для разработки и внедрения полнофункциональной системы управления библиотекой.


Выбор технологии

В этом проекте используются следующие технологии:

  • Spring Boot: быстрое создание приложений на основе Spring.
  • JPA: API персистентности Java упрощает операции по сохранению данных
  • Thymeleaf: движок шаблонов для рендеринга внешних страниц.
  • MySQL: реляционная база данных, используемая для хранения данных, связанных с книгами.
  • Bootstrap: для дизайна и верстки страниц.

Функциональные требования

Наша система управления библиотекой должна реализовывать следующие функции:

  1. Управление информацией о книге: включая добавление, удаление, изменение и запрос названия книги, автора, издателя и других атрибутов.
  2. Добавить книги: добавьте в систему информацию о новой книге.
  3. Удалить книгу: удалить указанную информацию о книге из системы.
  4. Обновить книги: изменить существующую информацию о книгах в системе.
  5. Поиск книг: поиск книг, соответствующих требованиям, по ключевым словам или другим условиям.
  6. Функция пейджинга: отображение информации о нескольких книгах на страницах для повышения эффективности просмотра.
  7. Функция сортировки: сортируйте информацию о книге по различным условиям для облегчения поиска и сравнения.

Системный дизайн

Мы храним информацию о книгах в базе данных MySQL и используем JPA для управления операциями с базой данных. Создавая классы сущностей и соответствующие интерфейсы репозитория, мы можем легко выполнять операции добавления, удаления, изменения и запроса. В то же время механизм шаблонов Thymeleaf используется для рендеринга интерфейсной страницы и отображения информации о книге.


Дисплей интерфейса


Этапы реализации 

1. Создайте проект Spring Boot.

Создайте новый проект Spring Boot с помощью Spring Initializr (https://start.spring.io/).

Добавьте необходимые зависимости, включая Spring Web, Spring Data JPA, Thymeleaf, драйвер MySQL и т. д.

2. Многоуровневое кодирование проекта

3. Проектируйте и создавайте классы сущностей

Создайте класс сущности Book, включая атрибуты id, title,author,PublishDate и CategoryId, и используйте аннотации сопоставления сущностей и таблиц.

@NoArgsConstructor
@AllArgsConstructor
@Data
@Entity
@Table(name = "books")
public class Book {

    // 表示该字段是实体类的主键
    @Id
    // 表示该主键的生成策略是自增长的方式生成。
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    private String title;

    private String author;

    // 定义一个ManyToOne(多对一)的关联关系
    @ManyToOne
    private Category category;

    @Column(name = "publish_date")
    private YearMonth publishDate;

}

Создайте класс сущности Category, включая атрибуты id и name, и используйте аннотации @Entity и @Table в классе для сопоставления сущностей и таблиц.

@NoArgsConstructor
@AllArgsConstructor
@Data
@Entity
@Table(name = "categories")
public class Category {

    // 表示该字段是实体类的主键
    @Id
    // 表示该主键的生成策略是自增长的方式生成。
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    private String name;

    // 这是一个一对多的关系,即一个类别对应多个图书
    @OneToMany(mappedBy = "category")
    private List<Book> books;

}

4. Создайте уровень доступа к данным (репозиторий).

Создайте интерфейс BookRepository, наследуйте JpaRepository и используйте аннотацию @Repository для идентификации.

@Repository
public interface BookRepository extends JpaRepository<Book, Long> {
    // 定义了两个方法用于实现根据关键字进行标题和作者模糊搜索,并返回一个带有分页结果的Page对象
    Page<Book> findByTitleContaining(String keyword, Pageable pageable);
    Page<Book> findByAuthorContaining(String keyword, Pageable pageable);
}

Создайте интерфейс CategoryRepository, наследуйте JpaRepository и используйте аннотацию @Repository для идентификации.

@Repository
public interface CategoryRepository extends JpaRepository<Category, Long> {

}

5. Создайте сервисный слой (Service)

Создайте интерфейс CategoryService и определите методы для добавления, удаления, изменения и запроса информации о классификации.

public interface BookService {
    // 获取所有图书的方法
    List<Book> getAllBooks();

    // 接受一个  `Book`  类型的参数,并将其保存
    void saveBook(Book book);

    // 通过图书ID获取图书的方法
    Book getBookById(long id);

    // 通过图书ID删除图书的方法
    void deleteBookById(long id);

    // 根据关键字和搜索条件进行分页搜索图书的方法
    Page<Book> searchBooks(String keyword, String searchBy, Pageable pageable);

    // 分页和排序
    Page<Book> findPaginated(int pageNo, int pageSize,StringsortField,StringsortDirection);

}

Создайте класс CategoryServiceImpl, реализуйте интерфейс CategoryService и используйте аннотацию @Service для идентификации.

Вставьте CategoryRepository в CategoryServiceImpl и реализуйте методы, определенные интерфейсом.

@Service
public class BookServiceImpl implements BookService {

    @Autowired
    private BookRepository bookRepository;

    @Override
    // 从数据库中获取所有图书的列表
    public List<Book> getAllBooks() {
        return bookRepository.findAll();
    }

    @Override
    // 将图书对象保存到数据库中
    public void saveBook(Book book) {
        bookRepository.save(book);
    }

    @Override
    // 根据id查找并返回对应的图书对象。如果找不到对应的图书,则返回`null`
    public Book getBookById(long id) {
        return bookRepository.findById(id).orElse(null);
    }

    @Override
    // 根据id从数据库中删除对应的图书
    public void deleteBookById(long id) {
        bookRepository.deleteById(id);
    }

    @Override
    // 在数据库中根据标题或作者进行模糊查找,并返回一个分页的结果
    public Page<Book> searchBooks(String keyword, String searchBy, Pageable pageable) {
        switch (searchBy) {
            case "title":
                return bookRepository.findByTitleContaining(keyword, pageable);
            case "author":
                return bookRepository.findByAuthorContaining(keyword, pageable);
            default:
                throw new IllegalArgumentException("Invalid searchBy parameter");
        }
    }

    // 分页查询图书列表
    @Override
    public Page<Book> findPaginated(int pageNo, int pageSize, String sortField, String sortDirection) {
        Sort sort = sortDirection.equals("asc") ? Sort.by(sortField).ascending() : Sort.by(sortField).descending();
        PageRequest pageable = PageRequest.of(pageNo - 1, pageSize, sort);
        return bookRepository.findAll(pageable);
    }
}

Создайте интерфейс BookService и определите методы для добавления, удаления, изменения и постраничного запроса информации о книге.

public interface CategoryService {
    // 获取所有分类的信息,并以列表的形式返回
    List<Category> getAllCategories();
    // 保存一个分类的信息
    void saveCategory(Category category);
    // 根据分类的ID获取对应的分类信息
    Category getCategoryById(long id);
    // 根据分类的ID删除对应的分类信息
    void deleteCategoryById(long id);
    // 根据分类的ID获取该分类下的图书,并根据分页设置进行分页处理,并以页面对象的形式返回
    Page<Book> getBooksByCategoryId(long categoryId, Pageable pageable);
    
}

Создайте класс BookServiceImpl, реализуйте интерфейс BookService и используйте аннотацию @Service для идентификации.

Вставьте CategoryRepository и BookRepository в BookServiceImpl и реализуйте методы, определенные интерфейсом.

@Repository
public class CategoryServiceImpl implements CategoryService {

    @Autowired
    private CategoryRepository categoryRepository;

    @Override
    // 获取数据库中的所有Category记录,然后返回一个Category列表
    public List<Category> getAllCategories() {
        return categoryRepository.findAll();
    }

    @Override
    // 将传入的Category对象保存到数据库中
    public void saveCategory(Category category) {
        categoryRepository.save(category);
    }

    @Override
    // 通过调用categoryRepository的findById()方法查询对应的Category记录,并返回该记录。如果查询不到对应的记录,则返回null
    public Category getCategoryById(long id) {
        return categoryRepository.findById(id).orElse(null);
    }

    @Override
    // 通过调用categoryRepository的deleteById()方法删除对应的Category记录
    public void deleteCategoryById(long id) {
        categoryRepository.deleteById(id);
    }

    @Override
    // 如果查到了该记录,则获取其关联的Book列表,并根据传入的分页参数进行分页处理,最后返回一个Page<Book>对象。如果未查到对应的Category记录,则返回一个空的Page对象
    public Page<Book> getBooksByCategoryId(long categoryId, Pageable pageable) {
        Optional<Category> categoryOptional = categoryRepository.findById(categoryId);
        if (categoryOptional.isPresent()) {
            Category category = categoryOptional.get();
            List<Book> books = category.getBooks();

            // 创建一个分页请求
            int pageSize = pageable.getPageSize();
            int currentPage = pageable.getPageNumber();
            int startItem = currentPage * pageSize;

            List<Book> pagedBooks;
            if (books.size() < startItem) {
                pagedBooks = Collections.emptyList();
            } else {
                int toIndex = Math.min(startItem + pageSize, books.size());
                pagedBooks = books.subList(startItem, toIndex);
            }

            return new PageImpl<>(pagedBooks, pageable, books.size());
        } else {
            return Page.empty();
        }
    }

}

5. Создаем контроллер (Контроллер) 

Создайте класс CategoryController и используйте аннотацию @Controller для его идентификации.

Внедрите CategoryService в CategoryController и реализуйте методы обработки HTTP-запросов, связанные с классификацией, включая запрос всех категорий, добавление категорий, запрос категорий, удаление категорий и т. д.

@Controller
@RequestMapping("/categories")
public class CategoryController {

    @Autowired
    private CategoryServiceImpl categoryRepository;

    @GetMapping("/list")
    public String getAllCategories(Model model) {
        List<Category> categories = categoryRepository.getAllCategories();
        model.addAttribute("categories", categories);
        return "category-list";
    }

    @GetMapping("/add")
    public String showAddCategoryForm(Model model) {
        Category category = new Category();
        model.addAttribute("category", category);
        return "add-category";
    }

    @PostMapping("/add")
    public String addCategory(@ModelAttribute("category") Category category) {
        categoryRepository.saveCategory(category);
        return "redirect:/categories/list";
    }

    @GetMapping("/edit/{id}")
    public String showEditCategoryForm(@PathVariable long id, Model model) {
        Category category = categoryRepository.getCategoryById(id);
        model.addAttribute("category", category);
        return "edit-category";
    }

    @PostMapping("/edit/{id}")
    public String editCategory(@PathVariable long id, @ModelAttribute("category") Category category) {
        category.setId(id);
        categoryRepository.saveCategory(category);
        return "redirect:/categories/list";
    }

    @GetMapping("/delete/{id}")
    public String deleteCategory(@PathVariable long id) {
        categoryRepository.deleteCategoryById(id);
        return "redirect:/categories/list";
    }

    @GetMapping("/{categoryId}/books")
    public String getBooksByCategoryId(@PathVariable long categoryId,
                                       @RequestParam(value = "page", defaultValue = "1") int page,
                                       @RequestParam(value = "pageSize", defaultValue = "999") int pageSize,
                                       Model model) {
        Page<Book> bookPage = categoryRepository.getBooksByCategoryId(categoryId, PageRequest.of(page - 1, pageSize));
        List<Book> books = bookPage.getContent();

        model.addAttribute("books", books);
        model.addAttribute("totalPages", bookPage.getTotalPages());
        model.addAttribute("totalItems", bookPage.getTotalElements());
        model.addAttribute("currentPage", page);

        return "book-list";
    }

}

Создайте класс BookController и используйте аннотацию @Controller для его идентификации.

Внедрите BookService в BookController и реализуйте методы обработки HTTP-запросов, связанные с книгами, включая такие операции, как запрос книг, добавление книг и запрос книг по категориям.

@Controller
@RequestMapping("/books")
public class BookController {

    @Autowired
    private BookService bookService;

    @Autowired
    private CategoryService categoryService;

    @GetMapping("/list")
    public String getAllBooks(Model model) {
        return findPaginated(1, 5,"id","desc", model);
    }

    @GetMapping("/add")
    public String showAddBookForm(Model model) {
        Book book = new Book();
        model.addAttribute("book", book);

        // 从数据库或其他数据源获取分类列表
        List<Category> categories = categoryService.getAllCategories();
        // 将分类列表传递给前端模板
        model.addAttribute("categories", categories);
        return "add-book";
    }

    @PostMapping("/add")
    public String addBook(@ModelAttribute("book") Book book) {
        bookService.saveBook(book);
        return "redirect:/books/list";
    }

    @GetMapping("/edit/{id}")
    public String showEditBookForm(@PathVariable long id, Model model) {
        Book book = bookService.getBookById(id);
        model.addAttribute("book", book);
        // 从数据库或其他数据源获取分类列表
        List<Category> categories = categoryService.getAllCategories();
        // 将分类列表传递给前端模板
        model.addAttribute("categories", categories);
        return "edit-book";
    }

    @PostMapping("/edit/{id}")
    public String editBook(@PathVariable long id, @ModelAttribute("book") Book book) {
        book.setId(id);
        bookService.saveBook(book);
        return "redirect:/books/list";
    }

    @GetMapping("/delete/{id}")
    public String deleteBook(@PathVariable long id) {
        bookService.deleteBookById(id);
        return "redirect:/books/list";
    }


    @PostMapping("/search")
    public String searchBooks(@RequestParam("keyword") String keyword,
                              @RequestParam("searchBy") String searchBy,
                              @PageableDefault(size = 999, sort = "id") Pageable pageable,
                              Model model) {
        Page<Book> bookPage = bookService.searchBooks(keyword, searchBy, pageable);
        model.addAttribute("books", bookPage.getContent());
        model.addAttribute("totalItems", bookPage.getTotalElements());
        model.addAttribute("currentPage", bookPage.getNumber() + 1);
        model.addAttribute("totalPages", bookPage.getTotalPages());
        return "book-list";
    }

    @GetMapping("/page/{pageNo}")
    public String findPaginated(@PathVariable(value = "pageNo") int pageNo,
                                @RequestParam("pageSize") int pageSize,
                                @RequestParam("sortField") String sortField,
                                @RequestParam("sortDir") String sortDir,
                                Model model) {
        Page<Book> page = bookService.findPaginated(pageNo, pageSize, sortField, sortDir);
        List<Book> books = page.getContent();
        model.addAttribute("currentPage", pageNo);
        model.addAttribute("totalPages", page.getTotalPages());
        model.addAttribute("totalItems", page.getTotalElements());
        model.addAttribute("books", books);
        model.addAttribute("sortField", sortField);
        model.addAttribute("sortDir", sortDir);
        return "book-list";
    }

}

 6. Создайте шаблоны страниц.

Используйте Thymeleaf или другие шаблонизаторы для создания шаблонов HTML-страниц, включая списки категорий, списки книг, формы для добавления/редактирования категорий, формы для добавления/редактирования книг и т. д.

7. Настройте подключение к базе данных

Настройте информацию, связанную с подключением к базе данных, в файле application.properties (или yml), например URL-адрес базы данных, имя пользователя и пароль.

spring.datasource.url=jdbc:mysql://localhost:3306/librarydb?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
spring.datasource.username= root
spring.datasource.password= 123456

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# for Spring Boot 2
# spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.MySQL5InnoDBDialect

# for Spring Boot 3
spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.MySQLDialect

# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto= update

logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type=TRACE

8. Запустите автозагрузочное приложение.

Запустите приложение, получите доступ к соответствующему URL-адресу и выполните операции классификации и управления книгами.


Подведем итог

В последнем проекте этого семестра у меня была возможность самостоятельно завершить веб- проект, что было для меня очень ценным и интересным опытом. Благодаря этому проекту я не только изучил правильные стандарты кодирования, но также освоил процесс работы Git и навыки управления кодом в командной работе.

Кроме того, я также глубоко изучил структуру данных Java , двоичный ввод-вывод , многопоточность и потоковое программирование, что дало мне прочную основу для написания более эффективных, параллельных и гибких программ.

Наконец, учитель научил методу многоуровневой разработки проектов, и я решил использовать Spring Boot , JPA , Thymeleaf , MySQL и Bootstrap для создания своего веб- проекта. Такое сочетание технологий позволило мне легко реализовать все аспекты проекта, включая сохранение данных, обработку бизнес-логики, интерфейсную презентацию и взаимодействие с пользователем.

Благодаря этому независимо завершенному итоговому проекту я не только углубил свое понимание и применение технических знаний, но также улучшил свои способности решать проблемы и развиваться самостоятельно. Это окажет положительное влияние на мое дальнейшее карьерное развитие.

Я очень доволен результатами обучения в этом семестре и надеюсь продолжить расширять свои технические знания и опыт реализации проектов в будущем.

Supongo que te gusta

Origin blog.csdn.net/m0_69496245/article/details/131546393
Recomendado
Clasificación