Spring Boot 3.2 + MySQL 8.0 图书馆管理系统实战指南

1. 技术栈选型与环境配置

在开始构建图书馆管理系统之前,我们需要明确技术选型并完成开发环境准备。本系统采用当前主流的技术组合:

  • 后端框架 :Spring Boot 3.2(基于Spring Framework 6)
  • 数据库 :MySQL 8.0(支持JSON类型和窗口函数)
  • ORM工具 :Spring Data JPA + Hibernate 6
  • 构建工具 :Gradle 8.2(支持Java 17+)
  • API文档 :SpringDoc OpenAPI 2.0

1.1 开发环境准备

首先确保你的开发环境满足以下要求:

# 检查Java版本
java -version
# 应输出:openjdk version "17.0.5"...

# 检查MySQL版本
mysql --version
# 应输出:mysql Ver 8.0.32...

创建Gradle项目时, build.gradle 关键依赖配置如下:

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-validation'
    runtimeOnly 'com.mysql:mysql-connector-j'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

1.2 数据库连接配置

application.yml 中配置MySQL连接:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/library_db?useSSL=false&serverTimezone=UTC
    username: library_admin
    password: secure_password
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    hibernate:
      ddl-auto: validate
    show-sql: true
    properties:
      hibernate:
        format_sql: true

注意:生产环境务必使用连接池(如HikariCP)并启用SSL加密连接

2. 数据库设计与实体建模

基于图书馆业务需求,我们设计8张核心表,以下是它们的ER关系:

表名 主要功能 关联表
book 存储图书基本信息 book_type
book_type 图书分类信息 -
borrower 借阅者个人信息 borrow_card
borrow_card 借阅证信息 -
admin 管理员信息 -
reserve_record 预借记录 book, borrow_card
borrow_record 借阅记录 book, borrow_card
overdue_record 逾期记录 borrow_record

2.1 核心实体类实现

使用JPA实体映射数据库表,以下是 Book 实体类的典型实现:

@Entity
@Table(name = "book")
@Getter
@Setter
@NoArgsConstructor
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(nullable = false, length = 100)
    private String title;
    
    @Column(nullable = false, length = 50)
    private String author;
    
    @Column(nullable = false, length = 20)
    private String isbn;
    
    @ManyToOne
    @JoinColumn(name = "type_id")
    private BookType type;
    
    @Column(nullable = false)
    private Integer totalCopies;
    
    @Column(nullable = false)
    private Integer availableCopies;
    
    @Column(nullable = false)
    private LocalDate publishDate;
    
    @Column(length = 500)
    private String description;
    
    @Version
    private Integer version;
}

实体设计时的几个关键点:

  1. 使用 @Version 实现乐观锁,防止并发修改冲突
  2. 关联关系使用 @ManyToOne / @OneToMany 等注解
  3. 字段约束通过 @Column 的nullable、length等属性设置

2.2 数据库初始化脚本

创建 schema.sql 用于初始化表结构:

CREATE TABLE IF NOT EXISTS book_type (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50) NOT NULL UNIQUE,
    description VARCHAR(200)
);

CREATE TABLE IF NOT EXISTS book (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    title VARCHAR(100) NOT NULL,
    author VARCHAR(50) NOT NULL,
    isbn VARCHAR(20) NOT NULL UNIQUE,
    type_id BIGINT NOT NULL,
    total_copies INT NOT NULL CHECK(total_copies > 0),
    available_copies INT NOT NULL CHECK(available_copies >= 0),
    publish_date DATE NOT NULL,
    description VARCHAR(500),
    FOREIGN KEY (type_id) REFERENCES book_type(id)
);

3. 核心业务逻辑实现

3.1 借阅业务流程实现

借阅业务涉及多个步骤的校验和状态变更,典型实现如下:

@Service
@RequiredArgsConstructor
@Transactional
public class BorrowService {
    private final BookRepository bookRepository;
    private final BorrowCardRepository cardRepository;
    private final BorrowRecordRepository recordRepository;
    
    public BorrowResult borrowBook(Long bookId, String cardNumber) {
        // 1. 验证借阅证有效性
        BorrowCard card = cardRepository.findByCardNumber(cardNumber)
            .orElseThrow(() -> new BusinessException("借阅证不存在"));
        
        if (!card.isActive()) {
            throw new BusinessException("借阅证已失效");
        }
        
        // 2. 检查图书可用性
        Book book = bookRepository.findById(bookId)
            .orElseThrow(() -> new BusinessException("图书不存在"));
        
        if (book.getAvailableCopies() <= 0) {
            throw new BusinessException("该图书已全部借出");
        }
        
        // 3. 检查用户借阅上限
        long borrowedCount = recordRepository.countByCardAndReturnDateIsNull(card);
        if (borrowedCount >= card.getMaxBorrowLimit()) {
            throw new BusinessException("已达到最大借阅数量限制");
        }
        
        // 4. 创建借阅记录
        BorrowRecord record = new BorrowRecord();
        record.setBook(book);
        record.setCard(card);
        record.setBorrowDate(LocalDate.now());
        record.setDueDate(LocalDate.now().plusDays(card.getBorrowDays()));
        recordRepository.save(record);
        
        // 5. 更新图书库存
        book.setAvailableCopies(book.getAvailableCopies() - 1);
        bookRepository.save(book);
        
        return new BorrowResult(record.getId(), record.getDueDate());
    }
}

3.2 归还业务实现

归还业务需要考虑逾期处理等场景:

public ReturnResult returnBook(Long recordId) {
    BorrowRecord record = recordRepository.findById(recordId)
        .orElseThrow(() -> new BusinessException("借阅记录不存在"));
    
    if (record.getReturnDate() != null) {
        throw new BusinessException("该图书已归还");
    }
    
    // 设置归还日期
    record.setReturnDate(LocalDate.now());
    recordRepository.save(record);
    
    // 更新图书库存
    Book book = record.getBook();
    book.setAvailableCopies(book.getAvailableCopies() + 1);
    bookRepository.save(book);
    
    // 检查是否逾期
    if (record.getReturnDate().isAfter(record.getDueDate())) {
        OverdueRecord overdue = createOverdueRecord(record);
        return new ReturnResult(true, overdue.getFineAmount());
    }
    
    return new ReturnResult(false, null);
}

4. RESTful API设计

4.1 控制器层实现

采用Spring MVC实现RESTful接口,以下是图书管理API示例:

@RestController
@RequestMapping("/api/books")
@RequiredArgsConstructor
public class BookController {
    private final BookService bookService;
    
    @GetMapping
    public Page<BookDTO> searchBooks(
        @RequestParam(required = false) String title,
        @RequestParam(required = false) String author,
        @RequestParam(required = false) Long typeId,
        @PageableDefault(sort = "title", direction = ASC) Pageable pageable) {
        
        BookSearchCriteria criteria = new BookSearchCriteria(title, author, typeId);
        return bookService.searchBooks(criteria, pageable);
    }
    
    @PostMapping
    @ResponseStatus(CREATED)
    public BookDTO addBook(@Valid @RequestBody BookCreationDTO dto) {
        return bookService.addBook(dto);
    }
    
    @PutMapping("/{id}")
    public BookDTO updateBook(@PathVariable Long id, @Valid @RequestBody BookUpdateDTO dto) {
        return bookService.updateBook(id, dto);
    }
    
    @DeleteMapping("/{id}")
    @ResponseStatus(NO_CONTENT)
    public void deleteBook(@PathVariable Long id) {
        bookService.deleteBook(id);
    }
}

4.2 API文档生成

使用SpringDoc OpenAPI自动生成API文档,配置示例:

@Configuration
public class OpenApiConfig {
    @Bean
    public OpenAPI libraryOpenAPI() {
        return new OpenAPI()
            .info(new Info().title("Library Management API")
                .description("API for library management system")
                .version("v1.0")
                .license(new License().name("Apache 2.0")))
            .externalDocs(new ExternalDocumentation()
                .description("Library Management Wiki")
                .url("https://example.com/docs"));
    }
}

访问 /v3/api-docs 获取JSON格式的API定义,或 /swagger-ui.html 查看交互式文档。

5. 高级功能实现

5.1 预约排队系统

当图书全部借出时,用户可以加入等待队列:

public class BookReservationService {
    private final ReservationQueueRepository queueRepository;
    
    @Transactional
    public ReservationResult reserveBook(Long bookId, String cardNumber) {
        // 检查是否已有预约记录
        boolean exists = queueRepository.existsByBookIdAndCardNumber(bookId, cardNumber);
        if (exists) {
            throw new BusinessException("您已预约该图书");
        }
        
        // 获取当前排队位置
        int queuePosition = queueRepository.countByBookId(bookId) + 1;
        
        // 创建预约记录
        ReservationQueue queue = new ReservationQueue();
        queue.setBookId(bookId);
        queue.setCardNumber(cardNumber);
        queue.setQueuePosition(queuePosition);
        queue.setReserveDate(LocalDateTime.now());
        queueRepository.save(queue);
        
        return new ReservationResult(queue.getId(), queuePosition);
    }
    
    @Scheduled(fixedRate = 3600000) // 每小时检查一次
    public void processReservationQueue() {
        // 获取可预约的图书列表
        List<Long> availableBooks = getAvailableBooks();
        
        availableBooks.forEach(bookId -> {
            // 获取队列中的第一个用户
            Optional<ReservationQueue> firstInQueue = 
                queueRepository.findFirstByBookIdOrderByReserveDateAsc(bookId);
            
            firstInQueue.ifPresent(queue -> {
                // 发送通知
                sendReservationNotification(queue.getCardNumber(), bookId);
                // 从队列中移除
                queueRepository.delete(queue);
            });
        });
    }
}

5.2 逾期计算策略

实现灵活的逾期计算规则:

public interface OverdueCalculator {
    BigDecimal calculateFine(LocalDate dueDate, LocalDate returnDate);
}

@Component
@RequiredArgsConstructor
public class SimpleOverdueCalculator implements OverdueCalculator {
    private final OverduePolicyRepository policyRepository;
    
    @Override
    public BigDecimal calculateFine(LocalDate dueDate, LocalDate returnDate) {
        if (!returnDate.isAfter(dueDate)) {
            return BigDecimal.ZERO;
        }
        
        long daysOverdue = ChronoUnit.DAYS.between(dueDate, returnDate);
        OverduePolicy policy = policyRepository.findActivePolicy()
            .orElseThrow(() -> new BusinessException("逾期政策未配置"));
        
        return policy.getDailyFine().multiply(new BigDecimal(daysOverdue));
    }
}

6. 系统安全与性能优化

6.1 安全防护措施

  1. 认证与授权 :集成Spring Security实现RBAC
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
    private final UserDetailsService userDetailsService;
    
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .csrf(AbstractHttpConfigurer::disable)
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/auth/**").permitAll()
                .requestMatchers("/api/books/**").hasAnyRole("LIBRARIAN", "ADMIN")
                .requestMatchers("/api/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
            )
            .sessionManagement(session -> 
                session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .addFilterBefore(jwtAuthFilter(), UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }
}
  1. 数据验证 :使用Bean Validation注解
public class BookCreationDTO {
    @NotBlank
    @Size(max = 100)
    private String title;
    
    @NotBlank
    @Size(max = 50)
    private String author;
    
    @ISBN
    private String isbn;
    
    @NotNull
    @Positive
    private Integer totalCopies;
    
    @NotNull
    @PastOrPresent
    private LocalDate publishDate;
}

6.2 性能优化技巧

  1. 查询优化 :使用JPA的 @EntityGraph 解决N+1问题
public interface BookRepository extends JpaRepository<Book, Long> {
    @EntityGraph(attributePaths = {"type"})
    Page<Book> findByTitleContaining(String title, Pageable pageable);
}
  1. 缓存策略 :对热点数据使用Spring Cache
@Service
@RequiredArgsConstructor
@CacheConfig(cacheNames = "books")
public class BookService {
    private final BookRepository bookRepository;
    
    @Cacheable(key = "#id")
    public BookDTO getBookById(Long id) {
        return bookRepository.findById(id)
            .map(this::convertToDTO)
            .orElseThrow(() -> new EntityNotFoundException("Book not found"));
    }
    
    @CacheEvict(key = "#id")
    public void updateBook(Long id, BookUpdateDTO dto) {
        // 更新逻辑
    }
}
  1. 批量操作 :使用JPA的批量插入
spring.jpa.properties.hibernate.jdbc.batch_size=50
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true

7. 测试策略与实践

7.1 单元测试示例

使用JUnit 5和Mockito测试服务层:

@ExtendWith(MockitoExtension.class)
class BorrowServiceTest {
    @Mock
    private BookRepository bookRepository;
    @Mock
    private BorrowCardRepository cardRepository;
    @InjectMocks
    private BorrowService borrowService;
    
    @Test
    void shouldThrowWhenBookNotAvailable() {
        // 准备测试数据
        Book book = new Book();
        book.setAvailableCopies(0);
        
        when(bookRepository.findById(anyLong()))
            .thenReturn(Optional.of(book));
        
        // 验证异常抛出
        assertThrows(BusinessException.class, 
            () -> borrowService.borrowBook(1L, "CARD123"));
    }
}

7.2 集成测试配置

使用 @SpringBootTest 进行集成测试:

@SpringBootTest
@AutoConfigureMockMvc
class BookControllerIT {
    @Autowired
    private MockMvc mockMvc;
    
    @Test
    void shouldReturn404WhenBookNotFound() throws Exception {
        mockMvc.perform(get("/api/books/999"))
            .andExpect(status().isNotFound());
    }
}

测试数据准备可以使用 @Sql 注解:

@Test
@Sql(scripts = "/test-data/books.sql")
void shouldReturnBookWhenExists() throws Exception {
    mockMvc.perform(get("/api/books/1"))
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.title").value("Effective Java"));
}

8. 部署与监控

8.1 Docker化部署

创建 Dockerfile 构建镜像:

FROM eclipse-temurin:17-jdk-jammy
WORKDIR /app
COPY build/libs/library-management-*.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]

使用Docker Compose编排服务:

version: '3.8'
services:
  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/library_db
      SPRING_DATASOURCE_USERNAME: library_admin
    depends_on:
      mysql:
        condition: service_healthy
  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
      MYSQL_DATABASE: library_db
      MYSQL_USER: library_admin
      MYSQL_PASSWORD: secure_password
    ports:
      - "3306:3306"
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 5s
      timeout: 10s
      retries: 5

8.2 监控与指标

集成Spring Boot Actuator监控端点:

management:
  endpoints:
    web:
      exposure:
        include: health, metrics, prometheus
  metrics:
    export:
      prometheus:
        enabled: true

关键监控指标包括:

  • 数据库连接池使用情况
  • HTTP请求延迟和错误率
  • JVM内存和GC情况
  • 业务指标(如每日借阅量)

9. 项目扩展方向

9.1 微服务化改造

当系统规模扩大时,可考虑拆分为以下服务:

  1. 用户服务 :管理借阅者和管理员账户
  2. 目录服务 :管理图书元数据
  3. 借阅服务 :处理借阅、归还等业务流程
  4. 通知服务 :发送预约到货、逾期提醒等通知

9.2 前端技术选型建议

现代前端技术栈组合推荐:

  • 框架 :React/Vue 3
  • UI库 :Ant Design/Element Plus
  • 状态管理 :Redux Toolkit/Pinia
  • 构建工具 :Vite

9.3 智能化功能扩展

  1. 推荐系统 :基于用户借阅历史的协同过滤推荐
  2. 逾期预测 :使用机器学习预测可能的逾期用户
  3. 智能排架 :优化图书物理存放位置

在实际项目中,我们采用了分阶段实现策略,先确保核心借阅流程的稳定性,再逐步添加高级功能。对于数据一致性要求高的操作,使用 @Transactional 确保原子性,并通过重试机制处理乐观锁冲突。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐