首页 / 资讯中心 / 文章详情

SpringBoot+MyBatis-Plus车辆管理系统实战:架构、数据模型与避坑指南

SpringBoot+MyBatis-Plus车辆管理系统实战:架构、数据模型与避坑指南 ★ FEATURED ARTICLE
简介基于SpringBoot的车辆管理系统是一套面向本科与高职毕业设计的项目资源系统以管理员和用户两类角色展开覆盖个人中心、用户管理、车辆管理、保养登记、事故登记、维修登记、违章登记等核心模块采用SpringBootVueMySQLMaven技术组合适合Java方向学生用于毕设参考、课程设计或二次开发练习。资源包共5个文件含源码zip、数据库sql、设计文档docx、答辩PPT和说明txt整体约16.69MB。目前已有46人学习下载。源码配合数据库脚本即可快速运行文档覆盖需求分析、数据库设计、模块实现的完整思路PPT可帮助梳理答辩流程借助这套资源读者能减少环境搭建与材料整理时间掌握车辆登记、违章、事故、维修等业务场景的落地实现适合需要高效完成毕业设计系统并准备答辩的高校学生。1. 基于SpringBoot的车辆管理系统先问自己要不要做再问怎么做一家有50台车的运输公司最值钱的信息往往不在ERP里而在调度员的Excel和微信聊天记录中。基于SpringBoot的车辆管理系统核心任务是把车辆档案、驾驶员信息、行程台账、维修保养和保险过户这些零散数据收进一个在线后台让查询、统计和交接不再依赖个人经验。它最适合两类人一类是校招、毕设或刚入职的开发新人需要一套结构完整、能跑通的案例另一类是只有几十台车的中小企业想用一个内部系统替代表格管理。这套编号326的交付包只要把源码、数据库脚本、项目文档和PPT理顺起步门槛反而在需求边界。先把范围定成「管车、管司机、管行程、管维修」再考虑要不要接OA和财务系统。2. 把SpringBoot后端跑起来SpringBoot配置、工程结构与核心依赖2.1 管理类系统为什么选SpringBoot而不是SSH或纯Servlet这个系统属于典型的管理信息系统业务集中在增删改查、审批流和报表没有高并发、没有复杂分布式事务。SpringBoot的价值不是让代码写得少而是把SSH里那一堆XML配置、监听器、事务代理收进自动配置里开发者能把精力放在车表、订单表、维修表这些业务对象上。换到现在的招聘环境懂SpringBoot的候选人也远比懂SSH的多后续维护成本更低。如果用纯Servlet加JDBC去写一个车辆列表页面至少要写八层代码连分页都要自己拼limit遇到事务还得手动管理连接。而用SpringBoot加Spring MVC一个RestController注解加一个Service方法就能完成同样的事。我一般会提醒刚入门的人管理类系统的技术栈选择第一条标准是「团队是否接手得住」第二条才是性能。SpringBoot版本选择是个容易被忽略的细节。2.7.x仍然是管理类项目里最稳妥的选择一方面它基于javax和现成脚手架、MyBatis-Plus 3.x兼容性最好另一方面它不需要Java 17拿毕业设计或者做企业内网部署时少踩很多系统环境问题。SpringBoot 3.x当然更好但如果你没有足够时间处理Jakarta命名空间和版本变更还是慎用。2.2 SpringBoot项目结构按业务模块拆包别把全家桶堆在controller里我见过不少车辆管理系统把controller、service、mapper按层拆成三个大包结果业务一复杂新增一个「车辆维修」功能要跨六个包改文件。更好的做法是先把业务模块分清楚再在每个模块里做MVC分层。下面是这套系统推荐的基础目录结构vehicle-system/ ├── src/main/java/com/car/admin/ │ ├── common/ # 统一返回体、异常处理、结果码 │ ├── config/ # 分页插件、拦截器、Jackson时间序列化 │ ├── modules/ │ │ ├── auth/ # 登录认证、用户Token │ │ ├── vehicle/ # 车辆信息 │ │ ├── driver/ # 驾驶员档案 │ │ ├── trip/ # 出车/行程记录 │ │ ├── maintenance/ # 维修保养 │ │ └── report/ # 统计报表 │ └── VehicleApplication.java ├── src/main/resources/ │ ├── mapper/ # 手写SQL的XML文件 │ ├── sql/ # 数据库初始化脚本 │ └── application.yml └── pom.xml按模块拆包的好处很直接改车辆状态时只需要打开modules/vehicle不会误碰驾驶员模块的代码。common、config这种横切层单独放避免模块之间互相引用时出现循环依赖。resource目录里单独放sql文件这样拿到源码的新人不会在几百行初始化SQL里找不到入口。这里有一个细节虽然MyBatis-Plus支持BaseMapper直接CRUD但复杂报表查询还是建议用XML写SQL。所以编译期配置里要保留mapper-locations不要把mapper目录删掉。另外启动类的位置放在com.car.admin根包下SpringBoot默认扫描才能覆盖所有子包不然Component和Mapper都扫不到。2.3 从空项目到第一个接口SpringBoot配置和依赖的取舍新建一个SpringBoot项目时pom.xml里只需要保留真正用到的依赖。管理类系统最常用的是spring-boot-starter-web、MyBatis-Plus、MySQL驱动、Lombok再加一个参数校验的starter。这是经过验证的依赖配置parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version /parent dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.5/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId scopeprovided/scope /dependency /dependencies这里没有引入spring-boot-starter-data-jpa也没有引Druid连接池因为Druid在MyBatis-Plus环境里需要额外配置监控项对小型管理后台价值有限。如果后续报表查询压力上来了再加连接池和缓存也不迟。注意MyBatis-Plus版本要和SpringBoot 2.7兼容换到3.x需要确认是否已经切换到Jakarta命名空间。接下来是本系统的核心配置文件application.yml。这里最容易翻车的是数据库连接串server: port: 8380 servlet: context-path: /vms spring: datasource: url: jdbc:mysql://localhost:3306/vehicle_system?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus: mapper-locations: classpath*:mapper/**/*.xml global-config: db-config: id-type: auto logic-delete-field: deletedserverTimezoneAsia/Shanghai必须写否则MySQL的CST时区会跟本机时间产生8小时偏差context-path/vms让所有接口都带/vms前缀方便前后端联调时直接区分服务。logic-delete-field配置加了之后MyBatis-Plus执行delete方法时不会物理删除而是把deleted字段改成1这对车辆档案这种要留痕的数据很关键。启动类要放在根包下并且不要忘记扫描MapperSpringBootApplication MapperScan(com.car.admin.modules.*.mapper) public class VehicleApplication { public static void main(String[] args) { SpringApplication.run(VehicleApplication.class, args); } }写完之后运行mvn spring-boot:run浏览器访问/vms/vehicle/ping能看到返回结果就说明SpringBoot基础链路通了。后面的auth模块再按同样的方式补整个骨架就立起来了。提示如果MySQL版本是8.0以上驱动类必须写com.mysql.cj.jdbc.Driver老版本才用com.mysql.jdbc.Driver。3. 把数据库增删改查写进车辆模块6张表、索引与分页查询3.1 车辆管理系统的数据模型6张表让业务闭环车辆管理系统最常见的失败是表建得不够或者建得太散。一个可以闭环的最小数据模型至少包括6张表车辆表、驾驶员表、行程表、维修保养表、保险表、用户表。车辆表关联驾驶员作为当前负责人行程表记录每一次出车维修保养表记录维修和保养记录保险表记录保险到期和理赔信息用户表负责登录和权限。下表是这套系统的核心表字段设计拿过来可以直接参考表名核心字段说明vehicleid, plate_number, vehicle_type, brand_model, buy_date, status, deleted车辆唯一档案车牌唯一driverid, name, license_no, phone, status驾驶员档案驾驶证号唯一tripid, vehicle_id, driver_id, start_mileage, end_mileage, start_time, end_time, purpose每次出车的里程和事由maintenanceid, vehicle_id, type, amount, maintenance_date, remark维修保养记录金额精确到分insuranceid, vehicle_id, insurer, expire_date, amount保险到期提醒sys_userid, username, password, real_name, role_id登录用户与权限车辆表的核心字段我会加三样东西车牌唯一索引、状态字段、逻辑删除标记。车牌号是业务天然唯一键必须直接在数据库层挡住重复数据status用TINYINT表示0正常、1维修、2停用、3报废比VARCHAR更容易做报表统计deleted字段配合MyBatis-Plus的逻辑删除避免删除车辆后行程记录变成孤儿数据。下面给出建表SQLCREATE TABLE vehicle ( id BIGINT PRIMARY KEY AUTO_INCREMENT, plate_number VARCHAR(20) NOT NULL COMMENT 车牌号, vehicle_type VARCHAR(30) COMMENT 车辆类型, brand_model VARCHAR(50) COMMENT 品牌型号, buy_date DATE COMMENT 购置日期, status TINYINT DEFAULT 0 COMMENT 0正常 1维修 2停用 3报废, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, deleted TINYINT DEFAULT 0 COMMENT 逻辑删除标记, UNIQUE KEY uk_plate (plate_number), KEY idx_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT车辆信息表;建表SQL里使用了utf8mb4字符集和InnoDB引擎。外键约束不推荐在表之间强制加因为MyBatis-Plus的关联查询能力有限加了外键反而给逻辑删除和数据导入带来麻烦。其他5张表按同样的规则建金额字段全部用DECIMAL(10,2)时间字段用DATETIME不用字符串存时间。3.2 MyBatis-Plus还是JPA管理类系统我选MyBatis-Plus的三个理由争论MyBatis-Plus和JPA谁更好的话题几乎每个项目都要来一轮我的结论很直接做这种以数据库增删改查为主的管理后台无脑选MyBatis-Plus。第一是CRUD零SQLBaseMapper和LambdaQueryWrapper能覆盖八成以上的单表查询第二是分页插件统一不用手动写limit第三是它在国内生态里普及度高团队成员上手成本低。JPA被诟病最多的是黑匣子问题在实体关系映射上尤其明显。车表关联行程表、司机表用JPA一段OneToMany就能把数据带出来但懒加载异常和N1查询排查起来像剥洋葱。MyBatis-Plus虽然也提供关联查询但更多时候还是靠SQL控制数据权限、报表统计反而更好写。所以我在架构阶段就会定下主框架用MyBatis-Plus复杂统计用XML手写SQL两边各管各的。分页插件必须单独配置否则selectPage不会触发MySQL的limit逻辑。Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }PaginationInnerInterceptor的参数DbType.MYSQL是必填的不填的话分页插件会猜测数据库类型在MySQL环境下有可能生成错误的分页方言。加了之后调用selectPage时它会自动在查询SQL上追加limit、offset代码里不需要再手工拼。3.3 车辆模块的增删改查条件分页、车牌校验和逻辑删除接下来直接看车辆模块的一个完整查询接口。这个接口同时处理车牌号模糊查询和状态筛选是管理端列表最常遇到的需求RestController RequestMapping(/vms/vehicle) RequiredArgsConstructor public class VehicleController { private final VehicleService vehicleService; GetMapping(/page) public ResultPageVehicle page(RequestParam(required false) String plateNumber, RequestParam(required false) Integer status, RequestParam(defaultValue 1) int page, RequestParam(defaultValue 10) int size) { return Result.ok(vehicleService.pageQuery(plateNumber, status, page, size)); } }Controller只做参数接收和结果包装。核心逻辑在Service实现里这也是方便加数据权限拦截的地方public PageVehicle pageQuery(String plateNumber, Integer status, int page, int size) { LambdaQueryWrapperVehicle wrapper Wrappers.VehiclelambdaQuery() .like(StringUtils.hasText(plateNumber), Vehicle::getPlateNumber, plateNumber) .eq(status ! null, Vehicle::getStatus, status) .orderByDesc(Vehicle::getCreateTime); return vehicleMapper.selectPage(new Page(page, size), wrapper); }LambdaQueryWrapper里的第一个参数是布尔条件plateNumber为空就直接跳过不会拼出like %%这种浪费性能的条件。orderByDesc用create_time而不是update_time因为列表页要的是新增顺序而不是修改顺序。这里的参数说明很重要分页查询不要用前端传来的页码直接当limit起始值MyBatis-Plus的Page对象内部会处理current、size到offset的转换业务层不需要关心。新增车辆时最常见的错误是只在前端判断车牌重复后端不做任何防护。如果两个人同时提交还是会插入两条重复车牌。所以Service里新增方法要同时做数据库唯一索引和代码层校验public ResultVehicle addVehicle(Vehicle vehicle) { long count vehicleMapper.selectCount(Wrappers.VehiclelambdaQuery() .eq(Vehicle::getPlateNumber, vehicle.getPlateNumber())); if (count 0) { return Result.fail(车牌号已存在); } vehicle.setStatus(0); vehicleMapper.insert(vehicle); return Result.ok(vehicle); }多一道selectCount的意义在于能给前端返回「车牌号已存在」这样的可读提示而不是让用户面对一个DuplicateKeyException。数据库唯一索引是最后一道防线两个要同时存在。修改车辆时用updateById它会根据主键更新但要注意status为空时不会覆盖原值这是MyBatis-Plus的默认行为。删除车辆不调用deleteById而是改为更新deleted1或者用MyBatis-Plus自带的逻辑删除拦截器去处理。4. 车辆管理系统排查手册5个常见翻车点的现象、原因、解决这类项目跑起来不难难的是上线后出现一堆看似没有规律的问题。我把最常见的5个翻车点按「现象、原因、解决」记录在这里开发时可以提前设防维护时也可以直接对号入座排查。4.1 数据层的三个坑时间偏差、车牌重复、字符集报错坑一页面保存的用车时间入库后少了8小时。现象前端选择14:00创建行程数据库里存的是06:00列表页读出来还是06:00对账直接对不上。原因JDBC连接串里没配serverTimezone或者MySQL所在服务器时区是UTC/CST混用。Spring Boot容器取系统时区JDBC驱动按数据库会话时区转换两边各算各的就出现了固定的8小时偏差。解决把jdbc url改成serverTimezoneAsia/Shanghai同时执行SET time_zone 08:00;把MySQL会话时区同步。改完后先重启应用再从库里用select now()对照一次确认前后一致。这个配置要写在每个环境的数据源URL里不能只改开发机。坑二同一车牌号的车辆在列表里出现两条。现象车辆管理列表里看到“京A12345”两条记录导出Excel后才发现重复但新增页面明明做了重复提示。原因数据库表没给plate_number加唯一索引应用层校验被绕过。手工导入、脚本刷数据时两次insert都没走Controller里的selectCount校验自然就插进去了。解决先清理重复数据然后执行ALTER TABLE vehicle ADD UNIQUE KEY uk_plate (plate_number);把最后一道防线补上。Service里的selectCount校验保留它负责返回友好提示避免前端直接面对DuplicateKeyException。坑三保存司机姓名或备注时报“Incorrect string value”。现象新增司机接口输入姓名或备注值里带emoji或生僻字提交后接口直接500后台日志抛出SQLException。原因数据库表和连接串的字符集还是utf8。MySQL的utf8只是utf8mb3只能存3字节字符emoji是4字节根本存不进去。解决建库建表时都用DEFAULT CHARSETutf8mb4jdbc url里写characterEncodingutf8。这里的utf8指客户端编码不是MySQL存储编码不要多写一个mb4进去。已经产生乱码的表先ALTER TABLE vehicle CONVERT TO CHARACTER SET utf8mb4;转换再确认业务数据没有丢失。4.2 接口和统计的两个坑越权修改和SUM返回null坑四普通登录用户可以修改其他部门的车辆状态。现象一个普通调度员调用修改车辆接口直接把其他分公司的车辆全改成报废状态前端页面根本没有对应的按钮。原因后端只做了登录拦截没做数据权限。修改接口只接收vehicleIdservice层没有校验这条车辆是否属于当前用户所在部门。前端隐藏按钮只能挡住操作路径拦不住直接请求后端的同事。解决在service层从登录上下文拿当前用户的部门ID修改前先查一次vehicle的companyId或部门ID不一致直接返回“无权限”。查询列表时也要在queryWrapper上强制加部门过滤条件而不是靠前端传参。坑五维修费用报表SUM结果为null金额合计出现一分钱误差。现象报表页面维修费用和保险金额为空导出Excel后金额小数位对不上财务找过来质疑数据。原因空表上SUM(amount)返回NULL许多模板引擎渲染NULL就是空白。金额字段如果用了FLOAT或DOUBLE二进制浮点累加必然出现精度漂移。解决报表SQL改成SELECT IFNULL(SUM(amount), 0) AS total FROM maintenance WHERE vehicle_id #{id}金额字段建表用DECIMAL(10, 2)Java侧用BigDecimal接收。开发环境先用空表跑一次SQL不要等上线后再测。5. 万字文档和PPT的组织把源码变成能答辩、能交付的东西5.1 文档结构四个部分撑起一万字不靠截图堆页数标题里的「万字文档」通常意味着交付给甲方或者导师评审的东西不是随手写的Readme。我一般会按四个部分组织文档保证每个部分都指向「为什么这样做」和「怎么跑起来」。下面是一份可以直接套用的章节结构文档章节核心内容篇幅需求分析项目背景、用户角色、功能需求用例、数据流2500字系统设计总体架构、技术选型、模块划分、数据库设计3000字核心实现登录认证、车辆CRUD、行程记录、报表统计、关键代码说明3000字测试与部署测试环境、核心测试用例、运行步骤、打包部署2500字写需求分析时文字在精不在多但用例表一定要有比如「管理员新增车辆」「调度员创建行程」「财务查看维修费用报表」这些才是车辆管理系统的差异化内容。系统设计部分把第三章的表结构和接口返回值粘贴进去再加一页ER图。核心实现部分别贴完整Controller代码而是贴关键方法并逐行解释参数。测试与部署部分写清楚JDK版本、数据库版本、Maven仓库地址、启动命令缺一个别人都跑不起来。这套结构的另一个作用是能直接生成目录树让评审专家一眼看到工作量。很多万字文档会把需求描述、界面截图、代码注释都算进去但只要每个表格不是空壳看的人不会觉得注水。5.2 答辩PPT10页逻辑让评委快速听懂车辆管理系统PPT是用来讲故事的不是用来放文档的。10页已经足够每页只留一个核心观点。我习惯把内容做成下面这10页骨架页面标题关键内容1封面项目名、技术栈、姓名2背景与问题Excel管理车辆的痛点3功能模块图六大模块一张图4技术架构SpringBoot MyBatis-Plus MySQL5数据库设计6张表核心表结构和ER图6核心功能演示车辆列表、行程录入、报表7关键代码展示条件分页、逻辑删除8测试结果核心接口的测试用例和截图9项目难点与解决并发车牌防重、统计精度10总结与展望可扩展方向其中第6页是决定答辩效果的一页。宁可少放两张架构图也要把车辆列表查询接口的实时效果录成动态图或者在现场操作一遍。第9页讲一个真实的坑比罗列五个功能有用得多。每页PPT的正文不超过3行其余用截图和表格支撑。5.3 源码交付清单三步跑起来避开交付翻车最后把源码交给别人时我一定会检查四类内容源码目录是否包含初始化SQL、配置文件是否去掉了真实密码、文档里的运行步骤是否和实际代码一致、PPT里的功能图是否需要更新。最常翻车的地方是数据库脚本版本和代码实体字段不一致导致别人导入后启动直接报错。为了减少来回沟通这一套编号326的交付包源码、SQL、文档、PPT四个目录缺一不可。数据库初始化脚本一定要能用一个命令跑完mysql -uroot -p -e CREATE DATABASE vehicle_system DEFAULT CHARACTER SET utf8mb4; mysql -uroot -p vehicle_system sql/vehicle_system.sql第一个命令创建数据库并指定字符集第二个命令导入初始化数据。注意第二个命令会把sql/vehicle_system.sql里的建表和初始数据一次性导入。如果MySQL 8.x的密码包含特殊字符建议改用-e source /path/to/sql方式否则会解析错。导入完成后把application.yml里的spring.datasource.password改成自己的本地密码再执行mvn spring-boot:run访问/vms/vehicle/ping。交付源码时不要把target目录和三方jar包含进去只发src、sql和doc三个目录另外在README里写明JDK版本、Maven版本、MySQL版本就能省掉大量答疑时间。注意交付前一定要在全新环境执行一次mvn clean package确认没有遗漏本地依赖再打zip包发出去。6. 最小冒烟测试一条命令验明车辆管理系统真正跑通接手任何一个SpringBoot项目我第一件事不是看设计文档而是先跑冒烟。我把最小冒烟定义为服务能启动、登录能拿到Token、列表接口能返回数据。这个三层链路验证下来数据库连接、权限配置、Mapper扫描都没问题后面往上叠功能才有底。下面这段脚本可以直接放到项目根目录假设默认登录账号admin密码admin123端口8380#!/usr/bin/env bash BASE_URLhttp://localhost:8380/vms TOKEN$(curl -s -X POST $BASE_URL/auth/login \ -H Content-Type: application/json \ -d {username:admin,password:admin123} | jq -r .data.token) curl -s $BASE_URL/vehicle/page?page1size10 \ -H Authorization: Bearer $TOKEN | jq .data.total如果机器上没有jq可以把jq换成python3 -c import sys,json; print(json.load(sys.stdin)[data][total])。这串命令里最关键的是Authorization头它说明车辆管理系统的接口并不是暴露给所有人的登录鉴权已经生效。返回.total字段如果大于等于0说明Mapper层、数据源、分页插件都正常不需要再登录前端系统。冒烟脚本的覆盖范围要刻意收敛不把新增车辆、删除车辆这些写操作放进去。写操作会污染测试数据尤其在做毕业设计演示时删掉一条重要记录就尴尬了。真正上线前还要单独跑一遍事务回滚测试和权限测试冒烟测试只管「通不通」不管「对不对」。我现在带项目时已经形成一个习惯凡是新同事接手这套代码第一天只允许跑冒烟脚本和看文档不允许改动任何Mapper XML。跑通了说明环境没问题接下来再谈改代码。这个习惯帮我省下太多排障时间也希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站