Java后端开发标准流程:从数据库到接口的完整实现

前言

在实际的Java后端开发中,我们通常会遵循一个标准的开发流程,确保代码结构清晰、层次分明、易于维护。本文将以一个图书管理系统为例,详细介绍从数据库设计到接口实现的完整流程。

完整开发流程

复制代码
第1步:创建数据表
       ↓
第2步:创建Entity实体类
       ↓
第3步:创建DTO对象
       ↓
第4步:创建Repository数据访问层
       ↓
第5步:创建Service业务逻辑层
       ↓
第6步:创建Controller控制器层
       ↓
第7步:创建SQL配置类(可选,复杂查询使用)

第1步:创建数据表

首先在数据库中创建表结构,这是整个系统的数据基础。

复制代码
-- 图书表
CREATE TABLE book (
    id VARCHAR(32) PRIMARY KEY,
    book_name VARCHAR(100) NOT NULL,
    author VARCHAR(50),
    isbn VARCHAR(20) UNIQUE,
    price DECIMAL(10, 2),
    stock INT DEFAULT 0,
    publish_date DATE,
    deleted INT DEFAULT 0,
    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 图书借阅记录表
CREATE TABLE book_borrow (
    id VARCHAR(32) PRIMARY KEY,
    book_id VARCHAR(32),
    reader_name VARCHAR(50),
    borrow_date DATE,
    return_date DATE,
    status INT DEFAULT 0,  -- 0-借阅中 1-已归还
    deleted INT DEFAULT 0,
    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (book_id) REFERENCES book(id)
);

设计要点:

  • 每个表都有主键 id
  • 使用 deleted 字段实现逻辑删除(0-正常 1-删除)
  • 使用 create_timeupdate_time 记录时间
  • 外键关联保持数据完整性

第2步:创建Entity实体类

Entity类对应数据库表结构,用于ORM映射。

复制代码
package com.example.library.entity;

import lombok.Data;
import javax.persistence.*;
import java.math.BigDecimal;
import java.util.Date;

/**
 * 图书实体类
 * 对应数据库表:book
 */
@Data
@Entity
@Table(name = "book")
public class Book {
    
    @Id
    @GeneratedValue(generator = "uuid")
    @GenericGenerator(name = "uuid", strategy = "uuid")
    @Column(name = "id", nullable = false)
    private String id;
    
    @Column(name = "book_name", nullable = false)
    private String bookName;
    
    @Column(name = "author")
    private String author;
    
    @Column(name = "isbn", unique = true)
    private String isbn;
    
    @Column(name = "price")
    private BigDecimal price;
    
    @Column(name = "stock")
    private Integer stock;
    
    @Column(name = "publish_date")
    private Date publishDate;
    
    @Column(name = "deleted")
    private Integer deleted;
    
    @Column(name = "create_time")
    private Date createTime;
    
    @Column(name = "update_time")
    private Date updateTime;
}

Entity的作用:

  • 与数据库表一一对应
  • 负责数据的存储和读取
  • 不包含业务逻辑

第3步:创建DTO对象

DTO(Data Transfer Object)用于前后端数据传输,与Entity分离。

3.1 请求DTO(接收前端参数)

复制代码
package com.example.library.dto;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

/**
 * 新增图书请求DTO
 */
@Data
@ApiModel("新增图书请求")
public class AddBookDTO {
    
    @ApiModelProperty("图书名称")
    private String bookName;
    
    @ApiModelProperty("作者")
    private String author;
    
    @ApiModelProperty("ISBN")
    private String isbn;
    
    @ApiModelProperty("价格")
    private BigDecimal price;
    
    @ApiModelProperty("库存数量")
    private Integer stock;
    
    @ApiModelProperty("出版日期")
    private String publishDate;
}

3.2 响应DTO(返回数据给前端)

复制代码
package com.example.library.dto;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;

/**
 * 图书详情响应DTO
 */
@Data
@ApiModel("图书详情")
public class BookDetailDTO {
    
    @ApiModelProperty("图书ID")
    private String id;
    
    @ApiModelProperty("图书名称")
    private String bookName;
    
    @ApiModelProperty("作者")
    private String author;
    
    @ApiModelProperty("价格")
    private BigDecimal price;
    
    @ApiModelProperty("库存状态")
    private String stockStatus;  // "充足"、"紧张"、"缺货"
    
    @ApiModelProperty("是否可借")
    private Boolean borrowable;
}

为什么要用DTO?

  • Entity管数据:与数据库表结构对应
  • DTO管传输:与前端接口对应,可以包含计算字段、格式化数据
  • 解耦合:数据库表结构变化不影响接口
  • 安全性:避免暴露敏感字段(如deleted、create_time)

第4步:创建Repository数据访问层

Repository负责与数据库交互,提供基本的CRUD操作。

复制代码
package com.example.library.repository;

import com.example.library.entity.Book;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;

/**
 * 图书数据访问层
 */
public interface BookRepository extends JpaRepository<Book, String> {
    
    /**
     * 根据ISBN查找图书
     */
    Book findByIsbn(String isbn);
    
    /**
     * 根据作者模糊查询
     */
    List<Book> findByAuthorContaining(String author);
    
    /**
     * 查询库存不足的图书
     */
    @Query("SELECT b FROM Book b WHERE b.stock < :threshold AND b.deleted = 0")
    List<Book> findLowStockBooks(@Param("threshold") Integer threshold);
    
    /**
     * 统计图书总数
     */
    @Query("SELECT COUNT(b) FROM Book b WHERE b.deleted = 0")
    Long countByDeleted();
}

Repository的特点:

  • 继承 JpaRepository 获得基础CRUD方法
  • 使用方法命名规则自动生成SQL(如 findByAuthorContaining
  • 使用 @Query 注解自定义复杂查询
  • 只负责数据访问,不包含业务逻辑

第5步:创建Service业务逻辑层

Service负责业务逻辑处理,连接Controller和Repository。

5.1 Service接口

复制代码
package com.example.library.service;

import com.example.library.dto.AddBookDTO;
import com.example.library.dto.BookDetailDTO;
import com.example.library.entity.Book;
import java.util.List;

/**
 * 图书业务逻辑接口
 */
public interface BookService {
    
    /**
     * 新增图书
     */
    boolean addBook(AddBookDTO dto);
    
    /**
     * 获取图书详情
     */
    BookDetailDTO getBookDetail(String bookId);
    
    /**
     * 借阅图书
     */
    boolean borrowBook(String bookId, String readerName);
    
    /**
     * 获取库存不足的图书列表
     */
    List<Book> getLowStockBooks(Integer threshold);
}

5.2 Service实现类

复制代码
package com.example.library.service.impl;

import com.example.library.dto.AddBookDTO;
import com.example.library.dto.BookDetailDTO;
import com.example.library.entity.Book;
import com.example.library.repository.BookRepository;
import com.example.library.service.BookService;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.List;
import java.util.UUID;

/**
 * 图书业务逻辑实现
 */
@Transactional
@Log4j2
@Service
public class BookServiceImpl implements BookService {
    
    @Autowired
    private BookRepository bookRepository;
    
    @Override
    public boolean addBook(AddBookDTO dto) {
        try {
            // 1. 检查ISBN是否已存在
            Book existingBook = bookRepository.findByIsbn(dto.getIsbn());
            if (existingBook != null) {
                log.warn("ISBN已存在: {}", dto.getIsbn());
                return false;
            }
            
            // 2. 创建图书实体
            Book book = new Book();
            book.setId(UUID.randomUUID().toString());
            book.setBookName(dto.getBookName());
            book.setAuthor(dto.getAuthor());
            book.setIsbn(dto.getIsbn());
            book.setPrice(dto.getPrice());
            book.setStock(dto.getStock());
            book.setDeleted(0);
            book.setCreateTime(new Date());
            book.setUpdateTime(new Date());
            
            // 3. 保存到数据库
            bookRepository.save(book);
            return true;
        } catch (Exception e) {
            log.error("新增图书失败", e);
            return false;
        }
    }
    
    @Override
    public BookDetailDTO getBookDetail(String bookId) {
        // 1. 查询图书
        Book book = bookRepository.findById(bookId).orElse(null);
        if (book == null) {
            return null;
        }
        
        // 2. Entity转DTO
        BookDetailDTO dto = new BookDetailDTO();
        dto.setId(book.getId());
        dto.setBookName(book.getBookName());
        dto.setAuthor(book.getAuthor());
        dto.setPrice(book.getPrice());
        
        // 3. 计算库存状态
        if (book.getStock() == 0) {
            dto.setStockStatus("缺货");
            dto.setBorrowable(false);
        } else if (book.getStock() < 5) {
            dto.setStockStatus("紧张");
            dto.setBorrowable(true);
        } else {
            dto.setStockStatus("充足");
            dto.setBorrowable(true);
        }
        
        return dto;
    }
    
    @Override
    public boolean borrowBook(String bookId, String readerName) {
        try {
            // 1. 查询图书
            Book book = bookRepository.findById(bookId).orElse(null);
            if (book == null) {
                log.warn("图书不存在: {}", bookId);
                return false;
            }
            
            // 2. 检查库存
            if (book.getStock() <= 0) {
                log.warn("库存不足: {}", book.getBookName());
                return false;
            }
            
            // 3. 扣减库存
            book.setStock(book.getStock() - 1);
            book.setUpdateTime(new Date());
            bookRepository.save(book);
            
            // 4. 创建借阅记录(省略)
            // ...
            
            return true;
        } catch (Exception e) {
            log.error("借阅图书失败", e);
            return false;
        }
    }
    
    @Override
    public List<Book> getLowStockBooks(Integer threshold) {
        return bookRepository.findLowStockBooks(threshold);
    }
}

Service的职责:

  • 业务逻辑处理(如库存检查、状态计算)
  • 事务管理(@Transactional
  • 异常处理和日志记录
  • Entity与DTO之间的转换

第6步:创建Controller控制器层

Controller负责接收HTTP请求,调用Service处理,返回响应。

复制代码
package com.example.library.controller;

import com.example.library.dto.AddBookDTO;
import com.example.library.dto.BookDetailDTO;
import com.example.library.entity.Book;
import com.example.library.service.BookService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;

/**
 * 图书管理接口
 */
@Log4j2
@RestController
@RequestMapping("/api/book")
@Api(tags = "图书管理")
public class BookController {
    
    @Autowired
    private BookService bookService;
    
    /**
     * 新增图书
     * POST /api/book/add
     */
    @PostMapping("/add")
    @ApiOperation("新增图书")
    public boolean addBook(@RequestBody AddBookDTO dto) {
        log.info("新增图书: {}", dto.getBookName());
        return bookService.addBook(dto);
    }
    
    /**
     * 获取图书详情
     * GET /api/book/detail/{id}
     */
    @GetMapping("/detail/{id}")
    @ApiOperation("获取图书详情")
    public BookDetailDTO getBookDetail(@PathVariable String id) {
        log.info("查询图书详情: {}", id);
        return bookService.getBookDetail(id);
    }
    
    /**
     * 借阅图书
     * POST /api/book/borrow
     */
    @PostMapping("/borrow")
    @ApiOperation("借阅图书")
    public boolean borrowBook(@RequestParam String bookId, 
                              @RequestParam String readerName) {
        log.info("借阅图书: bookId={}, reader={}", bookId, readerName);
        return bookService.borrowBook(bookId, readerName);
    }
    
    /**
     * 获取库存不足的图书
     * GET /api/book/low-stock
     */
    @GetMapping("/low-stock")
    @ApiOperation("获取库存不足的图书")
    public List<Book> getLowStockBooks(@RequestParam(defaultValue = "5") Integer threshold) {
        log.info("查询库存不足图书: threshold={}", threshold);
        return bookService.getLowStockBooks(threshold);
    }
}

Controller的职责:

  • 定义REST接口(URL、请求方法、参数)
  • 参数校验和绑定
  • 调用Service处理业务
  • 返回响应数据
  • 记录接口访问日志

第7步:创建SQL配置类(可选)

对于复杂的统计查询、多表关联,可以使用YAML配置SQL,提高可维护性。

7.1 YAML配置文件

复制代码
# src/main/resources/sql/book-sql.yml
book:
  # 图书借阅统计
  borrow-statistics:
    columns: |
      SELECT 
          b.book_name,
          b.author,
          COUNT(bb.id) AS borrow_count,
          SUM(CASE WHEN bb.status = 0 THEN 1 ELSE 0 END) AS borrowing_count
    tables: |
      FROM book b
      LEFT JOIN book_borrow bb ON b.id = bb.book_id AND bb.deleted = 0
    condition: |
      WHERE b.deleted = 0
        AND b.publish_date >= :startDate
      GROUP BY b.id, b.book_name, b.author
      ORDER BY borrow_count DESC
  
  # 热门图书排行
  popular-books:
    columns: |
      SELECT 
          b.id,
          b.book_name,
          b.author,
          COUNT(bb.id) AS borrow_times
    tables: |
      FROM book b
      INNER JOIN book_borrow bb ON b.id = bb.book_id
    condition: |
      WHERE b.deleted = 0 
        AND bb.borrow_date >= :startDate
        AND bb.borrow_date <= :endDate
      GROUP BY b.id
      ORDER BY borrow_times DESC
      LIMIT 10

7.2 SQL配置类

复制代码
package com.example.library.sql;

import com.gri.iot.common.sourceloader.YamlPropertySourceFacetory;
import com.gri.iot.common.sql.SqlBody;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

/**
 * 图书SQL配置类
 */
@Data
@PropertySource(value = "classpath:/sql/book-sql.yml", factory = YamlPropertySourceFacetory.class)
@Configuration
@ConfigurationProperties(prefix = "book")
public class BookSqlConfig {
    
    /**
     * 图书借阅统计
     */
    private SqlBody borrowStatistics;
    
    /**
     * 热门图书排行
     */
    private SqlBody popularBooks;
}

7.3 使用SQL配置

复制代码
@Service
public class BookReportServiceImpl implements BookReportService {
    
    @Autowired
    private BookSqlConfig bookSqlConfig;
    
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    @Override
    public List<Map<String, Object>> getBorrowStatistics(String startDate) {
        // 1. 获取SQL配置
        SqlBody sqlBody = bookSqlConfig.getBorrowStatistics();
        
        // 2. 组装SQL
        String sql = sqlBody.getColumns() + 
                    sqlBody.getTables() + 
                    sqlBody.getCondition();
        
        // 3. 执行查询
        return jdbcTemplate.queryForList(sql, startDate);
    }
}

什么时候用YAML配置SQL?

  • ✅ 复杂的多表关联查询
  • ✅ 需要动态条件拼接
  • ✅ 频繁调整的统计报表
  • ✅ SQL语句很长(超过50行)

什么时候用JPA?

  • ✅ 简单的CRUD操作
  • ✅ 单表查询
  • ✅ 方法命名规则可以满足的需求

各层之间的调用关系

复制代码
┌─────────────────────────────────────────────────────────┐
│  前端(Vue/React/小程序)                                │
└──────────────────────┬──────────────────────────────────┘
                       │ HTTP请求
                       ▼
┌─────────────────────────────────────────────────────────┐
│  Controller层(控制器)                                   │
│  - 接收请求参数                                          │
│  - 调用Service                                           │
│  - 返回响应                                              │
└──────────────────────┬──────────────────────────────────┘
                       │
                       ▼
┌─────────────────────────────────────────────────────────┐
│  Service层(业务逻辑)                                    │
│  - 业务逻辑处理                                          │
│  - 事务管理                                              │
│  - Entity ↔ DTO 转换                                     │
└─────────────┬─────────────────────────┬─────────────────┘
              │                         │
              ▼                         ▼
┌─────────────────────────┐  ┌──────────────────────────┐
│  Repository层(JPA)     │  │  SQL配置类(YAML)        │
│  - 简单CRUD              │  │  - 复杂查询               │
│  - 方法命名规则          │  │  - 统计报表               │
└────────────┬────────────┘  └────────────┬─────────────┘
             │                           │
             └───────────┬───────────────┘
                         ▼
              ┌─────────────────────────┐
              │  数据库(PostgreSQL)     │
              └─────────────────────────┘

总结

Java后端开发的标准流程可以总结为:

步骤 名称 作用 技术选型
1 数据库设计 数据存储基础 PostgreSQL/MySQL
2 Entity实体类 ORM映射,数据存储 JPA @Entity
3 DTO对象 数据传输,前后端交互 @Data, @ApiModel
4 Repository层 数据访问,简单CRUD JpaRepository
5 Service层 业务逻辑,事务管理 @Service, @Transactional
6 Controller层 接口定义,请求处理 @RestController
7 SQL配置类 复杂查询,统计报表 YAML + SqlBody

核心原则

  1. 单一职责:每层只做自己该做的事
  2. 向下依赖:Controller → Service → Repository → Database
  3. Entity与DTO分离:数据存储与数据传输解耦
  4. 简洁至上:简单用JPA,复杂用YAML SQL
  5. 异常处理:Service层统一处理异常,记录日志

最佳实践

  • ✅ Entity类只包含字段映射,不包含业务逻辑
  • ✅ Service接口和实现类分离,便于扩展
  • ✅ 使用Optional避免NPE
  • ✅ 使用@Transactional管理事务
  • ✅ Controller只做参数绑定和响应,不做业务逻辑
  • ✅ 复杂SQL使用YAML配置,便于维护
  • ✅ 所有接口添加Swagger文档注解

希望这篇文章能帮助你理解Java后端开发的完整流程!有问题欢迎讨论。

相关推荐
泯仲2 小时前
从零起步学习MySQL 第一章:初识MySQL及深入理解内部数据类型
数据库·mysql
有想法的py工程师2 小时前
PostgreSQL 触发器性能评估实战(pg_stat_user_functions)
数据库·postgresql
御坂10101号2 小时前
「2>&1」是什么意思?半个世纪的 Unix 谜题
java·数据库·bash·unix
韩立学长2 小时前
基于Springboot校园志愿者服务平台77pz7812(程序、源码、数据库、调试部署方案及开发环境)系统界面展示及获取方式置于文档末尾,可供参考。
数据库·spring boot·后端
代码雕刻家3 小时前
MySQL和SQL Server注意事项
数据库·mysql
代码探秘者3 小时前
【Redis】分布式锁深度解析:实现、可重入、主从一致性与强一致方案
java·数据库·redis·分布式·缓存·面试
IvorySQL3 小时前
IvorySQL 5.3 正式发布:基于 PG 18.3 内核,多特性升级+全场景适配
数据库·postgresql·开源
冰糖拌面3 小时前
mysql 与 pg 的网卡监听参数
数据库·mysql·postgresql
DBA小马哥3 小时前
智能电网调度系统国产化:为什么总卡在数据库替换这一步?
数据库