首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Java 單機版書店管理系統(四):UUID 自動生成與圖書模塊完整實現

     編輯:Java 綜合教學

前言

本系列第四篇將介紹書店管理系統的重要升級:引入 UUID 自動生成機制,並完整實現圖書管理模塊。相比前幾期,本次更新不僅優化了主界面導航方式,更通過設計模式的應用,展示了如何構建清晰分層、易於擴展的桌面應用架構。

核心更新內容

  • UUID 模塊:採用「唱票模式」自動生成唯一標識,彻底杜絕手動輸入導致的重複風險
  • 主界面重構:採用菜單欄導航替代原有按鈕跳轉,為後續登錄頁面預留接口
  • 圖書模塊上線:完整實現圖書的增刪改查,驗證分層架構的復用性
  • 代碼組織規範:嚴格遵循「先公共類/工具類,後業務模塊」的開發順序

系統運行截圖

主界面現通過頂部菜單「模塊」進入各功能區:

主界面菜單導航

用戶模塊主界面:

用戶模塊

圖書模塊主界面:

圖書模塊

添加用戶界面(無需輸入 UUID):

添加用戶

添加圖書界面:

添加圖書

項目目錄結構

清晰分層的包結構是維護性的關鍵:

目錄結構1 目錄結構2 目錄結構3

核心代碼解析

主程序入口:BookStore 類

public class BookStore extends javax.swing.JFrame {
    public BookStore() {
        super("書店管理系統---CHX---聯系QQ——619699629");
        initComponents();
        this.setResizable(false);
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        // 居中顯示邏輯
        Dimension scr = Toolkit.getDefaultToolkit().getScreenSize();
        this.setLocation(scr.width / 5, scr.height / 5);
    }

    // 菜單項事件:切換到圖書模塊
    private void jMenuItemBookActionPerformed(ActionEvent evt) {
        this.setContentPane(new BookListPanel(this));
        this.validate();
    }

    // 菜單項事件:切換到用戶模塊
    private void jMenuItemUserActionPerformed(ActionEvent evt) {
        this.setContentPane(new ListPanel(this));
        this.validate();
    }
    
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(() -> new BookStore().setVisible(true));
    }
}

設計亮點:通過 setContentPane() 動態替換面板實現模塊切換,避免多窗口管理複雜性;菜單項綁定快捷鍵(Ctrl+U/B)提升操作效率。

值對象層(VO)

BookModel - 圖書實體

public class BookModel implements Serializable {
    private String uuid;      // 主鍵,自動生成
    private String name;      // 書名
    private double inPrice;   // 進價
    private double salePrice; // 售價
    
    // 重寫 equals/hashCode 基於 uuid,保證集合操作正確性
    @Override public boolean equals(Object obj) { ... }
    @Override public int hashCode() { ... }
}

BookQueryModel - 查詢條件封裝

public class BookQueryModel extends BookModel {
    private double inPrice2;    // 進價上限
    private double salePrice2;  // 售價上限
    // 支持區間查詢:inPrice ~ inPrice2, salePrice ~ salePrice2
}

數據訪問層(DAO)

BookDao 接口

public interface BookDao {
    boolean create(BookModel book);
    boolean delete(String uuid);
    boolean update(BookModel book);
    List getAll();
    List getByCondition(BookQueryModel bqm);
    BookModel getSingle(String uuid);
}

BookDaoSerImpl - 基於文件序列化的實現

public class BookDaoSerImpl implements BookDao {
    private final String FILE_NAME = "Book.txt";
    
    @Override
    public boolean create(BookModel book) {
        List books = FileIoUtil.readFormFile(FILE_NAME);
        // 業務級唯一性校驗
        if (books.stream().anyMatch(m -> m.getUuid().equals(book.getUuid()))) 
            return false;
        books.add(book);
        FileIoUtil.write2file(books, FILE_NAME);
        return true;
    }
    
    @Override
    public List getByCondition(BookQueryModel bqm) {
        return getAll().stream().filter(book -> {
            if (bqm.getUuid() != null && !book.getUuid().equals(bqm.getUuid())) return false;
            if (bqm.getName() != null && !book.getName().contains(bqm.getName())) return false;
            if (bqm.getInPrice() > 0 && book.getInPrice() < bqm.getInPrice()) return false;
            if (bqm.getInPrice2() > 0 && book.getInPrice() > bqm.getInPrice2()) return false;
            if (bqm.getSalePrice() > 0 && book.getSalePrice() < bqm.getSalePrice()) return false;
            if (bqm.getSalePrice2() > 0 && book.getSalePrice() > bqm.getSalePrice2()) return false;
            return true;
        }).collect(Collectors.toList());
    }
}

注意:原代碼中 getByCondition 邏輯有殘缺,此處補全為流式寫法更清晰。實際項目中建議遷移至 SQLite/嵌入式數據庫。

業務邏輯層(EBI/EBO)

BookEbi 接口

public interface BookEbi {
    boolean create(BookModel book);
    boolean delete(String uuid);
    boolean update(BookModel book);
    List getAll();
    List getByCondition(BookQueryModel bqm);
    BookModel getSingle(String uuid);
}

BookEbo 實現 - 整合 UUID 服務

public class BookEbo implements BookEbi {
    private BookDao dao = BookDaoFactory.getBookDao();
    
    @Override
    public boolean create(BookModel book) {
        // 核心改動:調用 UUID 模塊自動生成主鍵
        String uuid = UuidDaoFactory.getUuidDao().getNextUuid(UuidModelConstance.BOOK);
        book.setUuid(uuid);
        return dao.create(book);
    }
    // 其他方法直接委託 DAO...
}

這裡體現了「依賴倒置」:業務層不直接 new 實現類,而是通過工廠獲取接口,同時演示了跨模塊調用 UUID 服務。

表現層(Swing UI)

BookListPanel - 列表與導航中樞

public class BookListPanel extends JPanel {
    private JFrame mainFrame;
    private JList bookLists;
    
    public BookListPanel(JFrame mainFrame) {
        this.mainFrame = mainFrame;
        initComponents();
        loadData();
    }
    
    private void loadData() {
        List list = BookEbiFactory.getBookEbi().getAll();
        bookLists.setListData(list.toArray(new BookModel[0]));
    }
    
    // 刪除示例:獲取選中項 -> 確認 -> 調用業務層 -> 刷新
    private void btnToDeleteActionPerformed(ActionEvent evt) {
        BookModel book = bookLists.getSelectedValue();
        if (book == null) { showMsg("請選擇要刪除的圖書!"); return; }
        mainFrame.setContentPane(new BookDeletePanel(mainFrame, book.getUuid()));
        mainFrame.validate();
    }
}

BookAddPanel - 添加界面(無 UUID 輸入框)

private void btnAddActionPerformed(ActionEvent evt) {
    String name = tfdName.getText().trim();
    double inPrice, salePrice;
    try {
        inPrice = Double.parseDouble(tfdInPrice.getText());
        salePrice = Double.parseDouble(tfdSalePrice.getText());
    } catch (NumberFormatException e) { showMsg("價格格式錯誤"); return; }
    
    if (name.isEmpty()) { showMsg("書名不能為空"); return; }
    
    BookModel book = new BookModel();
    book.setName(name);
    book.setInPrice(inPrice);
    book.setSalePrice(salePrice);
    // UUID 由業務層自動注入
    
    if (BookEbiFactory.getBookEbi().create(book)) back();
    else showMsg("該圖書已存在!");
}

工具類:FileIoUtil - 通用序列化持久化

public class FileIoUtil {
    @SuppressWarnings("unchecked")
    public static  List readFormFile(String fileName) {
        List list = new ArrayList<>();
        File file = new File(fileName);
        if (!file.exists()) return list;
        
        try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(file))) {
            list = (List) in.readObject();
        } catch (Exception e) { e.printStackTrace(); }
        return list;
    }
    
    public static  void write2file(List list, String fileName) {
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(fileName))) {
            out.writeObject(list);
        } catch (Exception e) { throw new RuntimeException("寫入失敗", e); }
    }
}

泛型方法實現了對任意實體列表的讀寫,配合 Serializable 接口實現零配置持久化。

架構總結與遺留問題

  1. 分層清晰:VO → DAO → EBI/EBO → UI,每層職責單一,便於單元測試
  2. 工廠模式解耦:DaoFactory/EbiFactory 隱藏實例化細節,支持未來替換實現(如換 MyBatis)
  3. UUID 統一管理:避免業務代碼硬編碼 ID 生成邏輯
  4. 待改進:
    • 文件存儲並發性能差,無事務支持
    • Swing 界面代碼與邏輯耦合,建議引入 MVC/MVP 模式
    • 異常處理僅打印堆棧,缺乏用戶友好提示與日誌記錄

下一期(系列四·2)將發布 UUID 模塊完整代碼、用戶模塊細節及公共基礎類,敬請期待。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved