目次
序文
図書館や書店などでは、大量の書籍を管理することが重要な仕事です。ライブラリ管理の効率と精度を向上させるために、Spring Boot と JPA をベースにしたライブラリ管理システムを開発できます。このブログでは、これらのテクノロジを使用して、完全に機能するライブラリ管理システムを設計および実装する方法について説明します。
テクノロジーの選択
このプロジェクトでは次のテクノロジーが使用されています。
- Spring Boot: Spring ベースのアプリケーションを迅速に構築する
- JPA: Java 永続性 API によりデータ永続化操作が簡素化されます
- Thymeleaf: フロントエンド ページ レンダリング用のテンプレート エンジン
- MySQL: 書籍関連データの保存に使用されるリレーショナル データベース
- ブートストラップ: ページのデザインとレイアウト用
機能要件
ライブラリ管理システムには次の機能を実装する必要があります。
- 書籍情報の管理:書籍のタイトル、著者、発行者、その他の属性の追加、削除、変更、および照会を含む
- 書籍の追加: 新しい書籍情報をシステムに追加します。
- 書籍の削除:指定した書籍情報をシステムから削除します。
- 書籍の更新: システム内の既存の書籍情報を変更します。
- 本を探す:キーワードなどの条件から条件に合った本を検索します。
- ページング機能:複数の書籍情報をページ内に表示して閲覧効率を向上
- ソート機能:書籍情報をさまざまな条件で分類し、検索や比較を容易にします。
システムデザイン
私たちは書籍情報を MySQL データベースに保存し、JPA を使用してデータベース操作を管理します。エンティティ クラスと対応するリポジトリ インターフェイスを作成することで、追加、削除、変更、クエリ操作を簡単に実行できます。同時に、Thymeleaf テンプレート エンジンを使用してフロントエンド ページをレンダリングし、書籍情報を表示します。
インターフェース表示
実装手順
1. Spring Boot プロジェクトを作成する
Spring Initializr (https://start.spring.io/) を使用して、新しい Spring Boot プロジェクトを作成します。
Spring Web、Spring Data JPA、Thymeleaf、MySQL ドライバーなどの必要な依存関係を追加します。
2. プロジェクトコードの階層化
3. エンティティクラスの設計と作成
id、title、author、publishDate、categoryId 属性を含む Book エンティティ クラスを作成し、エンティティとテーブル マッピング アノテーションを使用します。
@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;
}
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.コントローラー(Controller)の作成
CategoryController クラスを作成し、 @Controller アノテーションを使用してそれを識別します。
カテゴリサービスを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. データベース接続の構成
データベース URL、ユーザー名、パスワードなどのデータベース接続関連の情報を application.properties (または yml) ファイルで構成します。
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にアクセスし、分類・書籍管理の操作を行います。
要約する
今学期最後のプロジェクトでは、単独でWebプロジェクトを完成させる機会に恵まれ、とても貴重でやりがいのある経験をさせていただきました。このプロジェクトを通じて、私は正しいコーディング標準を学んだだけでなく、チームコラボレーションにおけるGit の運用プロセスとコード管理スキルも習得しました。
さらに、Javaのデータ構造、バイナリIO 、マルチスレッド、ストリーム プログラミングについても深く学び、より効率的で同時実行性のある柔軟なプログラムを作成するための強力な基盤を提供しました。
最後に、先生は階層化されたプロジェクト開発方法を教え、私はSpring Boot 、JPA 、Thymeleaf 、MySQL 、およびBootstrapを使用してWebプロジェクトを構築することにしました。このテクノロジーの組み合わせにより、データの永続化、ビジネス ロジック処理、フロントエンド プレゼンテーション、ユーザー インタラクションなど、プロジェクトのあらゆる側面を簡単に実装できるようになりました。
自主的に完成させた最終プロジェクトを通じて、技術知識の理解と応用が深まっただけでなく、問題解決能力や自主開発能力も向上しました。これは私の将来のキャリア開発に良い影響を与えるでしょう。
私は今学期の学習成果に非常に満足しており、今後も技術的な知識とプロジェクトの経験を拡大し続けることを楽しみにしています。