Java后端開發(fā)標準流程之從數(shù)據(jù)庫到接口的完整實現(xiàn)方法
前言
在實際的Java后端開發(fā)中,我們通常會遵循一個標準的開發(fā)流程,確保代碼結(jié)構(gòu)清晰、層次分明、易于維護。本文將以一個圖書管理系統(tǒng)為例,詳細介紹從數(shù)據(jù)庫設計到接口實現(xiàn)的完整流程。
完整開發(fā)流程
第1步:創(chuàng)建數(shù)據(jù)表
↓
第2步:創(chuàng)建Entity實體類
↓
第3步:創(chuàng)建DTO對象
↓
第4步:創(chuàng)建Repository數(shù)據(jù)訪問層
↓
第5步:創(chuàng)建Service業(yè)務邏輯層
↓
第6步:創(chuàng)建Controller控制器層
↓
第7步:創(chuàng)建SQL配置類(可選,復雜查詢使用)第1步:創(chuàng)建數(shù)據(jù)表
首先在數(shù)據(jù)庫中創(chuàng)建表結(jié)構(gòu),這是整個系統(tǒng)的數(shù)據(jù)基礎。
-- 圖書表
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字段實現(xiàn)邏輯刪除(0-正常 1-刪除) - 使用
create_time和update_time記錄時間 - 外鍵關聯(lián)保持數(shù)據(jù)完整性
第2步:創(chuàng)建Entity實體類
Entity類對應數(shù)據(jù)庫表結(jié)構(gòu),用于ORM映射。
package com.example.library.entity;
import lombok.Data;
import javax.persistence.*;
import java.math.BigDecimal;
import java.util.Date;
/**
* 圖書實體類
* 對應數(shù)據(jù)庫表: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的作用:
- 與數(shù)據(jù)庫表一一對應
- 負責數(shù)據(jù)的存儲和讀取
- 不包含業(yè)務邏輯
第3步:創(chuàng)建DTO對象
DTO(Data Transfer Object)用于前后端數(shù)據(jù)傳輸,與Entity分離。
3.1 請求DTO(接收前端參數(shù))
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("庫存數(shù)量")
private Integer stock;
@ApiModelProperty("出版日期")
private String publishDate;
}
3.2 響應DTO(返回數(shù)據(jù)給前端)
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("庫存狀態(tài)")
private String stockStatus; // "充足"、"緊張"、"缺貨"
@ApiModelProperty("是否可借")
private Boolean borrowable;
}
為什么要用DTO?
- Entity管數(shù)據(jù):與數(shù)據(jù)庫表結(jié)構(gòu)對應
- DTO管傳輸:與前端接口對應,可以包含計算字段、格式化數(shù)據(jù)
- 解耦合:數(shù)據(jù)庫表結(jié)構(gòu)變化不影響接口
- 安全性:避免暴露敏感字段(如deleted、create_time)
第4步:創(chuàng)建Repository數(shù)據(jù)訪問層
Repository負責與數(shù)據(jù)庫交互,提供基本的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;
/**
* 圖書數(shù)據(jù)訪問層
*/
public interface BookRepository extends JpaRepository<Book, String> {
/**
* 根據(jù)ISBN查找圖書
*/
Book findByIsbn(String isbn);
/**
* 根據(jù)作者模糊查詢
*/
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);
/**
* 統(tǒng)計圖書總數(shù)
*/
@Query("SELECT COUNT(b) FROM Book b WHERE b.deleted = 0")
Long countByDeleted();
}
Repository的特點:
- 繼承
JpaRepository獲得基礎CRUD方法 - 使用方法命名規(guī)則自動生成SQL(如
findByAuthorContaining) - 使用
@Query注解自定義復雜查詢 - 只負責數(shù)據(jù)訪問,不包含業(yè)務邏輯
第5步:創(chuàng)建Service業(yè)務邏輯層
Service負責業(yè)務邏輯處理,連接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;
/**
* 圖書業(yè)務邏輯接口
*/
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實現(xiàn)類
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;
/**
* 圖書業(yè)務邏輯實現(xiàn)
*/
@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. 創(chuàng)建圖書實體
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. 保存到數(shù)據(jù)庫
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轉(zhuǎn)DTO
BookDetailDTO dto = new BookDetailDTO();
dto.setId(book.getId());
dto.setBookName(book.getBookName());
dto.setAuthor(book.getAuthor());
dto.setPrice(book.getPrice());
// 3. 計算庫存狀態(tài)
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. 創(chuàng)建借閱記錄(省略)
// ...
return true;
} catch (Exception e) {
log.error("借閱圖書失敗", e);
return false;
}
}
@Override
public List<Book> getLowStockBooks(Integer threshold) {
return bookRepository.findLowStockBooks(threshold);
}
}
Service的職責:
- 業(yè)務邏輯處理(如庫存檢查、狀態(tài)計算)
- 事務管理(
@Transactional) - 異常處理和日志記錄
- Entity與DTO之間的轉(zhuǎn)換
第6步:創(chuàng)建Controller控制器層
Controller負責接收HTTP請求,調(diào)用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、請求方法、參數(shù))
- 參數(shù)校驗和綁定
- 調(diào)用Service處理業(yè)務
- 返回響應數(shù)據(jù)
- 記錄接口訪問日志
第7步:創(chuàng)建SQL配置類(可選)
對于復雜的統(tǒng)計查詢、多表關聯(lián),可以使用YAML配置SQL,提高可維護性。
7.1 YAML配置文件
# src/main/resources/sql/book-sql.yml
book:
# 圖書借閱統(tǒng)計
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 {
/**
* 圖書借閱統(tǒng)計
*/
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. 執(zhí)行查詢
return jdbcTemplate.queryForList(sql, startDate);
}
}
什么時候用YAML配置SQL?
- ? 復雜的多表關聯(lián)查詢
- ? 需要動態(tài)條件拼接
- ? 頻繁調(diào)整的統(tǒng)計報表
- ? SQL語句很長(超過50行)
什么時候用JPA?
- ? 簡單的CRUD操作
- ? 單表查詢
- ? 方法命名規(guī)則可以滿足的需求
各層之間的調(diào)用關系
┌─────────────────────────────────────────────────────────┐
│ 前端(Vue/React/小程序) │
└──────────────────────┬──────────────────────────────────┘
│ HTTP請求
▼
┌─────────────────────────────────────────────────────────┐
│ Controller層(控制器) │
│ - 接收請求參數(shù) │
│ - 調(diào)用Service │
│ - 返回響應 │
└──────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Service層(業(yè)務邏輯) │
│ - 業(yè)務邏輯處理 │
│ - 事務管理 │
│ - Entity ? DTO 轉(zhuǎn)換 │
└─────────────┬─────────────────────────┬─────────────────┘
│ │
▼ ▼
┌─────────────────────────┐ ┌──────────────────────────┐
│ Repository層(JPA) │ │ SQL配置類(YAML) │
│ - 簡單CRUD │ │ - 復雜查詢 │
│ - 方法命名規(guī)則 │ │ - 統(tǒng)計報表 │
└────────────┬────────────┘ └────────────┬─────────────┘
│ │
└───────────┬───────────────┘
▼
┌─────────────────────────┐
│ 數(shù)據(jù)庫(PostgreSQL) │
└─────────────────────────┘
總結(jié)
Java后端開發(fā)的標準流程可以總結(jié)為:
| 步驟 | 名稱 | 作用 | 技術選型 |
|---|---|---|---|
| 1 | 數(shù)據(jù)庫設計 | 數(shù)據(jù)存儲基礎 | PostgreSQL/MySQL |
| 2 | Entity實體類 | ORM映射,數(shù)據(jù)存儲 | JPA @Entity |
| 3 | DTO對象 | 數(shù)據(jù)傳輸,前后端交互 | @Data, @ApiModel |
| 4 | Repository層 | 數(shù)據(jù)訪問,簡單CRUD | JpaRepository |
| 5 | Service層 | 業(yè)務邏輯,事務管理 | @Service, @Transactional |
| 6 | Controller層 | 接口定義,請求處理 | @RestController |
| 7 | SQL配置類 | 復雜查詢,統(tǒng)計報表 | YAML + SqlBody |
核心原則
- 單一職責:每層只做自己該做的事
- 向下依賴:Controller → Service → Repository → Database
- Entity與DTO分離:數(shù)據(jù)存儲與數(shù)據(jù)傳輸解耦
- 簡潔至上:簡單用JPA,復雜用YAML SQL
- 異常處理:Service層統(tǒng)一處理異常,記錄日志
最佳實踐
- ? Entity類只包含字段映射,不包含業(yè)務邏輯
- ? Service接口和實現(xiàn)類分離,便于擴展
- ? 使用Optional避免NPE
- ? 使用@Transactional管理事務
- ? Controller只做參數(shù)綁定和響應,不做業(yè)務邏輯
- ? 復雜SQL使用YAML配置,便于維護
- ? 所有接口添加Swagger文檔注解
到此這篇關于Java后端開發(fā)標準流程之從數(shù)據(jù)庫到接口的完整實現(xiàn)方法的文章就介紹到這了,更多相關Java數(shù)據(jù)庫到接口實現(xiàn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Spring Boot全局異常處理與日志監(jiān)控全解析
本文給大家介紹Spring Boot全局異常處理與日志監(jiān)控全解析,通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧2025-08-08
java實現(xiàn)附件預覽(openoffice+swftools+flexpaper)實例
本篇文章主要介紹了java實現(xiàn)附件預覽(openoffice+swftools+flexpaper)實例,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2016-10-10

