资讯详情

资讯详情

基于Spring Boot与MyBatis的个人在线理财系统全栈开发实践

简介这是一份基于Spring Boot、MyBatis、Spring MVC与Bootstrap的个人在线理财系统完整源代码及数据库文件适合Java Web学习者、毕业设计或中小型财务管理系统二次开发使用。项目包含用户注册登录、资产账户管理、收支记录、投资跟踪、财务报表与统计等核心功能覆盖从后端持久层到前端响应式页面的完整实现思路。压缩包共654个文件主要类型包括238个Java源码、152个XML配置与Mapper映射、104个依赖Jar包、70个HTML页面以及JS/CSS、SQL脚本等整体大小39.12MB目录结构清晰便于按模块阅读与部署。目前已有133人学习浏览。借助该源码开发者可直接导入IDE运行参照SQL脚本初始化数据库学习Spring Boot自动配置、MyBatis灵活持久化、RESTful接口设计以及Bootstrap响应式布局的整合实践也可在此基础上扩展多账户权限、预算提醒等功能快速搭建可用的个人理财平台。1. 个人在线理财系统一套能直接抄作业的全栈样本拿到这套基于 Spring Boot MyBatis Spring MVC Bootstrap 的个人在线理财系统第一反应是它把个人财务管理拆成了标准的 CRUD 加报表组合注册登录、资产账户维护、收支流水、投资跟踪最终汇总成资产负债表和收支报告。这类系统在真实业务里不起眼却覆盖了 Java Web 开发最该反复打磨的基本功——表结构设计、动态 SQL、事务控制、权限拦截、前端表格渲染。适合两类人做课程设计或毕业设计的在校生以及想快速搭内部财务工具的工程师。源代码加数据库的价值在于能看到完整项目的目录组织Controller 如何瘦身、Service 事务放在哪、MyBatis XML 映射怎么拆模块。接下来按拆项目的顺序把选型理由、表设计、关键实现和常见坑逐一过一遍。2. 技术栈分工Spring Boot 自动配置、MyBatis 持久化与 Bootstrap 响应式的协作逻辑2.1 Spring Boot 自动配置替代传统 SSM 的 XML 装配传统 SSM 整合 Spring、Spring MVC、MyBatis 时最繁琐的是写数据源、事务管理器、SqlSessionFactory、MapperScannerConfigurer、视图解析器这一串配置。Spring Boot 的核心价值在自动配置引入 spring-boot-starter-web 和 spring-boot-starter-jdbc 后内嵌 Tomcat、HikariCP 连接池、Spring MVC 默认装配全部生效项目只需在 application.yml 里覆盖需要改动的部分。spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/finance?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 hikari: maximum-pool-size: 10 minimum-idle: 2 mvc: view: prefix: classpath:/templates/ suffix: .html mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.finance.entity configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl server: port: 8080这段配置有四个关键点。datasource 走 HikariCPSpring Boot 2.x 默认集成maximum-pool-size 控制并发峰值下的连接上限mapper-locations 声明 XML 映射文件位置map-underscore-to-camel-case 把数据库的 create_time 自动映射成实体类的 createTime省掉手写 resultMap 的工作量log-impl 设为 StdOutImpl 后控制台直接打印带参数值的完整 SQL排查问题时比看占位符 ? 直观一个量级。有一个启动环节的误用很常见Spring Boot 版本太高比如 2.7 升到 3.x后javax.servlet 包全部迁移到 jakarta.servlet旧项目直接编译失败。配套做法是确认 JDK 版本2.7 配 JDK 8 或 11 最稳升 3.x 则必须同步改 import 和配置项。2.2 Spring MVC 的请求处理链与 RESTful 接口设计Spring MVC 在这里承担 Web 层接收 HTTP 请求、参数绑定、校验、调用 Service、返回视图或 JSON。理财系统的接口设计按 RESTful 风格组织资源用复数名词动作交给 HTTP Method。收支记录核心接口的约定如下接口方法功能/transactionsPOST新增收支记录/transactions/{id}PUT修改记录/transactions/{id}DELETE删除记录/transactions?category餐饮start2025-01-01GET按条件分页查询Controller 层只做参数接收与结果封装业务逻辑全部下沉到 Service。最应该避免的反模式是在 Controller 里直接注入 JdbcTemplate 或者把 SQL 写死在注解里这套系统的代码结构没有这个问题Controller 注入 ServiceService 注入 Mapper职责边界清楚。分页参数 pageNum、pageSize 建议在拦截器层做默认值兜底防止前端不传时出现负数导致的异常 SQL。2.3 Bootstrap 静态资源引入与前后端协作方式前端用 Bootstrap 是这套系统体验成本最低的部分。bootstrap.min.css、style.min.css、materialdesignicons.min.css 这类资源说明页面是典型的后台管理布局左侧菜单、顶部导航、内容区放表格和表单。静态资源统一放在 src/main/resources/static 下页面模板用 Thymeleaf 渲染。link relstylesheet th:href{/css/bootstrap.min.css} / script th:src{/js/bootstrap.min.js}/script用 th:href 而非 href 的目的是让 Thymeleaf 自动拼接 context-path项目将来部署到带前缀的域名下时不会出现静态资源 404。Bootstrap 的下拉菜单组件dropdown在这里承担账户切换和分类筛选的交互它是纯 CSS jQuery 插件实现的不需要额外引包但要注意 dropdown 的父容器必须加position: relative否则菜单会从页面边缘滑出。3. 数据库设计与 MyBatis 动态 SQL五张核心表撑起理财业务3.1 表结构设计用户、账户、交易、投资各司其职理财系统的数据库设计是整个项目的地基。拆解需求后可以提炼出五个核心实体用户user、资产账户account、交易记录transaction、投资记录investment、分类字典category。表名核心字段职责边界userid, username, password, salt, create_time登录凭证与权限隔离accountid, user_id, type, name, balance, status银行/信用卡/投资账户transactionid, account_id, category_id, type, amount, note, trade_time每一笔收支流水investmentid, account_id, product_name, cost_price, quantity, market_value投资持仓与浮动盈亏categoryid, parent_id, name, type收支分类树user 与 account 是一对多account 与 transaction 是一对多。一个需要强调的字段选型问题金额一律用 decimal(10,2)不要用 float 或 double。浮点数在 JVM 和 MySQL 内部都无法精确表达 0.1 这类十进制小数累计收支时误差会被滚雪球放大对账时差出来 0.01 会让人查到怀疑人生。MyBatis 对应实体字段用 BigDecimal从查询到报表全程保持精度。3.2 MyBatis XML 动态 SQL按条件拼查询数据持久层用 XML 映射而不是注解原因是收支查询的条件组合太多时间范围、分类、金额区间、账户、类型。XML 动态 SQL 在维护这类场景时有天然优势一个典型的分页查询映射如下select idselectPage resultTypecom.example.finance.entity.Transaction SELECT t.id, t.account_id, t.category_id, t.type, t.amount, t.note, t.trade_time FROM transaction t where if testuserId ! null AND t.account_id IN ( SELECT id FROM account WHERE user_id #{userId} ) /if if testcategoryId ! null AND t.category_id #{categoryId} /if if testtype ! null and type ! AND t.type #{type} /if if teststartTime ! null AND t.trade_time gt; #{startTime} /if if testendTime ! null AND t.trade_time lt; #{endTime} /if /where ORDER BY t.trade_time DESC LIMIT #{offset}, #{pageSize} /select这段 SQL 有三个细节值得单独讲。第一子查询account_id IN (SELECT id FROM account WHERE user_id #{userId})做了数据隔离用户只能看到自己账户名下的流水这个条件在 Service 层还要再强制拼一次防止有人把别人的 account_id 直接传进接口。第二XML 里小于等于必须写成lt;写成会被解析器当成标签开始这类报错启动时不出现运行到查询才抛 SQL 语法异常。第三LIMIT #{offset}, #{pageSize}是 MySQL 的物理分页写法换 Oracle 要改 ROWNUM 或 FETCH FIRST说明这套脚本是 MySQL 专属的。注意where标签会自动去掉开头多余的 AND/OR但不会处理结尾所以每个if的条件都要以 AND 开头写在 SQL 片段内。3.3 事务边界流水与余额的原子更新理财场景对事务要求极高典型场景是「新增交易流水时同步更新账户余额」。两步必须在一个事务里否则流水写进去了、余额没改对账永远差一分钱。Service 层的标准写法Service public class TransactionService { Autowired private TransactionMapper transactionMapper; Autowired private AccountMapper accountMapper; Transactional(rollbackFor Exception.class) public void addTransaction(Transaction transaction) { int rows transactionMapper.insert(transaction); if (rows ! 1) { throw new BusinessException(流水写入失败); } Account account accountMapper.selectById(transaction.getAccountId()); BigDecimal newBalance account.getBalance().add(transaction.getAmount()); accountMapper.updateBalance(account.getId(), newBalance); } }Transactional必须加在 public 方法上而且同类内部 this 调用不经过代理对象注解会直接失效这是面试里反复考、开发里反复踩的点。rollbackFor Exception.class一定要写因为默认只回滚 RuntimeException业务异常如果自定义为 Exception 子类不加这个参数事务就不会回滚。更新余额时应该用UPDATE account SET balance balance #{delta} WHERE id #{id}这种原子的相对更新而不是先查再改避免并发下丢更新。4. 核心功能实战收支跟踪、统计报表与投资分析的实现路径4.1 收支管理模块记账主链路的 Controller 与校验收支管理是理财系统的第一入口。业务流程是用户选择账户和分类、填入金额与备注、系统写入 transaction 表、同步更新 account 余额。Service 层用上一章的事务方法完成两步写入Controller 层只做参数绑定和基础校验RestController RequestMapping(/transactions) public class TransactionController { Autowired private TransactionService transactionService; PostMapping public ResultVoid add(RequestBody TransactionDTO dto) { if (dto.getAmount().compareTo(BigDecimal.ZERO) 0) { return Result.error(金额必须大于0); } if (!currentUserOwnsAccount(dto.getAccountId())) { return Result.error(无权操作该账户); } transactionService.addTransaction(dto.toEntity()); return Result.success(); } }参数校验可以上 Hibernate Validator 的 NotNull、DecimalMin但对于这套系统体量手动判断更直白阅读成本也低。currentUserOwnsAccount这一步要做拿到当前登录用户 id查 account 表的 user_id 是否匹配不匹配直接拒绝。很多课程设计只做登录校验漏掉资源归属校验接口被人遍历 account_id 就能看光别人的流水这属于数据越权。4.2 统计报表按月份、按分类汇总的 SQL 写法报表模块是区分「能跑」和「能用」的分水岭。个人理财的报表核心就三张按分类统计的收支占比、按月统计的收支趋势、资产负债总览。后两者靠 Service 拼装按月份趋势用一条 GROUP BY 就能搞定SELECT DATE_FORMAT(trade_time, %Y-%m) AS month, SUM(CASE WHEN type expense THEN amount ELSE 0 END) AS expense_total, SUM(CASE WHEN type income THEN amount ELSE 0 END) AS income_total FROM transaction t WHERE t.account_id IN (SELECT id FROM account WHERE user_id #{userId}) AND t.trade_time BETWEEN #{startTime} AND #{endTime} GROUP BY DATE_FORMAT(trade_time, %Y-%m) ORDER BY month ASCDATE_FORMAT(trade_time, %Y-%m)把时间截断到月份做分组聚合是 MySQL 里最常见的趋势统计写法。注意 WHERE 里仍然带 user_id 子查询保证统计结果只落在当前用户名下。如果流水表涨到百万级这条 SQL 要在 trade_time 和 account_id 上建联合索引再往大了走数仓分析引擎里 DATE_FORMAT 要换成 DATE_TRUNC这是迁移到 Doris/StarRocks 时容易忽略的兼容点。4.3 投资分析持仓盈亏计算与 Bootstrap 图表展示投资模块跟踪持仓浮动盈亏表结构里 cost_price成本价、quantity数量、market_value市值三个字段足够支撑计算盈亏金额 market_value - cost_price * quantity盈亏比例 盈亏金额 / (cost_price * quantity)。market_value 一般由外部行情接口定时刷新源码里如果没有对接实时行情就预留批量更新接口由调度任务或者人工维护价格。前端展示阶段Bootstrap 负责布局报表容器里渲染图表。如果项目里没有引入 ECharts用 Bootstrap 的 progress-bar 组件也能做出分类占比的视觉效果每个分类一条进度条宽度按支出占比百分比设置。若引入 ECharts常见交互是后端返回 JSONjQuery 拉取后渲染折线图$.ajax({ url: /report/expense-trend, method: GET, success: function (res) { var months res.data.map(function (item) { return item.month; }); var totals res.data.map(function (item) { return item.expense_total; }); var chart echarts.init(document.getElementById(trendChart)); chart.setOption({ xAxis: { type: category, data: months }, yAxis: { type: value }, series: [{ type: line, data: totals }] }); } });这段代码里res.data.map用了 ES5 的写法而不是箭头函数考虑到 Bootstrap 3 时期的老项目经常不配 Babel这种写法在低版本浏览器里不会挂。5. 部署排错与 MyBatis 缓存收敛从 IDEA 启动到安全加固5.1 启动顺序与三类典型故障拿到源代码和数据库后的推荐顺序先用 Navicat 导入 .sql 脚本建好 finance 库和表再改 application.yml 里的数据库账号密码最后用 IDEA 打开项目等 Maven 依赖下载完启动 FinanceApplication 主类。启动失败的案例里八成集中在三个位置。第一Spring Boot 版本与 JDK 不匹配2.7 配 JDK 17 会出现反射相关的 InaccessibleObjectException处理办法是降 JDK 或整体升级到 Spring Boot 3.x 并同步改 jakarta 包名。第二mapper 接口与 XML 的 namespace 不一致报 BindingException检查接口全限定名和mapper namespace...是否完全一致。第三数据库驱动不匹配MySQL 8 必须用com.mysql.cj.jdbc.Driver5.x 用com.mysql.jdbc.Driver混用时要么 ClassNotFoundException 要么 CommunicationsException。5.2 慢查询定位与索引设计开发阶段把 log-impl 设为 StdOutImpl控制台直接打印完整 SQL 和绑定参数生产环境换成 logback 输出到独立文件配合 MySQL 的 slow query log 判断哪些条件缺索引。transaction 表最常用的过滤条件是 account_id 和 trade_time建议建联合索引ALTER TABLE transaction ADD INDEX idx_account_time (account_id, trade_time);查询条件里已经带上了 account_id 的子查询索引覆盖这两个字段后单用户百万级流水下的响应时间能控制在可接受范围。注意不要让前端传入的排序字段直接拼进 ORDER BY${}拼接会引入 SQL 注入风险排序字段应该用白名单枚举在前端就约束死。5.3 SQL 注入防线与 MyBatis 缓存收敛这套系统里所有查询条件都用#{}占位符MyBatis 会编译成 PreparedStatement 的?从根上阻断 SQL 注入。真正需要警惕的是${}它只该出现在 ORDER BY 字段名这类无法预编译的位置如果代码里出现${pageSize}或${orderBy}必须做白名单校验。最后落到一个实用配置把 MyBatis 二级缓存关掉只保留一级缓存SqlSession 级别。个人理财是典型的读写混合场景交易记录实时性要求高二级缓存跨 SqlSession 共享脏读风险远大于性能收益。在 application.yml 的 mybatis.configuration 下设置cache-enabled: false再配合前面的 StdOutImpl 观察 SQL 命中情况。开发期打开 SQL 日志、生产期关闭并落文件这一开一关之间就是这套系统从「能跑」到「能上线」的距离。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →