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

