☰
SpringBoot+Vue+MyBatis前后端分离图书管理系统实战详解
2026/10/6 3:29:17 网站建设 项目流程

这套图书管理系统算是我接触过的前后端分离项目里比较有代表性的一个,技术栈很经典:SpringBoot扛后端,Vue负责界面,MyBatis处理数据库交互,MySQL存数据。对于正在学Java后端、或者准备做毕业设计、又或者想搞一个能完整跑通前后端分离流程的练手项目的人来说,这套组合是绕不开的标配。我把整个项目的源码思路、数据库设计、接口实现、前端页面、部署流程以及我自己踩过的坑从头到尾梳理一遍,照着这个思路走,你不仅能把这个项目跑起来,还能真正理解每个环节为什么这么做。

1. 技术选型与整体架构拆解

1.1 为什么是这套技术栈

先聊选型。图书管理系统这种业务,说白了就是典型的CRUD加一点关联查询,业务复杂度不高,但又需要覆盖登录鉴权、分页搜索、借还书流程这些常见功能。用SpringBoot+Vue+MyBatis+MySQL这套组合,在国内Java开发圈子里几乎是最主流、资料最多、遇到问题最好搜方案的一套搭配。

SpringBoot选它的理由不用多讲,简化配置、内嵌Tomcat、起步依赖机制,省掉了传统SSH或SSM那一堆XML配置的麻烦。它能让一个后端服务从零到跑起来只用几分钟,这对学习阶段非常重要,因为精力应该花在业务逻辑上,而不是耗在环境配置里。

前端选Vue,是因为它组件化开发和响应式数据绑定的思路非常符合管理后台这种交互密集的场景。图书管理系统的页面基本就是表格、表单、弹窗、分页这一套,用Vue的组件系统拆完之后,代码组织会很清晰,维护起来也舒服。

MyBatis在这套系统里的角色很关键。它不是全自动ORM,SQL还是要自己写,但正因为这样,你对SQL的执行过程、结果映射、动态SQL的拼装逻辑会掌握得更扎实。图书管理里经常需要按书名、作者、分类、状态做组合条件查询,用MyBatis的动态SQL可以很优雅地处理这类"条件不确定"的场景,比在Java代码里手动拼字符串SQL要安全可靠得多。

MySQL作为数据库没什么好争议的,开源免费、部署简单、资料丰富。图书管理这种规模的数据量,MySQL的性能绰绰有余,配合Navicat或命令行工具管理起来也顺手。

1.2 前后端分离的核心交互逻辑

了解"前后端分离"这几个字背后的运行机制,比跑通项目本身更重要。所谓分离,是指前端和后端作为两个独立的应用分别开发和部署,前端跑在浏览器里,后端跑在服务器上,二者通过HTTP接口通信,数据格式统一用JSON。

具体到图书管理系统:你在浏览器里打开Vue开发服务器(默认8080端口),页面上的操作比如点"查询图书",前端会向后端接口发一个HTTP请求;后端SpringBoot应用(默认8081端口或你自己配的端口)接收到请求后,通过Controller接收参数、调用Service处理业务逻辑、再通过Mapper接口配合MyBatis去操作MySQL数据库,最后把查询结果转成JSON返回给前端;前端拿到JSON后更新页面表格数据。整个过程中前端不直接操作数据库,数据库的访问权限完全收口在后端,这是分离架构最核心的安全边界。

分离开发带来的一个直接问题就是跨域。前端8080端口请求后端8081端口的接口,浏览器会拦截这种跨域请求。解决方案通常两种:后端配置CORS(跨域资源共享)过滤器,在前端开发环境下配代理转发。

1.3 项目目录结构规划

项目拿到手先别急着跑,把目录结构理清楚,后面改代码会省很多事。一个标准的前后端分离项目,通常是两个独立目录:

book-manage/ ├── backend/ # SpringBoot后端 │ ├── src/main/java/com/example/bookmanage/ │ │ ├── controller/ # 接口层 │ │ ├── service/ # 业务逻辑层 │ │ ├── mapper/ # MyBatis Mapper接口 │ │ ├── entity/ # 实体类 │ │ ├── common/ # 通用返回结果、异常处理 │ │ └── config/ # 跨域、拦截器配置 │ └── src/main/resources/ │ ├── mapper/ # MyBatis的XML映射文件 │ └── application.yml └── frontend/ # Vue前端 ├── src/ │ ├── api/ # 接口请求封装 │ ├── router/ # 路由配置 │ ├── views/ # 页面组件 │ ├── components/ # 通用组件 │ ├── store/ # 状态管理(Vuex/Pinia) │ └── utils/ # axios封装等工具 └── package.json

这样的分层逻辑很清晰:后端Controller只做参数接收和数据返回,Service处理业务规则,Mapper只负责和数据库打交道;前端每个页面视图对应api目录下的一个接口模块,改页面不影响接口逻辑,改接口不影响页面结构。项目扩展新功能的时候,比如加一个"图书分类管理",只需在后端加一套Controller-Service-Mapper,在前端加一个页面组件和对应的api方法,其他代码不用动。

2. 数据库设计与MyBatis实战要点

2.1 核心表结构设计

图书管理系统的数据模型不算复杂,但表关系设计好不好,直接影响后面代码好不好写。我的建议是最少设计四张表:用户表、图书表、借阅记录表、分类表(可选但推荐)。

先看用户表(user):

CREATE TABLE `user` ( `id` INT AUTO_INCREMENT PRIMARY KEY COMMENT '用户ID', `username` VARCHAR(50) NOT NULL UNIQUE COMMENT '用户名', `password` VARCHAR(255) NOT NULL COMMENT '密码,存储加密后的密文', `real_name` VARCHAR(50) DEFAULT NULL COMMENT '真实姓名', `role` TINYINT DEFAULT 1 COMMENT '角色:0管理员 1普通用户', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

密码字段务必用加密存储,我见过很多新手直接存明文,这是非常危险的习惯。密码加密可以用BCrypt,Spring Security里自带这个工具,也可以用MD5加盐。推荐BCrypt,它会自动加盐,而且每次hash的结果都不一样,安全性远高于裸MD5。

图书表(book)设计时要注意几个字段:

CREATE TABLE `book` ( `id` INT AUTO_INCREMENT PRIMARY KEY, `isbn` VARCHAR(20) DEFAULT NULL COMMENT 'ISBN号', `book_name` VARCHAR(100) NOT NULL COMMENT '书名', `author` VARCHAR(50) DEFAULT NULL COMMENT '作者', `publisher` VARCHAR(100) DEFAULT NULL COMMENT '出版社', `category_id` INT DEFAULT NULL COMMENT '分类ID', `stock` INT DEFAULT 0 COMMENT '库存总量', `available` INT DEFAULT 0 COMMENT '可借数量', `status` TINYINT DEFAULT 1 COMMENT '状态:1上架 0下架', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

这里我特别要强调stock和available两个字段的区别。stock是这本书一共多少本,available是当前还有多少本可借。借书时available减1,还书时available加1,stock不变。如果只用一个数量字段,就没法知道一本书丢没丢、有多少本在读者手里,统计起来很费劲。

借阅记录表是业务核心,外键关联用户和图书:

CREATE TABLE `borrow_record` ( `id` INT AUTO_INCREMENT PRIMARY KEY, `user_id` INT NOT NULL COMMENT '借阅人ID', `book_id` INT NOT NULL COMMENT '图书ID', `borrow_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '借书时间', `due_time` DATETIME COMMENT '应还时间', `return_time` DATETIME DEFAULT NULL COMMENT '实际归还时间', `status` TINYINT DEFAULT 1 COMMENT '1借出 2已还 3逾期' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

特别注意借阅和归还都是这个表。归还时不是删掉记录,而是把return_time写入、把status改成已还,这样每本书的借阅历史就有迹可循。

四张表建好后,给borrow_record表的user_id和book_id分别加上索引,给book表的book_name加上普通索引,查询性能会有明显提升。数据量小的时候感觉不出来,但好习惯要养起来。

2.2 MyBatis的XML映射与动态SQL

MyBatis的使用中,最核心的是Mapper接口和XML映射文件的配合。我习惯把复杂的SQL写在XML里,简单查询用注解,不过图书管理系统里的组合条件查询,用XML的动态SQL最合适。

一个很典型的需求场景:图书列表页的搜索功能,用户可能只输入书名,可能只输入作者,也可能什么都不输入直接点查询。这时候后端不能写死SQL,必须根据前端传来的参数动态拼SQL。MyBatis提供了<if>、<where>、<set>、<foreach>这些标签,专门解决这类问题。

看这个例子:

<select id="searchBooks" resultType="com.example.bookmanage.entity.Book"> SELECT * FROM book <where> <if test="bookName != null and bookName != ''"> AND book_name LIKE CONCAT('%', #{bookName}, '%') </if> <if test="author != null and author != ''"> AND author LIKE CONCAT('%', #{author}, '%') </if> <if test="categoryId != null"> AND category_id = #{categoryId} </if> <if test="status != null"> AND status = #{status} </if> </where> ORDER BY create_time DESC </select>

<where>标签会自动处理AND前缀的问题:如果第一个条件不成立,而第二个条件成立,<where>会自动去掉SQL里多余的AND,不需要你在每个<if>标签里手动处理AND,这对防止SQL语法错误非常有用。

分页查询我建议用PageHelper插件,这是MyBatis生态里非常成熟的分页方案。用法很简单:在查询前调用PageHelper.startPage(pageNum, pageSize),然后紧跟的第一次查询会被自动拼上LIMIT语句。返回的PageInfo对象里包含了总记录数、总页数、当前页等所有分页信息,直接传给前端即可。

使用PageHelper有一个必须注意的坑:startPage必须紧跟查询语句,中间不能插入其他SQL操作,否则分页会失效。我看到过有人在这个方法之前先做了个count查询,结果分页条件set到了count那条SQL上,查出来的数据完全不对。

2.3 数据初始化与测试数据准备

导入建表SQL后,需要准备一批测试数据。图书表可以手动插入二十条左右的记录,涵盖不同分类、不同库存状态的典型情况。用户表至少准备两个账号:一个管理员账号(role=0),一个普通用户账号(role=1),方便测试不同权限下的接口行为。

密码字段要处理一下。如果你用的是BCrypt,可以在后端写一个临时的单元测试方法,调用BCryptPasswordEncoder.encode("123456")生成密文,再把这个密文INSERT到数据库里。有些人图省事直接在数据库里存明文"123456",开发阶段跑通没问题,但一定要有这个意识:上线前必须全部换成加密后的密文,这不是可选项。

3. 后端SpringBoot接口开发实践

3.1 项目初始化与基础配置

后端工程我习惯用Spring Initializr创建,选Java 8或Java 11都行,依赖勾选Spring Web、MyBatis、MySQL Driver,如果你是IDEA,也可以直接在IDEA里用Spring Initializr生成。

生成之后,pom.xml里要确保包含以下核心依赖:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.3.1</version> </dependency> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency>

注意MyBatis的SpringBoot Starter版本要和你的SpringBoot版本兼容,比如SpringBoot 2.7.x配合MyBatis Starter 2.3.x是最稳的。网上有不少人遇到启动报错,八成就是starter版本和boot版本不匹配。

application.yml里最需要关注的是数据源配置和MyBatis配置:

spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/book_manage?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false username: root password: 你的密码 mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.bookmanage.entity configuration: map-underscore-to-camel-case: true

map-underscore-to-camel-case这个配置我建议一定要开,它能自动把数据库里的下划线字段book_name映射成Java实体类中的bookName,省掉大量写resultMap的时间。如果数据库字段命名不规范(比如全大写、中文),这个功能就帮不上忙了,所以建表时一定要用规范的下划线命名。

3.2 统一返回结果与全局异常处理

前后端分离项目里,接口返回的数据格式需要统一约定,这样前端处理起来才不用针对每个接口单独判断。我习惯定义一个通用的响应体类,包含三个字段:code(状态码)、message(提示信息)、data(业务数据)。

后端所有接口都返回这个结构,前端axios封装的拦截器里统一判断code,非0时弹出错误提示,业务代码不需要到处写try-catch(全局异常处理器兜底),代码会清爽很多。

全局异常处理是用SpringBoot的@RestControllerAdvice注解实现的,在类里写一个方法专门捕获Exception,返回统一格式的错误信息。实际开发中,我遇到过给前端返回500错误时浏览器控制台报CORS错误的奇葩情况,原因就是全局异常没有处理跨域响应头。解决办法是在异常处理方法上也加上@CrossOrigin注解,或者在过滤器里统一给所有响应设置CORS头,这个细节新手很容易忽略。

3.3 登录鉴权与拦截器

图书管理系统的登录逻辑要分角色处理:管理员可以管理图书、查看所有借阅记录;普通用户只能查询图书、借书还书、查看自己的借阅记录。这需要登录接口返回当前用户的角色信息,前端根据角色渲染不同的菜单和按钮。

JWT(JSON Web Token)是目前前后端分离项目最常用的登录凭证方案。用户登录成功后,后端生成一个Token返回给前端,前端存到localStorage里,之后每次请求都在请求头带上Authorization字段,后端通过拦截器解析Token、识别用户身份。这样做的好处是服务端不需要保存会话状态,天然适合水平扩展。

Token生成可以用jjwt库,核心逻辑大概是:把用户ID、用户名、角色放进Token的payload里,加上过期时间,用密钥签名。密钥一定不要硬编码在代码里,至少放到配置文件中,生产环境更要妥善保管。

写拦截器时有一个坑:前端Vue项目的预检请求(OPTIONS方法)会先于真实请求到达后端,如果拦截器把所有请求都拦截下来校验Token,预检请求会因为没带Token被拒掉,前端会看到"跨域请求失败"的报错。正确做法是在拦截器里判断:如果是OPTIONS请求直接放行。

3.4 图书CRUD与借还业务

图书的新增、修改、删除接口本身不难,就是基本的Mapper方法调用。但删除功能要考虑一个业务边界:如果这本书存在未归还的借阅记录,直接物理删除会导致关联数据悬空。我在设计时通常采用逻辑删除的方式,status字段从1置为0,前端列表不再展示,但借阅记录里的数据还能正常关联查询。

借书接口是整个系统的核心亮点,也是一个极容易出现并发隐患的地方。简单的写法是这样:

Book book = bookMapper.findById(bookId); if (book.getAvailable() <= 0) { throw new BusinessException("库存不足"); } book.setAvailable(book.getAvailable() - 1); bookMapper.updateById(book); borrowRecordMapper.insert(new BorrowRecord(userId, bookId));

在单用户测试时这个逻辑没有任何问题,但并发场景下,两个用户同时借同一本书,可能都读到available=1,然后都执行减1,最后库存变成-1。解决思路是加事务和行锁:查询时使用SELECT ... FOR UPDATE锁定该行记录,事务提交后再释放锁。

<select id="findByIdForUpdate" resultType="com.example.bookmanage.entity.Book"> SELECT * FROM book WHERE id = #{id} FOR UPDATE </select>

调用这个方法的Service方法加上@Transactional注解,就能保证同一时间只有一个事务能修改这本书的库存。图书管理系统的并发量可能没那么高,但通过这个案例理解"并发场景下的数据一致性"这个核心概念,才是写这个项目最大的收获之一。

4. 前端Vue项目实战

4.1 Vue环境准备与创建项目

前端部分我用的Vue CLI创建项目,命令很简单:

npm install -g @vue/cli vue create book-manage-frontend

创建过程中选择Vue 3(也可以选Vue 2,看你的学习方向,如果刚入门建议直接Vue 3),然后勾选Router、Vuex/Pinia、Axios这些插件。装完之后进入目录跑npm run serve,本地开发服务器就起来了。

网上创建Vue项目踩的最多的坑就是Node版本问题。Vue CLI要求Node.js版本不能太低(一般要求12以上),版本太老会各种报错;版本太新的Node配合老版本的node-sass也会报错,建议直接看官方文档确认版本要求。如果遇到node-sass安装失败,一个更省事的方案是卸载掉改用sass(dart-sass),兼容性更好。

4.2 axios封装与请求拦截器

前端所有的HTTP请求建议统一走axios封装。原因是真实的项目里你几乎一定要做两件事:在请求头里自动加Token、统一处理错误提示。如果每个页面都自己调axios然后自己处理,代码重复严重,也容易出纰漏。

我习惯的封装方式是这样的,在utils/request.js里创建一个axios实例:

import axios from 'axios' import { ElMessage } from 'element-plus' const request = axios.create({ baseURL: '/api', 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.message) return Promise.reject(new Error(res.message)) } 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

这里有两个细节值得注意。第一个是baseURL设为/api,配合前面说到的跨域代理,前端代码里只写相对路径,后面部署到生产环境时,Nginx做一个/api的反向代理到后端服务,前端代码一行都不用改。第二个是401状态码的统一处理:Token过期时自动跳回登录页,这是管理系统必备的用户体验。

4.3 路由配置与登录守卫

Vue Router的配置里,图书管理系统应该有这几类页面:登录页、图书列表页、图书编辑页、借阅管理页、个人借阅记录页、用户管理页(管理员可见)。

路由需要分级权限控制。基础做法是在路由配置的meta字段里标记需要的角色:

{ path: '/book', name: 'BookList', component: () => import('@/views/BookList.vue'), meta: { requiresAuth: true } }, { path: '/borrow', name: 'BorrowManage', component: () => import('@/views/BorrowManage.vue'), meta: { requiresAuth: true, roles: ['admin'] } }

然后在router.beforeEach导航守卫里做校验:

router.beforeEach((to, from, next) => { const token = localStorage.getItem('token') const role = localStorage.getItem('role') if (to.meta.requiresAuth && !token) { next('/login') } else if (to.meta.roles && !to.meta.roles.includes(role)) { ElMessage.warning('没有权限访问') next(false) } else { next() } })

注意路由懒加载的写法() => import(...),这样打包时会自动按路由拆分成多个chunk,首屏加载速度会比打包成一个大的JS文件快很多。项目小的时候差别不明显,但养成习惯很重要。

4.4 核心页面实现思路

图书列表页是前端最复杂的页面,包含搜索表单、表格、分页、新增弹窗、编辑弹窗、删除确认这些模块。我习惯用Element Plus组件库来搭建,el-table绑定列表数据,el-pagination控制分页,el-dialog做表单弹窗,el-form配合校验规则实现表单验证。

有一点要提醒:表单弹窗里的数据,新增和编辑虽然共用一个组件,但打开时务必区分初始状态。新增时清空表单,编辑时根据当前行数据回填。很多人在"点编辑弹窗里还是上一次新增的数据"这个bug上浪费过时间,根源就是没在open方法里正确重置表单。

借阅管理页需要展示借阅记录,包括用户ID、书名、借书时间、应还时间、状态这些列。管理员可以在这里点"确认归还",普通用户在个人中心只能看到自己的记录。这些页面不复杂,但要注意时间格式处理。后端返回的时间格式通常是2025-01-15T10:30:00这种带T的格式,要处理好格式转换,否则页面上显示的时间非常难看,前端可以写一个formatDate公共方法统一处理。

5. 联调、打包与部署上线

5.1 开发环境跨域配置实操

前端开发服务器默认是localhost:8080,后端是localhost:8081,直接请求必然跨域。我推荐的做法是在Vue项目的vue.config.js里配置devServer的代理:

module.exports = { devServer: { port: 8080, proxy: { '/api': { target: 'http://localhost:8081', changeOrigin: true, pathRewrite: { '^/api': '' } } } } }

这个配置的效果是:前端发请求到/api/user/login,开发服务器会把它转发到http://localhost:8081/user/login,同时changeOrigin: true会修改请求头里的Host,让后端认为请求来自同源,从根源上规避了跨域问题。

这种方式比在后端加@CrossOrigin注解好在哪里?最大的优势是生产环境可以无缝切换。开发环境下代码用相对路径/api/xxx,部署到生产环境后,Nginx同样配一个/api的反向代理,前端代码不需要做任何修改。而如果代码里硬编码了http://localhost:8081这种绝对路径,部署时要么改代码重新打包,要么做环境变量替换,麻烦得多。

5.2 前后端生产环境打包

后端打包比较简单,在项目根目录执行:

mvn clean package -DskipTests

打包成功后,target目录下会生成book-manage-0.0.1-SNAPSHOT.jar文件,这就是一个可以直接运行的后端服务。在服务器上执行:

java -jar book-manage-0.0.1-SNAPSHOT.jar

如果不指定端口,默认8080。但需要注意:如果前后端部署在同一台机器上,后端端口不能和Nginx的端口冲突,建议在application.yml里把server.port改成8081,避免直接占用80或8080。

前端打包执行:

npm run build

打包完成后,dist目录就是所有的静态文件(HTML、CSS、JS)。接下来用Nginx托管这些静态文件,并把/api的请求代理到后端:

server { listen 80; server_name yourdomain.com; root /usr/share/nginx/html/book-manage; index index.html; location /api/ { proxy_pass http://127.0.0.1:8081/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location / { try_files $uri $uri/ /index.html; } }

这个配置里最关键的是try_files $uri $uri/ /index.html这一行。因为Vue是单页应用,路由用的history模式,直接访问/book这种路径时,服务器上并没有对应的物理文件,必须重定向到index.html,让前端路由接管页面渲染。如果不写这行,刷新页面就会出现404。

5.3 常见问题排查与避坑实录

把这个项目从头到尾跑一遍,我遇到过的典型问题整理了一下,给你排雷:

问题现象可能原因解决方案
后端启动报数据库连接失败MySQL服务未启动、密码错误、URL里的库名拼错先确认MySQL能正常连接,再检查application.yml配置
前端请求接口报404代理路径或Controller路径不匹配先看后端Swagger或直接浏览器访问接口URL确认通不通,再查代理配置
前端请求接口报500Mapper XML路径不对、SQL语法错误看后端控制台详细异常栈,重点检查mapper-locations配置和XML里SQL
登录接口返回401Token生成或校验逻辑有误检查拦截器是否放行了登录接口、密钥是否一致
页面刷新404前后端路由history配置问题Nginx配try_files兜底,或用hash模式路由
分页数据不对PageHelperstartPage与查询之间插入了其他SQL确保startPage紧邻要分页的查询语句

还有一个很经典的问题:数据库里的时间字段(比如create_time)带了T字母。这是JDBC驱动处理DATETIME时按ISO 8601格式序列化的结果,解决办法是在application.yml里配置JSON序列化格式:

spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: Asia/Shanghai

配置之后,接口返回的时间就变成2025-01-15 10:30:00,前端显示就没有那个讨厌的T了。

5.4 系统扩展与进阶优化方向

这个图书管理系统跑通之后,如果想继续往深了学,有几个方向值得探索。

第一个方向是把权限体系升级成Spring Security + JWT的方式。现在很多后台接口只用拦截器校验Token,代码简单但功能有限。Spring Security提供了完整的认证授权框架,方法级权限控制、密码编码器、会话管理这些都是现成的,学习曲线陡一些,但弄懂了之后写任何管理系统都会轻松很多。

第二个方向是加入Redis做缓存。比如说图书列表的热门分类查询,可以把查询结果缓存到Redis里,设置过期时间,下次请求直接走缓存,减轻数据库压力。这个优化思路在真实项目中非常重要,也是面试常问的点,值得专门研究。

第三个方向是引入MyBatis-Plus。这个增强工具提供了通用Mapper、通用Service、条件构造器等轮子,能把CRUD代码量再砍掉一大截。我见过不少用MyBatis-Plus做图书管理系统的案例,代码结构比纯MyBatis简洁不少,如果你在学MyBatis的同时也了解一下Plus,将来工作直接用得上。

回到最初的话题,为什么我建议把图书管理系统作为前后端分离练手项目来认真做一遍?因为它的业务边界足够清晰、数据模型简单但不简陋,你能在这个项目里完整经历从建库到接口到前端页面再到部署上线的全过程。这中间踩的每一个坑、解决的每一个报错,都会变成你下一段技术路上的宝贵经验。你在实际动手时可能会遇到跟我上面描述略有出入的情况,这是很正常的,搜索引擎和报错日志是你最好的老师,项目做完了,你的进步会比想象中大得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询