SpringBoot+Vue3+MyBatis汽车租赁系统全栈实战解析 📅 发布时间:2026/9/15 1:21:16 👁 浏览次数: 拿到这套 SpringBoot Vue3 MyBatis 的汽车租赁系统源码时我第一个反应是这确实是一个很适合拿来练手、改造成毕设、甚至当比赛项目的全栈示例。技术栈不算新但胜在前后端分离结构清晰、业务闭环完整——从用户注册登录、浏览车辆、提交租车订单到管理员维护车辆信息、处理订单、查看统计该有的模块都有而且用的都是当前招聘市场上出现频率很高的 Java 后端框架和前端工程化方案。这篇文章我不打算只贴两张截图就完事而是把这套系统的理解、技术选型、数据库设计、关键接口与前端页面的实现逻辑以及我在跑通这类项目时踩过的一些坑按实际开发顺序拆开讲。无论你是准备拿它当毕业设计还是想通过一个完整项目巩固 SpringBoot 和 Vue3 的技能这篇内容都能给你一条清晰的参考路径。1. 先从业务说起这套系统到底做了哪些事很多人在看项目源码时喜欢直接打开代码但以我这些年的经验第一件事应该是先看业务。代码是业务的表达只有先把业务流程理顺了后面看表结构、看接口、看页面才能对得上号。汽车租赁系统本质上是一个典型的“资源 订单 用户”模型跟酒店预订、会议室预约、共享单车计费这类系统高度相似所以它非常适合当全栈学习的载体。1.1 用户视角下的租车全流程站在租车用户的角度一套合格的汽车租赁系统至少要覆盖这几个环节注册登录、浏览车辆、选车下单、支付或下单确认、取车还车、订单查看。这套源码里用户端的主流程大致是这样走的用户注册账号并登录然后进入车辆列表页面可以看到所有“上架中”的车辆包括车辆品牌、型号、日租金、座位数、变速箱类型这些基础信息。选中某辆车之后进入详情页选择租车开始时间和结束时间系统会算出总租金最终提交订单。这里有个业务细节值得注意租车订单不是直接生效的。很多练习项目会把订单做得很简单提交即成功但这套系统里加入了“待审核 / 已审核 / 已拒绝”的状态流转对应管理员端需要确认车辆是否可用、用户信息是否正常。这个设计更贴近真实业务也对后面的表结构和接口设计产生了直接影响。还车环节也做了简化处理但逻辑是通的。用户点击“还车”按钮后订单状态变为已完成车辆状态恢复为可租。如果要做扩展可以在还车时增加里程数录入、违章扣款计算、押金退还这些逻辑这些我在文章结尾会给出扩展建议。1.2 管理员端的核心操作管理员端解决的问题和用户端完全不同。用户端是“我要租车”管理员端是“我要保证车能租、租得出去、租完还能回来”。车辆管理是管理员端最基础的功能。管理员可以新增车辆信息包括车辆名称、品牌、车型、车牌号、日租金、车辆图片、座位数、排量、变速箱类型、车辆状态等。车辆状态分为“可用 / 已出租 / 维修中”这个状态字段很重要因为它直接决定了用户在车辆列表页能否看到这辆车、能否下单。订单管理则是管理员的核心工作台。管理员可以看到所有租车订单按状态筛选对待审核订单进行通过或拒绝操作。订单审核通过后车辆的库存或状态才会被占用订单完成或取消后车辆资源释放。除了车辆和订单管理员端通常还包括用户管理模块负责查看注册用户、禁用异常账号等。如果项目里还放了统计报表比如每日订单量、车辆利用率、营收排行那这个系统的完整度就更高了也更容易在答辩或演示时讲出亮点。1.3 核心业务表的设计思路数据库表的设计我建议直接对照业务流程来理解。这套系统最核心的表我认为至少是这几张用户表、车辆表、订单表。用户表存的是用户基础信息包括用户名、密码、手机号、角色类型。密码字段的设计值得关注这里必须强调任何真实项目中密码都不能明文存储至少要用 MD5 加盐或 BCrypt 加密。我在改这种项目的时候第一件事往往就是把密码加密方式统一改掉。车辆表对应管理员录入的车辆信息字段名称稍微想一下就能对上车辆名称、品牌、型号、车牌号、日租金、车辆图片地址、座位数、变速箱类型、车辆状态、创建时间、更新时间。订单表是业务的核心关联了用户和车辆。常见字段包括订单编号、用户ID、车辆ID、租车开始时间、租车结束时间、总租金、订单状态、创建时间。订单编号一般不会用自增ID直接展示给用户而是用时间戳加随机数的形式生成比如20250321103045 4位随机数方便追踪也显得更真实。这三张表之间通过外键逻辑关联订单表通过用户ID关联用户表通过车辆ID关联车辆表。实际建表时我建议保留逻辑关联即可不一定非要在数据库层面强加外键约束因为 MyBatis 设计关联查询时逻辑关联更加灵活也避免了一些插入和删除时的约束麻烦。2. 为什么这么选型技术栈背后的匹配逻辑这套系统的技术栈是 Java 系全栈开发里非常经典的一组搭配SpringBoot 做后端基础框架MyBatis 负责数据库访问MySQL 存数据Vue3 写前端页面前后端通过 JSON 格式的 RESTful API 通信。这套组合能成为主流不是因为哪一个技术“天下第一”而是因为它们匹配得恰到好处。下面把每一个环节的逻辑展开讲一下。2.1 SpringBoot为什么不是 SSH 或 SSMJava 后端这几年经历了从 SSHStruts2 Spring Hibernate到 SSMSpring SpringMVC MyBatis再到 SpringBoot 的演进。SSH 时代配置极其繁琐光是 XML 配置文件就能写几百行SSM 时期配置简化了一些但依然需要手动整合三大框架。SpringBoot 做的事情用一句话概括就是“约定优于配置”。它内置了 Tomcat 容器开发者不需要单独部署 Tomcat通过 starter 依赖机制引入一个场景只需要加一个依赖比如spring-boot-starter-web就包含了 SpringMVC、内嵌 Tomcat、JSON 解析等所有 Web 场景需要的依赖。放到汽车租赁系统这个场景里SpringBoot 带来的直接好处是一个main方法就能启动整个后端服务极大降低了环境搭建成本。对于学项目、做毕设的同学来说少踩一个环境坑就多一份心力放在业务逻辑上。2.2 Vue3 前后端分离的真实价值说到前后端分离很多人只是知道“前端一套工程、后端一套工程、接口通信”但对它解决的实际问题感受不深。我换个说法你就明白了以前用 JSP 或 Thymeleaf 做服务端渲染前端页面是嵌在后端工程里的改一个按钮样式要重启后端前后端分离之后前端工程独立部署像汽车租赁这种项目前端跑在localhost:5173后端跑在localhost:8080两者通过 axios 发请求通信互不干扰。Vue3 相比 Vue2 最大的变化是组合式 APIComposition API。以前写 Vue2 组件代码是按data、methods、computed、watch这些选项划分的一个稍复杂的页面逻辑会很散Vue3 里可以用setup把同一业务相关的数据和方法写在一起配合ref、reactive、computed等 API逻辑聚合度更高。这套系统用 Vue3 搭配 Vite 构建工具配合 Element Plus 组件库。Element Plus 里的表格、表单、日期选择器、消息提示、弹窗确认这些组件几乎覆盖了租赁管理系统后台页面的全部交互需求开发效率相当高。2.3 MyBatis灵活 SQL 与半 ORM 的取舍数据持久层选 MyBatis 而不是 Spring Data JPA最主要的理由是 MyBatis 把 SQL 的控制权完全交给开发者。Spring Data JPA 更强调“根据方法名推导 SQL”简单查询很方便但遇到复杂报表统计、多表关联、动态条件拼接时要么写 JPQL要么写原生 SQL反而没有 MyBatis 那么直接。汽车租赁系统里哪些地方最能体现 MyBatis 的价值一个是动态 SQL。车辆列表页需要按名称模糊搜索、按日租金排序、按座位数筛选这些条件有可能会同时出现也有可能会为空。在 MyBatis 里用if标签判断非空再拼接 SQL代码非常直观。另一个是自定义查询统计比如按日期分组统计订单数量这种 SQL 写起来没有任何额外的框架层限制。如果用 MyBatis-Plus其实开发效率会更高但很多课程设计和毕业设计的答辩环境里老师会更认可手写 SQL 的能力。所以这套系统用原生 MyBatis反而是一个“累但扎实”的选择。2.4 MySQL从建库到事务的落地场景MySQL 是这套系统最合理的存储选择因为项目复杂度不需要上 PostgreSQL 或 Oracle而且 MySQL 在 Java 生态里的兼容性和教程丰富度都是最好的。实操中需要注意的点有两个。第一个是数据库字符集。创建数据库时建议显式指定DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_general_ci。utf8mb4 比 utf8 多支持了 emoji 表情等四字节字符虽然租赁系统不一定用得到但这是个好习惯避免后续为字符集问题返工。第二个是事务。租车下单是一个典型的写操作场景插入订单记录、更新车辆状态为已出租这两步必须保证要么同时成功、要么同时失败。SpringBoot 里在 Service 方法上加Transactional注解就能搞定。如果不用事务一旦订单插入成功但车辆状态更新失败就会出现车被重复租出去的问题。3. 实操落地从建表建库到后端接口接下来进入真正的开发环节。我会按照“建数据库 - 建表 - 搭后端工程 - 实现接口”的顺序带你把项目跑通。这里很多操作是我在实际开发中反复验证过的照着做大概率不会踩坑。3.1 数据库初始化脚本拿到源码之后第一步是执行项目里的car_rental.sql脚本或者自己手动建库建表。如果你项目里没有现成脚本可以参考下面的核心建表语句逻辑去设计。CREATE DATABASE IF NOT EXISTS car_rental DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_general_ci; USE car_rental; CREATE TABLE sys_user ( id int(11) NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL COMMENT 登录名, password varchar(255) NOT NULL COMMENT 密码(MD5或BCrypt), nickname varchar(50) DEFAULT NULL COMMENT 昵称, phone varchar(20) DEFAULT NULL COMMENT 手机号, role tinyint(1) DEFAULT 1 COMMENT 1普通用户 2管理员, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB AUTO_INCREMENT1000 COMMENT用户表;关于用户表我多说一句role字段直接决定了后端的权限拦截逻辑。普通用户能访问租车下单、我的订单管理员能访问后台管理的相关接口。当然光靠前端隐藏按钮是不够的后端接口必须有权限校验否则懂技术的人直接调接口就能越权这属于安全细节不容忽视。车辆表的设计我就不再重复字段了重点说两个容易忽略的点一个是车辆图片字段建议存相对路径或 URL而不是把图片转成 Base64 存进数据库另一个是车辆状态字段最好加默认值比如默认1可用避免管理员录入车辆时漏选导致用户端看不到车。订单表是业务核心建表时注意索引设计。用户ID、车辆ID、订单状态、租车开始时间和结束时间这几个字段经常出现在查询条件中建议给它们加上普通索引。数据量小时现不出来数据量大了差距非常明显。3.2 后端工程结构与接口规范后端工程的包结构推荐这样划分com.example.carrental ├── controller // 前端请求入口 ├── service // 业务逻辑层 │ └── impl ├── mapper // MyBatis 接口 ├── entity // 数据库实体类 ├── dto // 数据传输对象接收前端请求参数 ├── vo // 视图对象返回给前端的响应数据 ├── config // 配置类跨域、拦截器、自定义异常处理 ├── common // 统一返回结果、常量、工具类 └── CarRentalApplication.java接口设计建议遵循 RESTful 风格比如车辆相关接口可以定义为功能请求方式路径分页查询车辆列表GET/api/car/list车辆详情GET/api/car/{id}新增车辆POST/api/car/add修改车辆PUT/api/car/update删除车辆DELETE/api/car/delete/{id}租车下单POST/api/order/add用户订单列表GET/api/order/myList管理员订单列表GET/api/order/adminList审核订单POST/api/order/review统一返回结果类我个人强烈建议封装。比如定义一个Result类包含code、msg、data三个字段所有接口都返回这个结构。这样前端 axios 拦截器只需要处理一种返回格式统一判断code 200即可。3.3 租车下单的事务与状态流转租车下单是这套系统里最具业务含金量的后端逻辑。除了把订单 insert 进去之外还要把对应车辆的状态改成“已出租”并且要把租车开始时间和结束时间跟已有订单做冲突校验——如果这辆车在这个时间段已经被租了就不能再下新单。时间冲突校验的逻辑不复杂查询已存在的订单中车辆ID等于当前车辆ID且订单状态不是“已取消/已拒绝”的记录判断当前下单单期是否与这些已有记录的时间段相交。SQL 大概长这样select idselectConflictOrders resultTypecom.example.carrental.entity.Order SELECT * FROM car_order WHERE car_id #{carId} AND status IN (0, 1) AND ( (start_time lt; #{endTime} AND end_time gt; #{startTime}) ) /select这个 SQL 里的逻辑很好理解新订单的开始时间早于或等于已有订单的结束时间且新订单的结束时间晚于或等于已有订单的开始时间就说明时间有重叠。这里我用了lt;和gt;因为在 XML 文件里小于号lt;和大于号gt;必须转义否则会解析报错。这个坑我第一次写的时候踩过现在看到 XML 里出现就会下意识检查。最后在 Service 层用Transactional包住整个方法任何一个环节失败就直接回滚数据一致性才有保障。4. 前端 Vue3 工程环境准备到页面跑通后端接口准备好了接下来的工作是把前端页面跑起来。这套系统的前端基于 Vue3 Vite Element Plus我按三个环节给你拆开讲环境准备、工程结构、核心功能实现。4.1 环境准备与工程创建Vue3 开发环境最简单的要求是 Node.js 版本。Vite 对 Node 版本有要求建议直接用 Node 18 以上版本省去很多兼容性烦恼。安装完 Node 后在命令行执行node -v和npm -v能输出版本号就说明环境没问题。创建 Vue3 工程我推荐用 Vite 的脚手架命令npm create vitelatest car-rental-front -- --template vue创建完进入工程目录安装依赖cd car-rental-front npm install接下来安装项目里需要的额外依赖路由是必须的状态管理看项目需求HTTP 请求库用 axiosUI 库用 Element Plus。安装命令如下npm install vue-router4 axios element-plus element-plus/icons-vue这里踩过的一个坑是版本匹配问题。比如 vue-router 必须装 4.x 版本因为 Vue3 对应的是 vue-router 4如果误装 v3 就会跑不起来Element Plus 同理必须和 Vue3 搭配使用。这个在使用 npm 安装时稍加注意即可。4.2 前端目录结构与基础封装前端的目录结构我推荐按下述方式组织src ├── api // 所有接口调用的封装文件 ├── assets // 静态资源 ├── components // 公共组件 ├── router // 路由配置 ├── store // Pinia状态管理 ├── views // 页面级组件 │ ├── admin // 管理员页面 │ └── user // 用户页面 ├── utils // 工具函数如 request.js axios封装 ├── App.vue └── main.jsaxios 封装是必不可少的环节。把 baseURL 统一设成后端地址http://localhost:8080再通过拦截器做两件事请求时从 localStorage 取出 token 放到请求头响应时统一处理业务错误码比如 code 不是 200 时直接弹出错误消息401 时跳转登录页。import axios from axios import { ElMessage } from element-plus import router from ../router const request axios.create({ baseURL: http://localhost:8080, timeout: 10000 }) request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] token } return config }) request.interceptors.response.use( response { const res response.data if (res.code ! 200) { ElMessage.error(res.msg || 请求失败) return Promise.reject(new Error(res.msg)) } return res }, error { if (error.response error.response.status 401) { localStorage.removeItem(token) router.push(/login) } ElMessage.error(error.message || 网络异常) return Promise.reject(error) } ) export default request这样封装的好处是页面里不用每次请求都手动处理错误提示代码会干净很多。我在改项目时第一步基本都会封装好 request后面写接口调用就是几行代码的事。4.3 核心页面实现与路由守卫用户端的核心页面是车辆列表和租车下单弹窗。车辆列表页用 Element Plus 的el-table展示车辆信息用el-pagination做分页通过条件搜索表单实现按车辆名称、品牌筛选。点击“租车”按钮弹出el-dialog里面放两个el-date-picker选择开始和结束时间下方实时计算总租金。这里有一个小技巧日期选择器里设置:disabled-date禁止选择今天之前的日期减少后端校验压力也是对用户体验的一种提升。管理员端页面则是一个典型的后台管理系统左侧菜单栏、顶部面包屑、右侧内容区。菜单项包括“车辆管理”“订单管理”“用户管理”。每个页面对应一张表加搜索、新增、编辑、删除操作组合Element Plus 的el-table配合el-form能很快搞定。路由守卫是必须做的。Vue3 里在 router 配置中加beforeEach全局前置守卫判断 localStorage 里有没有 token没有就去登录页同时还要判断目标路由需要的角色权限比如/admin开头的路由只有管理员能进。这样的话即使有人知道管理员页面的 URL没有权限也进不去前后端的权限校验形成双保险。5. 常见问题与排查技巧实录最后一个环节也是我最有底气分享的环节——实际问题排查。前后端分离项目第一次跑通往往不是代码逻辑难住你而是环境、配置、版本这些问题堆在一起让人抓狂。下面挑几个出现频率最高的问题讲清楚。5.1 启动类问题后端起不来前端装不上后端最常见的启动失败原因是端口占用。SpringBoot 默认占用 8080 端口如果本机已经有其他服务占了 8080启动就会报端口被占用。解决办法有两个关掉占用端口的进程或者在application.yml里改server.port。前端项目npm install慢或者报错的情况也很常见。我的建议是切换 npm 镜像源在命令行执行npm config set registry https://registry.npmmirror.com下载速度会快很多。如果你使用的是 pnpm 或 yarn也都有对应的镜像配置方式。5.2 跨域问题请求发出去了但浏览器拦截了前后端分离必然遇到跨域问题。你在前端页面发起请求浏览器地址栏是localhost:5173请求目标是localhost:8080端口不同就触发了浏览器的同源策略。解决办法有几种后端配置全局跨域、前端 Vite 配置代理、用 Nginx 反向代理。我建议项目开发阶段用 Vite 代理生产部署用 Nginx。Vite 代理配置如下server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } }配置了代理之后前端请求的 baseURL 直接写成/api即可浏览器看到的是同源请求跨域问题自然消失。这个方案还顺带解决了上线后接口地址硬编码的问题。5.3 MyBatis 日期比较和缓存问题写 MyBatis 查询时有一个很容易出错的点日期字段比较。Java 的LocalDateTime传递到 XML 里如果你直接写#{startTime}MyBatis 可以正确传参。但如果涉及lt;这类符号没转义或者在if标签里比较字符串和数字类型时类型不一致SQL 就会执行报错。MyBatis 缓存这里也多说一句。MyBatis 有一级缓存和二级缓存一级缓存默认开启同一个 SqlSession 内相同查询会直接返回缓存结果。在写租车时间冲突校验这类查询时一定要保证用到的是最新的数据必要时在 Mapper 接口的方法上配置flushCachetrue或使用Options(flushCache Options.FlushCachePolicy.TRUE)避免查询到脏数据。5.4 数据库连接的时区与驱动版本数据库连接串里有一项必须加就是时区配置。使用 MySQL 8.x 驱动时URL 一定要带上serverTimezoneAsia/Shanghai否则会报时区错误或者日期时间差 8 小时。完整的连接串示例spring: datasource: url: jdbc:mysql://localhost:3306/car_rental?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse username: root password: 你的密码 driver-class-name: com.mysql.cj.jdbc.Driver驱动类名也有讲究。MySQL 5.x 用com.mysql.jdbc.DriverMySQL 8.x 用com.mysql.cj.jdbc.Driver。很多人项目连不上数据库排查半天最后发现是驱动类名写错了这种低级错误真的很浪费时间所以一开始就要检查自己的 MySQL 版本对应哪个驱动。这套汽车租赁系统从业务模型到技术实现完整走完一遍后你对 SpringBoot Vue3 MyBatis MySQL 这条技术链路的理解会有一个明显的提升。它不是一个“玩具项目”而是一个具备真实业务状态的完整闭环有状态流转、有时间冲突校验、有事务处理、有角色权限这些恰恰是工作中最常见的需求场景。如果你打算在这个基础上做二次开发我个人建议优先从三个方向下手一是接入支付模拟流程让下单到支付形成完整链路二是增加 Spring Security 或 Sa-Token 做更细粒度的权限控制三是引入 Redis 缓存热门车辆信息和会话管理把性能优化这个亮点加进去。踩过几次坑之后你会发现自己对框架的理解已经完全不是停留在“会调用接口”的层面了。