基礎crud項目(前端部分+總結)

本人根據自己對前端微不足道的理解和 AI 老師的指導下,艱難地完成了基礎crud代碼的全棧開發,算是自己的第一個 Java 項目,對此做個簡單總結。

后端部分


在前后端分離開發中,前端負責頁面交互與數據展示,后端提供接口支持。本文基于 Vue3+Element Plus 構建用戶管理前端頁面,實現新增、編輯、刪除、搜索核心功能,配合后端 RESTful 接口完成聯調,并解決開發中常見的報錯問題,適合新手快速上手。

技術棧

JavaSE,MySQL,Spring,SpringMVC,MyBatis,SpringBoot,Vue3,Element Plus。

原理

后端原理

依賴注入:通過 @Autowired 實現 Service 與 Dao、Controller 與 Service 層的解耦;
MyBatis 數據映射:開啟 map-underscore-to-camel-case 自動轉換數據庫下劃線字段(如 user_id)與 Java 駝峰屬性(如 userId),通過注解式 SQL 實現 CRUD;
RESTful 接口規范:用 @GetMapping/{id}/@PostMapping/@PutMapping/{userId}/@DeleteMapping/{id} 對應查、增、改、刪操作,路徑傳主鍵 + 請求體傳數據,確保語義清晰。
整合 Lombok 簡化 Pojo 類代碼。

前端原理
Vue3 響應式:通過ref/reactive定義響應式數據(如userList/formData),數據變化時自動更新頁面(如表格、表單)。
Element Plus 組件化:復用表格(el-table)、表單(el-form)、對話框(el-dialog)等組件,快速搭建交互界面,減少原生 DOM 操作。
Fetch 異步請求:通過原生Fetch API調用后端 RESTful 接口,實現 “數據請求 - 響應 - 頁面更新” 閉環,配合async/await簡化異步代碼。

功能

后端功能

提供用戶數據的完整 CRUD 接口:
查:按 ID 查單個用戶(/users/{id})、查所有用戶(/users);
增:新增用戶(/users,請求體傳待增字段);
改:按 ID 更新用戶信息(/users,請求體傳待更字段);
刪:按 ID 刪除用戶(/users/{id})。

前端功能(交互 + 數據展示)
數據展示:用el-table展示用戶列表,加載時顯示動畫,搜索空結果時提示 “未找到用戶”。
新增用戶:點擊 “新增” 打開對話框,表單驗證(ID 必為數字、用戶名 / 密碼必填),提交后刷新列表。
編輯用戶:點擊 “編輯” 填充當前用戶數據,ID 禁用不可改,提交后更新列表。
搜索用戶:輸入 ID 回車 / 點擊搜索,查詢單個用戶;清空輸入框顯示所有用戶。
刪除用戶:點擊 “刪除” 彈出確認框,確認后刪除并刷新列表。

前端代碼

<template><div class="user-page"><h1>用戶管理</h1><!-- 操作區 --><div class="operation-bar"><el-input v-model="searchId"placeholder="輸入ID搜索"style="width: 200px"clearable@keyup.enter="handleSearch" /><el-button type="primary" icon="Search" @click="handleSearch">搜索</el-button><el-button type="success" icon="Plus" @click="openAddDialog">新增用戶</el-button></div><!-- 用戶表格 --><el-table :data="userList"borderstyle="width: 100%":loading="loading"><el-table-column prop="userId" label="ID" width="80" align="center" /><el-table-column prop="userName" label="用戶名" width="150" align="center" /><el-table-column prop="password" label="密碼" align="center" /><el-table-column label="操作" width="240" align="center"><template #default="scope"><el-button type="primary"size="small"icon="Edit"@click="openEditDialog(scope.row)"style="margin-right: 5px">編輯</el-button><el-button type="danger"size="small"icon="Delete"@click="handleDelete(scope.row.userId)">刪除</el-button></template></el-table-column></el-table><!-- 新增用戶對話框 --><el-dialog title="新增用戶"v-model="addDialogVisible"width="300px"><el-form :model="addForm" :rules="addRules" ref="addFormRef" label-width="80px"><el-form-item label="用戶ID" prop="userId"><el-input v-model.number="addForm.userId" placeholder="請輸入ID" /></el-form-item><el-form-item label="用戶名" prop="userName"><el-input v-model="addForm.userName" /></el-form-item><el-form-item label="密碼" prop="password"><el-input v-model="addForm.password" type="password" /></el-form-item></el-form><template #footer><el-button @click="addDialogVisible = false">取消</el-button><el-button type="primary" @click="handleAddSubmit">確認新增</el-button></template></el-dialog><!-- 編輯用戶對話框 --><el-dialog title="編輯用戶"v-model="editDialogVisible"width="300px"><el-form :model="editForm" :rules="editRules" ref="editFormRef" label-width="80px"><!-- 編輯時ID不可修改,僅展示 --><el-form-item label="用戶ID"><el-input v-model="editForm.userId" disabled /></el-form-item><el-form-item label="用戶名" prop="userName"><el-input v-model="editForm.userName" /></el-form-item><el-form-item label="密碼" prop="password"><el-input v-model="editForm.password" type="password" /></el-form-item></el-form><template #footer><el-button @click="editDialogVisible = false">取消</el-button><el-button type="primary" @click="handleEditSubmit">確認編輯</el-button></template></el-dialog><!-- 刪除確認對話框 --><el-dialog title="確認刪除"v-model="deleteDialogVisible"width="300px"><p>確定要刪除ID為 {{ deleteId }} 的用戶嗎?</p><template #footer><el-button @click="deleteDialogVisible = false">取消</el-button><el-button type="danger" @click="confirmDelete">確認刪除</el-button></template></el-dialog></div>
</template><script setup>import { ref, reactive, onMounted } from 'vue'import { ElMessage } from 'element-plus'import { Search, Plus, Edit, Delete } from '@element-plus/icons-vue'// 基礎數據const userList = ref([])const loading = ref(false)const searchId = ref('')// 新增相關const addDialogVisible = ref(false)          // 新增對話框顯示狀態const addFormRef = ref(null)                 // 新增表單引用const addForm = reactive({                   // 新增表單數據userId: 0,userName: '',password: ''})const addRules = reactive({                  // 新增表單驗證規則userId: [{ required: true, message: '請輸入用戶ID', trigger: 'blur' },{ type: 'number', message: 'ID必須是數字', trigger: 'blur' }],userName: [{ required: true, message: '請輸入用戶名', trigger: 'blur' }],password: [{ required: true, message: '請輸入密碼', trigger: 'blur' }]})// 編輯相關const editDialogVisible = ref(false)         // 編輯對話框顯示狀態const editFormRef = ref(null)                // 編輯表單引用const editForm = reactive({                  // 編輯表單數據userId: '',userName: '',password: ''})const editRules = reactive({                 // 編輯表單驗證規則(無需驗證ID)userName: [{ required: true, message: '請輸入用戶名', trigger: 'blur' }],password: [{ required: true, message: '請輸入密碼', trigger: 'blur' }]})// 刪除相關const deleteDialogVisible = ref(false)const deleteId = ref(0)// 后端接口地址const API_URL = 'http://localhost:8081/users'// 1. 獲取所有用戶const getAllUsers = async () => {loading.value = truetry {const res = await fetch(API_URL)if (!res.ok) throw new Error('獲取失敗')userList.value = await res.json()} catch (err) {console.error('獲取用戶失敗:', err)ElMessage.error('獲取用戶失敗')} finally {loading.value = false}}// 2. 搜索用戶const handleSearch = async () => {if (!searchId.value) {getAllUsers()return}loading.value = truetry {const res = await fetch(`${API_URL}/${searchId.value}`)if (res.status === 404) {userList.value = []ElMessage.warning('用戶不存在')return}if (!res.ok) throw new Error('搜索失敗')userList.value = [await res.json()]} catch (err) {console.error('搜索失敗:', err)ElMessage.error('搜索失敗')} finally {loading.value = false}}// 3. 打開新增對話框const openAddDialog = () => {// 重置新增表單addForm.userId = ''addForm.userName = ''addForm.password = ''addDialogVisible.value = true}// 4. 提交新增表單const handleAddSubmit = async () => {if (!addFormRef.value) returntry {// 驗證新增表單await addFormRef.value.validate()// 發送新增請求const res = await fetch(API_URL, {method: 'POST',headers: { 'Content-Type': 'application/json' },body: JSON.stringify(addForm)  // 提交新增表單數據})if (!res.ok) throw new Error('新增失敗')ElMessage.success('新增成功')addDialogVisible.value = false  // 關閉新增對話框getAllUsers()                   // 刷新列表} catch (err) {console.error('新增失敗:', err)ElMessage.error(err.message || '新增失敗')}}// 5. 打開編輯對話框const openEditDialog = (user) => {// 填充編輯表單editForm.userId = user.userIdeditForm.userName = user.userNameeditForm.password = user.passwordeditDialogVisible.value = true}// 6. 提交編輯表單const handleEditSubmit = async () => {if (!editFormRef.value) returntry {// 驗證編輯表單await editFormRef.value.validate()// 發送編輯請求(URL包含原ID)const res = await fetch(API_URL, {method: 'PUT',headers: { 'Content-Type': 'application/json' },body: JSON.stringify(editForm)  // 提交編輯表單數據})if (!res.ok) throw new Error('編輯失敗')ElMessage.success('編輯成功')editDialogVisible.value = false  // 關閉編輯對話框getAllUsers()                    // 刷新列表} catch (err) {console.error('編輯失敗:', err)ElMessage.error(err.message || '編輯失敗')}}// 7. 刪除用戶const handleDelete = (id) => {deleteId.value = iddeleteDialogVisible.value = true}// 確認刪除const confirmDelete = async () => {try {const res = await fetch(`${API_URL}/${deleteId.value}`, {method: 'DELETE'})if (!res.ok) throw new Error('刪除失敗')ElMessage.success('刪除成功')deleteDialogVisible.value = falsegetAllUsers()} catch (err) {console.error('刪除失敗:', err)ElMessage.error('刪除失敗')}}// 初始化onMounted(() => {getAllUsers()})
</script><style scoped>.user-page {padding: 20px;max-width: 1000px;margin: 0 auto;}.operation-bar {margin-bottom: 15px;display: flex;gap: 10px;align-items: center;}h1 {color: #333;font-size: 20px;margin-bottom: 20px;}
</style>

后端代碼已展示在文前博客鏈接中。

問題和挑戰

1.輸入數字 ID 仍提示 “ID 必須是數字”
普通 v-model 會將輸入值轉為字符串(如輸入 101,實際是 "101"),而驗證規則 type: 'number' 校驗的是數據類型,不是格式。
解決方案:用 v-model.number 自動將輸入字符串轉為數字,如果是字符串則轉為NaN,符合要求。

2.編輯功能報 “Failed to fetch”
編輯請求 URL 錯誤(如 http://localhost:8081/users/undefined),可能是 editForm.userId 未正確賦值;也可能是后端 PUT 接口路徑與前端不一致。
解決方案:
打印 URL 確認正確性:在 handleEditSubmit 中添加 console.log;修改前端接口路徑。

3.前端頁面中文亂碼
這個確實花了很長時間,反復部署各種配置,跟著 AI 和各路大佬的博客調了好久,最終發現還是經典的 encoding 問題。
附上博客

4.后端 mybatis-plus 與 springboot 版本沖突
試了好多版本都不能正常運行,最后選擇放棄阿里的 mybatis-plus,使用 mybatis。畢竟差距確實不大,進行復雜 Dao 層開發時肯定還是要自己手搓,就當練習基礎語法了。

5.Linux相關
因為 MySQL 老師講企業開發中都是在 Linux 環境下進行部署等操作的,于是跟著 AI 一步一步在 Linux 虛擬機中安裝 MySQL 和 Redis(本項目沒用到Redis),期間有不少問題是自己根本不熟悉 Linux 造成的,后來特地學習了 Linux 相關知識,計劃日后繼續學習 JVM。

6.“白學”問題
這一點見仁見智,但是部分課程確實白學。我最開始學2021年黑馬的 Javaweb,確實好多東西已經用不上了,連老師也是提前準備或對著 ppt 敲的。不過很多人認為學習了 springboot 就沒必要學習 SSM 全家桶,這一點我持否認態度。畢竟我一開始也是這樣想的時候,直接去看 springboot 根本看不懂,連URL都不知道是什么。學習確實需要腳踏實地,不能妄想一步登天。

總結

整個暑假都在 Java 課程中度過,期間學習了不少前所未聞的知識。雖然第一次做的 Java 項目還是只有基礎的crud,但是“守得云開見月明”,相信在自己的努力下,一切終將美好。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/921264.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/921264.shtml
英文地址,請注明出處:http://en.pswp.cn/news/921264.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

MATLAB矩陣及其運算(二)函數

函數分為MATLAB內置函數及用戶自定義函數&#xff0c;用戶可以直接調用內置函數進行數據處理。內置函數的使用函數由三部分組成&#xff1a;名稱、輸入和輸出。內置函數示例&#xff1a;單輸入單輸出函數&#xff1a;sqrt(x)&#xff1b;單輸入多輸出函數&#xff1a;size(x)&a…

自動化運維-ansible中對于大項目的管理

自動化運維-ansible中對于大項目的管理 一、引用主機清單 在Playbook中引用主機時&#xff0c;hosts 字段指定的目標必須與Ansible主機清單中定義的標識符完全匹配。如果清單中配置的是主機名&#xff0c;則在Playbook中使用IP地址或其他別名將無法匹配&#xff0c;導致任務被跳…

59_基于深度學習的麥穗計數統計系統(yolo11、yolov8、yolov5+UI界面+Python項目源碼+模型+標注好的數據集)

目錄 項目介紹&#x1f3af; 功能展示&#x1f31f; 一、環境安裝&#x1f386; 環境配置說明&#x1f4d8; 安裝指南說明&#x1f3a5; 環境安裝教學視頻 &#x1f31f; 二、數據集介紹&#x1f31f; 三、系統環境&#xff08;框架/依賴庫&#xff09;說明&#x1f9f1; 系統環…

面試問題詳解十六:Qt 內存管理機制

在 Qt 開發過程中&#xff0c;很多初學者&#xff08;包括不少有經驗的 C 程序員&#xff09;經常會產生這樣的疑問&#xff1a;“我在 Qt 中 new 出來的控件好像都沒有 delete&#xff0c;那內存不會泄漏嗎&#xff1f;”比如下面這段代碼&#xff1a; void Widget::createLef…

Pycharm 試用

Ubuntu 重置Pycharm試用期限&#xff08;30 天&#xff09; 先關閉Pycharm刪除系統緩存 rm -rf ~/.config/JetBrains/ && rm -rf ~/.local/share/JetBrains/ && rm -rf ~/.cache/JetBrains/刪除已經安裝的 Pycharm 軟件運行目錄去官網下載新的 就行了

C++ Qt 開發核心知識

Qt 框架概述Qt 是一個跨平臺的 C 應用程序開發框架&#xff0c;廣泛用于開發圖形用戶界面程序。其核心特性包括跨平臺能力、豐富的功能模塊和強大的工具集。核心概念與機制元對象系統Qt 擴展了標準 C&#xff0c;通過元對象系統提供信號與槽機制、運行時類型信息和動態屬性系統…

net9 aspose.cell 自定義公式AbstractCalculationEngine,帶超鏈接excel轉html后背景色丟失

AbstractCalculationEngine 是 Aspose.Cells 中一個強大的抽象類&#xff0c;允許您自定義公式計算邏輯。當您需要覆蓋默認計算行為或實現自定義函數時非常有用。直接上代碼1. 創建自定義計算引擎using Aspose.Cells; using System;// 創建自定義計算引擎 public class CustomC…

如何監控員工的電腦?7款實用的員工電腦管理軟件,探索高效管理捷徑!

當銷售團隊在淘寶刷單、設計師用公司電腦挖礦、程序員頻繁訪問代碼托管網站時&#xff0c;企業損失的不僅是帶寬——低效、泄密、合規風險正成為隱形利潤殺手。 傳統管理依賴“人盯人”或抽查日志&#xff0c;但面對分布式辦公與遠程協作趨勢&#xff0c;這些方法早已力不從心…

機器視覺軟件--VisionPro、Visual Master,Halcon 和 OpenCV 的學習路線

Halcon 和 OpenCV區別 Halcon 和 OpenCV 都是計算機視覺領域的重要工具&#xff0c;但它們的設計理念、功能側重和適用場景有顯著不同。下面這個表格匯總了它們的核心區別&#xff0c;方便你快速了解&#xff1a; 開發模式與體驗??&#xff1a;Halcon 配備了強大的??圖形化…

算法-根據前序+中序遍歷打印樹的右視圖

題目請根據二叉樹的前序遍歷&#xff0c;中序遍歷恢復二叉樹&#xff0c;并打印出二叉樹的右視圖數據范圍&#xff1a; 0≤n≤100000≤n≤10000 要求&#xff1a; 空間復雜度 O(n)O(n)&#xff0c;時間復雜度 O(n)O(n)如輸入[1,2,4,5,3],[4,2,5,1,3]時&#xff0c;通過前序遍歷…

Kafka面試精講 Day 7:消息序列化與壓縮策略

【Kafka面試精講 Day 7】消息序列化與壓縮策略 在Kafka的高性能消息系統中&#xff0c;消息序列化與壓縮是影響吞吐量、延遲和網絡開銷的核心環節。作為“Kafka面試精講”系列的第7天&#xff0c;本文聚焦于這一關鍵主題&#xff0c;深入剖析其原理、實現方式、配置策略及常見…

Xterminal軟件下載_Xterminal ssh遠程鏈接工具下載__Xterminal安裝包 網盤下載_Xterminal ssh遠程鏈接工具安裝包

Xterminal 作為一款國產 SSH 工具&#xff0c;專為開發人員量身打造。它支持 SSH 和 Telnet 協議連接遠程服務器與虛擬機&#xff0c;無論是進行代碼部署&#xff0c;還是服務器運維&#xff0c;都能輕松勝任。軟件界面采用極簡設計&#xff0c;黑色背景搭配白色文字&#xff0…

Lua > 洛谷

Lua > 洛谷P1000 超級瑪麗游戲P1001 AB ProblemP1008 [NOIP 1998 普及組] 三連擊P1035 [NOIP 2002 普及組] 級數求和P1046 [NOIP 2005 普及組] 陶陶摘蘋果P1047 [NOIP 2005 普及組] 校門外的樹P1085 [NOIP 2004 普及組] 不高興的津津P1089 [NOIP 2004 提高組] 津津的儲蓄計劃…

小企業環境-火山方舟和扣子

背景說明 并不是說應該怎么辦&#xff0c;而是基本配置有這些可以進行使用&#xff0c;具體不同企業使用的時候肯定要個性化配置。 使用了火山方舟和扣子 火山方舟 應用實驗室列表 簡單使用了提示詞的功能&#xff0c;后端服務ARK_API_KEY 應用ID 來對應請求發送http請求…

QT-事件

Qt事件 除了信號和槽通信機制外&#xff0c;Qt中還提供了事件處理機制實現與用戶的交互和對象間的通信。Qt捕獲底層操作系統消息&#xff0c;進行封裝之后轉換為Qt事件&#xff0c;事件處理后才發出信號。 一、事件概述Qt中事件是程序內部或外部發生的動作。比如程序外部&#…

HI3519DRFCV500/HI3519DV500海思核心板IPC算力2.5T圖像ISP超高清智能視覺應用提供SDK軟件開發包

Hi3519DV500是一顆面向視覺行業推出的超高清智能 SoC。最高支持四路sensor輸入&#xff0c;支持最高4K30fps的ISP圖像處理能力&#xff0c;支持 2F WDR、多級降噪、六軸防抖、全景拼接、多光 譜融合等多種傳統圖像增強和處理算法&#xff0c;支持通過AI算法對輸入圖像進行實時降…

go 初始化組件最佳實踐

Go 語言初始化最佳實踐 在 Go 語言中, 有一個 init() 函數可以對程序進行包級別的初始化, 但 init() 函數有諸多不便, 例如: 無法返回錯誤, 進行耗時初始化時, 會增加程序啟動時間。因此 init() 函數并不適用于所有初始化。 1.初始化方式 在程序進行初始化時&#xff0c;我們應…

域名暫停解析是怎么回事

域名注冊和使用是需要付費的&#xff0c;如果沒有及時續費&#xff0c;域名注冊商就會暫停該域名的解析服務。相關數據顯示&#xff0c;大約有 30% 的域名暫停解析情況是由于欠費引起的。比如&#xff0c;有個小公司的網站域名到期了&#xff0c;負責續費的員工忘記操作&#x…

前端開發的“三劍客”—— ??HTML、CSS、JavaScript??

前端開發的“三劍客”—— ??HTML、CSS、JavaScript??&#xff0c;是構建所有網頁和Web應用的基石。它們分工明確又緊密協作&#xff0c;共同實現了網頁的“內容結構”“視覺表現”和“交互行為”。以下是三者的詳細解析及協作邏輯&#xff1a;??1. HTML&#xff1a;網頁…

TDengine TIMEDIFF() 函數用戶使用手冊

TDengine TIMEDIFF() 函數詳細使用手冊 目錄 功能概述函數語法參數說明返回值說明版本變更說明技術特性使用場景及示例時間單位處理數據類型兼容性注意事項常見問題最佳實踐 功能概述 TIMEDIFF() 函數用于計算兩個時間戳的差值&#xff0c;返回 expr1 - expr2 的結果。結果…