本文还有配套的精品资源点击获取简介直接可运行的Java个人理财管理项目后端用SpringBoot搭建Controller、Service、DAO、Entity分层清晰所有代码按标准Maven结构组织前端用Thymeleaf渲染不依赖JavaScript框架页面逻辑内嵌在HTML模板中配套提供MySQL兼容的数据库建表SQL文件字段涵盖收支类型、金额、日期、账户、备注、分类等核心记账要素项目根目录包含pom.xml、.gitignore、IDEA配置文件.idea和.imlfinance模块已实现用户登录、收支录入、分类查询、月度汇总报表等基础功能适合Java入门者理解SpringBoot整合流程也方便毕设学生快速启动开发、修改界面或扩展统计图表等功能。1. 项目概述为什么这个记账系统值得你花30分钟认真读完我带过十几届Java方向的毕业设计也帮不少刚转行的朋友搭过第一个SpringBoot项目。每次聊到“从零开始做一个能跑起来的小系统”十有八九卡在三个地方一是不知道Controller/Service/DAO到底该怎么分层才不别扭二是Thymeleaf模板里怎么把后端传来的List安全又干净地渲染出来三是建表时字段类型选VARCHAR还是DECIMAL、日期用DATE还是DATETIME、主键要不要加自增——这些看似琐碎的细节恰恰是新手调试两小时却连登录页都刷不出来的真实原因。这个“Java个人记账系统”不是玩具Demo它是我去年给一个大三学生辅导毕设时边写边拆解出来的最小可行骨架没有炫酷图表不集成Redis或消息队列但每一行代码都在回答“为什么这么写”。比如它的Account实体类里balance字段用的是BigDecimal而不是double不是因为“看起来高级”而是实测过——当用户连续做5次19.99元的支出记录后用double累计会出现0.000000000000001这样的鬼数字导致前端显示余额为-0.00而用BigDecimal配合setScale(2, RoundingMode.HALF_UP)就能稳稳守住两位小数。再比如它的数据库脚本里income_expense_record表的record_date字段明确指定为DATE类型而非DATETIME是因为记账场景下“哪一天”比“几点几分”重要得多省掉时间部分既能减少索引体积又避免了前端传参时因时区或格式不一致引发的查询错位。关键词里写的“SpringBoot、个人记账、Thymeleaf、Java毕设、MySQL建表”每一个都不是虚词SpringBoot体现在自动配置的精准取舍比如只开Web和JDBC关掉Actuator和Security个人记账聚焦在收支两条线账户余额联动的核心逻辑闭环Thymeleaf的使用严格遵循“模板即HTML”的原则所有JS逻辑用原生写在script标签里不掺杂Vue指令Java毕设意味着它预留了清晰的扩展钩子比如报表模块用接口定义方便后续替换成EChartsMySQL建表则经受过真实数据量考验单表百万级记录下按月份类型组合查询仍能保持毫秒级响应。如果你正卡在搭建第一个SpringBoot项目的临界点或者毕设开题在即却对工程结构没底这个项目就是为你准备的“可运行说明书”。2. 整体架构与分层设计为什么说它的结构比功能更值得抄作业2.1 分层逻辑的底层动机不是为了分而分而是为了改起来不心慌很多初学者看到“Controller/Service/DAO/Entity”就本能地照着目录名新建包结果写完发现Controller里直接new了DAOService层成了空壳最后改个字段要动五个文件。这个记账系统的分层是从第一行代码就带着明确意图设计的。它的核心驱动力就一条让业务逻辑变更的影响范围可控。举个最典型的例子——用户修改一笔支出的金额。在未分层的写法里你可能在Controller里直接调用JdbcTemplate更新数据库同时手动计算账户余额并更新。一旦需求变成“修改金额时需同步生成一条操作日志”你就得回溯所有涉及金额修改的地方补日志逻辑。而在这个项目里整个流程被严格约束在三层内Controller层只做三件事接收HTTP请求参数校验格式、调用Service方法、封装返回结果SuccessResponse或ErrorResponse。它不碰任何SQL不计算余额不拼接字符串。Service层是真正的业务中枢。IncomeExpenseService.updateRecord()方法内部会先查出原记录再查出对应账户当前余额然后执行金额差值计算新金额 - 原金额最后调用DAO更新记录并调用AccountService.adjustBalance()调整余额。关键点在于所有数据库操作都通过DAO接口进行Service不依赖具体实现类。这意味着未来如果要把MySQL换成PostgreSQL你只需要重写DAO的实现类Service代码一行不动。DAO层只负责单表CRUD。IncomeExpenseDAO接口定义了updateById()、findByAccountIdAndMonth()等方法IncomeExpenseDAOImpl类用JdbcTemplate实现。这里刻意避开了MyBatis的XML映射全部用new BeanPropertyRowMapper(IncomeExpense.class)完成对象映射降低学习成本的同时也让SQL语句完全暴露在Java代码中——你可以一眼看清WHERE account_id ? AND YEAR(record_date) ? AND MONTH(record_date) ?这样的条件是如何构建的而不是在XML里翻半天。这种设计带来的直接好处是当你需要为每笔修改操作添加审计日志时只需在Service层的updateRecord()方法开头插入一行auditLogService.log(UPDATE_INCOME_EXPENSE, userId, recordId)所有调用该方法的地方无论是网页提交还是未来可能增加的API接口都会自动生效。这就是分层的价值——它把“变”与“不变”切分开让修改像拧螺丝一样精准。2.2 Thymeleaf的务实主义为什么放弃Vue选择“HTML即模板”现在提Web开发很多人默认前端Vue/React但这个项目坚持用Thymeleaf不是守旧而是基于两个现实考量学习曲线陡峭度和部署复杂度。一个毕设学生要在两周内搞定前后端联调如果前端用Vue他得先搞懂Node.js环境、Webpack打包、axios跨域配置、Vue Router路由守卫……任何一个环节出问题项目就卡死。而Thymeleaf的逻辑极其朴素后端Controller把数据塞进ModelThymeleaf在HTML里用th:each遍历列表用th:text输出字段用th:if控制显隐。所有交互都靠传统表单提交POST和页面跳转GET没有Ajax异步没有JSON序列化没有CORS报错。比如收支录入页面的保存按钮代码就是标准的form th:action{/record/save} th:object${record} methodpost点击后整个页面刷新错误提示直接显示在表单上方通过th:errors绑定BindingResult。这种“笨办法”的优势在于你能用浏览器开发者工具Network面板清清楚楚看到每一次请求的URL、参数、响应状态码和HTML源码。当页面显示空白时你立刻知道是Controller没返回Model还是Thymeleaf表达式写错了当数据显示错乱时你打开View Source就能确认后端传来的数据是否正确。更重要的是它完美契合Java初学者的认知模型——他们刚学完Servlet对“请求-响应”模型有肌肉记忆Thymeleaf只是把这个模型包装得更简洁而不是推翻重来。项目里的所有页面登录页、收支列表页、统计报表页都遵循同一套规则CSS用CDN引入Bootstrap 5JS逻辑写在页面底部的script标签里处理表单验证、日期控件初始化绝不出现v-model或useState这类需要额外解释的概念。这种克制恰恰是教学项目最珍贵的品质。2.3 Maven工程结构的教科书级组织pom.xml里的每一行都是经验之谈打开pom.xml你会发现它没有堆砌几十个依赖而是精准控制在12个以内。这种精简不是偷懒而是对SpringBoot Starter机制的深度理解。比如它只引入spring-boot-starter-web和spring-boot-starter-jdbc而坚决不用spring-boot-starter-data-jpa。原因很实在JPA虽然能自动生成SQL但新手往往陷入“为什么我的save()方法没生效”的困惑——是事务没开启是实体没加Entity注解还是级联关系配置错了而纯JDBCJdbcTemplate的方式让你写的每一行jdbcTemplate.update()都对应一条真实的UPDATE语句SQL在哪、参数怎么绑定、异常怎么捕获全部透明可见。再看数据库驱动它用的是mysql:mysql-connector-java:8.0.33而不是最新版8.3.x。这不是拒绝升级而是经过测试8.3.x在某些低版本JDK如JDK 11下会触发SSL握手异常而8.0.33兼容性极佳且足够稳定。另一个容易被忽略的细节是maven-compiler-plugin的配置source11/source和target11/target明确锁定JDK 11这直接规避了“本地IDEA用JDK 17编译服务器只有JDK 8跑不起来”的经典坑。还有spring-boot-maven-plugin的executabletrue/executable配置确保打包后的jar可以直接用./finance.jar启动Linux/macOS或finance.jar双击运行Windows不需要额外写shell脚本。这些配置背后是无数次部署失败后总结出的“最小可靠集合”。它不追求技术前沿只确保你在任意一台装了JDK 11的电脑上执行mvn clean package后得到的jar包双击就能打开登录页——这才是毕设项目最核心的交付价值。3. 核心模块与数据库设计从建表脚本读懂记账系统的业务灵魂3.1 MySQL建表脚本的字段哲学每个类型选择都有业务依据数据库目录下的数据库建表语句.sql文件表面看是几段CREATE TABLE语句实则是一份浓缩的记账业务白皮书。我们逐表拆解其设计逻辑-- 用户表核心是密码加密方式与状态标识 CREATE TABLE user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, -- 存BCRYPT加密后的密文长度100足够 email VARCHAR(100), status TINYINT DEFAULT 1, -- 1:启用, 0:禁用比ENUM更易维护 created_time DATETIME DEFAULT CURRENT_TIMESTAMP, updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ); -- 账户表余额精度与货币单位分离 CREATE TABLE account ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL, name VARCHAR(50) NOT NULL, -- 如招商银行储蓄卡、微信零钱 balance DECIMAL(15,2) DEFAULT 0.00, -- 15位总长2位小数覆盖千万级金额 currency VARCHAR(10) DEFAULT CNY, -- 支持多币种扩展但默认人民币 is_default TINYINT DEFAULT 0, -- 主账户标识方便首页快速展示 created_time DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 收支记录表时间粒度与分类体系的平衡 CREATE TABLE income_expense_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, account_id BIGINT NOT NULL, user_id BIGINT NOT NULL, type TINYINT NOT NULL, -- 1:收入, 2:支出比VARCHAR节省空间且查询快 amount DECIMAL(15,2) NOT NULL, record_date DATE NOT NULL, -- 关键只存日期不存时间符合记账习惯 category_id BIGINT NOT NULL, -- 关联分类表支持自定义分类树 remark VARCHAR(200), -- 备注长度200够记超市采购-牛奶面包鸡蛋 created_time DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_user_date_type (user_id, record_date, type), -- 复合索引加速月度汇总 INDEX idx_account_date (account_id, record_date) -- 加速账户流水查询 );这里有几个反直觉但至关重要的设计点。首先是record_date字段用DATE而非DATETIME。新手常觉得“存完整时间更精确”但在记账场景下用户录入一笔“2024年5月15日的午餐支出”他关心的是“这一天花了多少钱”而不是“中午12:35:22花的”。用DATE类型- 避免了前端传参时的时间格式混乱2024-05-15T00:00:00vs2024-05-15- 索引更紧凑DATE占3字节DATETIME占8字节百万级数据下查询性能提升约15%- 业务逻辑更干净——统计5月支出时直接WHERE record_date 2024-05-01 AND record_date 2024-05-31无需考虑时间部分。其次是type字段用TINYINT存储1/2而不是VARCHAR(10)存”income”/”expense”。这看似微小实则影响深远TINYINT仅占1字节而字符串至少占7字节income在income_expense_record表预计超10万条记录时仅此一项就节省近600KB存储空间更重要的是WHERE type 2的查询速度远快于WHERE type expense因为整数比较是CPU原生指令字符串比较需逐字符扫描。项目在Service层用枚举类RecordType封装了这种映射public enum RecordType { INCOME(1), EXPENSE(2); private final int value; RecordType(int value) { this.value value; } public int getValue() { return value; } }这样既保证了数据库的高效又让Java代码语义清晰record.setType(RecordType.EXPENSE.getValue())。最后是索引设计。idx_user_date_type这个复合索引精准命中了最频繁的查询场景“查张三2024年5月的所有支出”。MySQL会按user_id→record_date→type顺序排序数据因此该查询能直接定位到数据块无需全表扫描。实测在10万条记录下响应时间从1200ms降至45ms。3.2 finance模块的功能闭环从登录到报表的7个关键节点finance模块不是功能堆砌而是围绕“用户如何完成一次记账动作”构建的闭环。我们按用户操作流梳理其7个核心节点登录认证LoginController采用Session管理非JWT。理由很实际——毕设项目不需要分布式会话Session简单可靠且Thymeleaf天然支持session.getAttribute()。密码校验用BCryptPasswordEncoder盐值随机生成杜绝明文存储。账户概览AccountController.listAccounts()首页显示所有账户及余额。这里有个易错点AccountService.getTotalBalance()方法必须用SELECT SUM(balance) FROM account WHERE user_id ?一次性查出总额而不是在Java里循环累加。因为后者在高并发下可能出现余额不一致A线程读余额100B线程读余额200AB300但实际可能已被其他线程修改。收支录入RecordController.saveRecord()表单提交后Controller校验金额是否为正数、日期是否不晚于今天、账户是否存在。关键逻辑在Service层先根据type判断是增加还是减少账户余额再执行account.setBalance(account.getBalance().add(delta))最后保存记录。delta是BigDecimal类型确保精度。分类管理CategoryController支持用户自定义收支分类如“餐饮”、“交通”、“工资”。分类表设计为单层结构无父子关系因为个人记账极少需要多级分类。category.name加了唯一索引防止用户重复创建“餐饮”分类。月度汇总ReportController.getMonthlyReport()这是最体现业务深度的部分。它不简单求和而是按record_date的年月分组用GROUP BY YEAR(record_date), MONTH(record_date)生成结果。返回的MonthlyReportDTO包含incomeTotal、expenseTotal、netAmount净额和categoryDetails各分类明细。前端用Thymeleaf的th:each遍历categoryDetails用th:if${detail.type 1}区分收入/支出条目。数据导出ExportController.exportCsv()提供CSV下载功能。不依赖POI等重型库而是用StringBuilder拼接CSV字符串每行用包裹字段,分隔System.lineSeparator()换行。这样生成的文件在Excel里打开不会乱码且内存占用极低10万条记录仅消耗约8MB堆内存。退出登录LoginController.logout()调用HttpSession.invalidate()销毁会话并重定向到登录页。这里特意没做“记住我”功能因为毕设项目应聚焦核心流程避免引入Cookie安全等复杂议题。这7个节点环环相扣构成一个可独立运行的最小业务闭环。你删掉报表功能它仍是完整记账系统你增加图表功能只需在报表页面引入Chart.js不改动后端逻辑——这就是良好架构的弹性。4. 实操部署与二次开发指南从运行第一行代码到扩展你的专属功能4.1 三步启动绕过90%的新手部署陷阱很多同学下载源码后卡在第一步不是代码有问题而是环境配置有盲区。按以下三步走5分钟内必见登录页第一步数据库初始化最容易被忽略的致命步骤- 在MySQL中创建名为finance_db的数据库字符集选utf8mb4排序规则utf8mb4_unicode_ci- 执行数据库建表语句.sql文件注意不要用Navicat的“运行SQL文件”功能它有时会截断长SQL推荐用命令行mysql -u root -p finance_db 数据库建表语句.sql- 检查user表中是否有初始用户执行INSERT INTO user(username, password, email) VALUES(admin, $2a$10$ZzKQqXxYyZzKQqXxYyZzKQqXxYyZzKQqXxYyZzKQqXxYyZzKQq, adminexample.com);密码admin已BCRYPT加密密文如上。第二步配置文件修正IDEA用户必做- 打开src/main/resources/application.yml修改数据库连接yaml spring: datasource: url: jdbc:mysql://localhost:3306/finance_db?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue username: root password: your_mysql_password # 替换为你的MySQL密码提示serverTimezoneAsia/Shanghai是关键若不加SpringBoot启动时会报The server time zone value XXX is unrecognized错误因为MySQL 8.0默认时区与JVM不一致。第三步运行与验证拒绝盲目点击- 在IDEA中右键FinanceApplication.java→Run FinanceApplication- 控制台出现Started FinanceApplication in X.XXX seconds即启动成功- 浏览器访问http://localhost:8080/login输入用户名admin、密码admin即可进入首页。注意首次访问可能稍慢Thymeleaf模板编译耗时请耐心等待5秒。若页面空白立即检查IDEA控制台末尾是否有ERROR字样——90%的情况是数据库密码错误或端口被占用。4.2 毕设扩展实战3个高价值、低难度的功能升级方案作为毕设光跑通不够还得体现你的思考深度。以下是三个经实践验证、工作量可控均在2小时内可完成、且能显著提升答辩分数的扩展方向方案一为收支记录添加图片凭证15分亮点-需求用户录入支出时可上传小票照片支持预览。-实现路径1. 在income_expense_record表新增receipt_image_path VARCHAR(255)字段存相对路径如/uploads/20240515_123456.jpg2. 修改RecordController.saveRecord()接收MultipartFile receiptImage参数用Files.write()保存到src/main/resources/static/uploads/目录将路径存入数据库3. 在Thymeleaf模板中用img th:src{${record.receiptImagePath}} th:if${record.receiptImagePath ! null} /显示图片。-为什么加分展示了文件上传、路径存储、安全防护限制文件大小和类型的全流程且UI效果直观。方案二增加收支趋势折线图20分技术力-需求在首页显示近6个月收入/支出对比折线图。-实现路径1. 新增ReportService.getSixMonthTrend()方法用原生SQL查出近6个月的月度汇总sql SELECT DATE_FORMAT(record_date, %Y-%m) as month, SUM(CASE WHEN type 1 THEN amount ELSE 0 END) as income, SUM(CASE WHEN type 2 THEN amount ELSE 0 END) as expense FROM income_expense_record WHERE record_date DATE_SUB(CURDATE(), INTERVAL 6 MONTH) GROUP BY DATE_FORMAT(record_date, %Y-%m) ORDER BY month2. Controller将结果转为JSON通过Model.addAttribute(trendData, trendList)传给前端3. 在HTML中引入Chart.js CDN用canvas idtrendChart/canvas和一段JS初始化图表。-为什么加分融合了SQL聚合查询、前后端数据传递、前端可视化技术栈完整且图表是答辩时最抓眼球的元素。方案三实现多币种记账25分专业度-需求支持美元、欧元等外币自动按汇率换算为人民币显示。-实现路径1. 在account表新增exchange_rate DECIMAL(10,6) DEFAULT 1.000000字段如美元账户填6.92. 修改AccountService.adjustBalance()当账户currency ! CNY时将amount乘以exchange_rate再计入人民币总额3. 在收支录入表单中增加币种下拉框select th:field*{currency}后端校验currency是否在白名单内。-为什么加分体现了对业务场景的深入理解跨境消费常见且涉及数据一致性保障汇率变动时历史记录如何处理展现工程思维。这三个方案任选其一都能让毕设脱颖而出。它们的共同特点是不颠覆原有架构只在现有分层上叠加新能力所有代码都可追溯、可测试、可演示。5. 常见问题与避坑指南那些文档里不会写的血泪教训5.1 启动报错排查速查表对照症状30秒定位根源症状可能原因解决方案经验备注Failed to configure a DataSourceapplication.yml中数据库URL、用户名或密码错误检查spring.datasource.url是否含?useSSLfalse确认MySQL服务已启动用命令行mysql -u root -p测试能否登录这是最高频错误占新手问题的70%。务必确认MySQL端口是3306非3307页面显示Whitelabel Error Page控制台报TemplateInputExceptionThymeleaf模板文件名或路径错误如login.html放在templates/下但Controller返回login时Thymeleaf找不到确认HTML文件位于src/main/resources/templates/目录检查Controller中return login;的字符串与文件名完全一致区分大小写Thymeleaf对路径极其敏感templates/login.html和templates/Login.html是两个文件登录后跳转到/login?error用户名密码正确user表中status字段值为0禁用状态或密码未BCRYPT加密执行SQLUPDATE user SET status 1 WHERE username admin;若密码是明文用在线BCRYPT工具加密后更新初始用户状态默认启用但手动插入数据时易忽略status字段收入/支出金额显示为0.00或科学计数法amount字段在Entity中用了Double而非BigDecimal或Thymeleaf中用th:text${record.amount}未格式化将Entity中amount改为BigDecimal在模板中用th:text${#numbers.formatDecimal(record.amount, 1, 2)}Double的精度丢失是隐形炸弹务必用BigDecimalformatDecimal组合日期选择器无法弹出或提交后日期为空前端JS未加载或input typedate的value属性格式错误如传入2024-5-15而非2024-05-15检查HTML中是否引入了Bootstrap JS在Controller中用DateTimeFormat(patternyyyy-MM-dd)注解修饰recordDate参数浏览器原生日期控件要求YYYY-MM-DD格式缺位补零不可少5.2 二次开发必知的3个隐藏雷区雷区一Thymeleaf表达式中的空指针新手常写th:text${user.username}但若user为null页面会直接报错500。正确写法是th:text${user?.username} ?: 游客。?.是安全导航操作符?:是Elvis操作符空则取默认值。这个细节在官方文档里提得很少但却是线上环境崩溃的头号诱因。雷区二JDBC事务的“假成功”在RecordService.saveRecord()中若先保存记录再调整余额两步操作不在同一事务内当第二步失败时记录已入库但余额未更新造成数据不一致。解决方案在Service方法上加Transactional注解并确保该方法由Spring代理调用即不能在同一个类内this.saveRecord()调用。这是Spring事务失效的经典场景必须用Transactional包裹整个业务方法。雷区三静态资源缓存导致样式失效修改了static/css/style.css后浏览器仍显示旧样式。这是因为SpringBoot默认开启静态资源缓存spring.resources.cache.period3600。临时解决在application.yml中添加spring.resources.cache.period0长期方案在link标签中加入版本号link th:href{/css/style.css?v1.0.1} relstylesheet。这个坑会让前端调试变成玄学务必提前规避。5.3 性能优化的轻量级实践不改架构也能提速50%当数据量超过5万条时基础查询会明显变慢。无需引入Redis或分库分表三个配置级优化立竿见影开启JDBC批处理在application.yml中添加yaml spring: datasource: hikari: connection-timeout: 30000 maximum-pool-size: 20 style="width:16px;margin-left:4px;vertical-align:text-bottom;cursor:text;" />简介直接可运行的Java个人理财管理项目后端用SpringBoot搭建Controller、Service、DAO、Entity分层清晰所有代码按标准Maven结构组织前端用Thymeleaf渲染不依赖JavaScript框架页面逻辑内嵌在HTML模板中配套提供MySQL兼容的数据库建表SQL文件字段涵盖收支类型、金额、日期、账户、备注、分类等核心记账要素项目根目录包含pom.xml、.gitignore、IDEA配置文件.idea和.imlfinance模块已实现用户登录、收支录入、分类查询、月度汇总报表等基础功能适合Java入门者理解SpringBoot整合流程也方便毕设学生快速启动开发、修改界面或扩展统计图表等功能。本文还有配套的精品资源点击获取