ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

SSM+MySQL股票交易管理系统:从建表设计到事务部署的完整实战解析

SSM+MySQL股票交易管理系统:从建表设计到事务部署的完整实战解析 简介面向Java毕业设计与课程设计的股票交易管理系统源码包基于SSMMyBatisMySQL实现包含前台用户注册登录、股票浏览收藏评论、资金账户管理、买入卖出交易以及管理员后台的用户、资金、股票、板块、系统管理等完整功能适合需要快速搭建金融类管理项目的学习者参考。包内共1268个文件以js、css、jsp等前端资源为主辅以120个Java源文件与SQL脚本、说明文档整体19.72MB结构清晰便于直接导入eclipse/idea配合Tomcat运行。已有137人学习浏览覆盖从数据库设计到前后端交互的完整项目流程读者可依据文档与源码理解SSM框架整合、Maven依赖管理及股票交易核心业务逻辑对完成毕业设计或课程设计具有较强参考价值。1. 股票交易管理系统到底是什么一个有数据库、有业务闭环、能跑起来演示的 SSM 项目看到“【java毕业设计】股票交易管理系统源码ssmmysql说明文档LW.zip”这种包名先别急着解压先搞清楚你拿到的是什么。这是一个基于 Spring SpringMVC MyBatisSSM三件套、数据落在 MySQL 里的完整 Web 系统前端是 JSP 页面核心业务是股票的买入、卖出、持仓查询和资金流水通常还带一个管理员后台。它解决的是你从零搭一个能演示、能答辩、能写进简历的 Java 后端全栈项目的时间成本适合正在选课题的 Java 方向学生也适合刚学完 SSM 但没摸过完整系统的毕业生。你不需要重新设计架构只需要把它拆开看明白、改对参数、跑通流程然后回答清楚“表为什么这么建、事务放在哪一层、并发下会不会出问题”。2. 数据骨架先立住建表 SQL 与 MyBatis 映射这样设计才不怕答辩提问拿到这种项目包我第一件事永远是打开包内的 sql 脚本或说明文档而不是急着启动服务。数据库表结构基本决定了这个系统的业务边界也决定了答辩老师会往哪个方向追问。常见的股票交易管理系统不会像真实券商那么复杂它会做一个简化但完整的闭环用户能注册登录、能查股票列表、能下单买入、能卖出持仓、能看到资金变动。对应到表上核心就六张左右。2.1 六张表的核心结构账户、持仓、成交记录怎么关联先看表与表之间的关系。用户表存登录账号和基础信息资金账户表存可用余额股票表存股票代码和最新价持仓表记录“谁持有了哪只股票、成本价多少、数量多少”委托表或成交表记录每一笔买卖流水。这五张表加一张管理员表就是一套标准的金融业务数据模型。设计要点是资金不能直接写在用户表里要单独拆表因为资金变动频繁独立表方便加流水记录持仓也不能只存在订单里要随时能算出当前余额。下面是一份参考建表 SQL你可以对照包内的 sql 文件看结构差异重点理解字段为什么必须存在。以 common 数据库名为例-- 用户表只存登录凭证和基础资料金额类字段不放在这里 CREATE TABLE t_user ( id INT NOT NULL AUTO_INCREMENT, username VARCHAR(50) NOT NULL, password VARCHAR(100) NOT NULL, real_name VARCHAR(50) DEFAULT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 资金账户表与用户一对一余额字段必须用 DECIMAL不用 DOUBLE CREATE TABLE t_account ( id INT NOT NULL AUTO_INCREMENT, user_id INT NOT NULL, balance DECIMAL(12,2) NOT NULL DEFAULT 0.00, frozen DECIMAL(12,2) NOT NULL DEFAULT 0.00, PRIMARY KEY (id), KEY idx_user_id (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 股票表股票代码是业务主键行情价格跟随大盘变动 CREATE TABLE t_stock ( id INT NOT NULL AUTO_INCREMENT, stock_code VARCHAR(10) NOT NULL, stock_name VARCHAR(50) NOT NULL, current_price DECIMAL(10,2) NOT NULL DEFAULT 0.00, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_stock_code (stock_code) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这段 SQL 的三个关键点第一金额一律用 DECIMAL 不要用 DOUBLEDOUBLE 有浮点误差面试官和数据核对时这是最容易挑刺的地方第二唯一约束要建在业务字段上比如用户名的 uk 索引和股票代码的 uk 索引这是 MyBatis 做根据用户名查询、根据股票代码查询时的底层保障第三所有表都要有主键和明确的索引否则数据量一上来联表和查询都会拖慢。持仓表和成交记录表的关联逻辑是面试高频区。持仓表应该以 user_id stock_code 做联合唯一索引因为同一个用户对同一只股票只能有一条持仓记录买入时没有就插入、有就更新数量。成交记录表则是一条只增不改的流水每次买或卖都插入新记录通过 trade_type 区分买卖方向。这样设计的好处是查询当前持仓走持仓表查询历史盈亏走成交表两条链路互不干扰。2.2 MyBatis 配置与动态 SQLresultMap 和 if 条件查询是必考内容SSM 项目里 MyBatis 的配置往往分成两个层次全局配置文件管数据库连接和驼峰映射Mapper 接口配 XML 文件管 SQL。很多毕业生只会照着包内代码改数据库账号密码但答辩被问到“MyBatis 中 #{} 和 ${} 有什么区别”就卡住这两个符号在 XML 里几乎处处可见不能忽略。连接层配置在 jdbc.properties 或 db.properties 里不同包的名字可能不一样但核心就是四个参数。以 MySQL 8.0 为例jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/stock_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowMultiQueriestrue jdbc.usernameroot jdbc.password你的数据库密码这组配置经常是跑不起来的第一道坎。driverClassName 在 MySQL 8.0 之后必须是 com.mysql.cj.jdbc.Driver旧的 com.mysql.jdbc.Driver 虽然能加载但会打警告url 里必须有 serverTimezone 参数否则高版本 MySQL 会报时区错误useSSLfalse 是为了避免本地开发时 SSL 握手警告刷屏。你可以直接打开包内的配置文件看作者用的是哪个驱动版本和本地 mysql 版本对不上就是第一个坑。再来看 Mapper 层。股票列表查询一般会带条件筛选用户可能按股票代码模糊搜索也可能按价格区间过滤这时候动态 SQL 就有用了。下面这段是常见写法select idselectStockList resultTypecom.example.entity.Stock SELECT id, stock_code, stock_name, current_price, update_time FROM t_stock where if teststockCode ! null and stockCode ! AND stock_code LIKE CONCAT(%, #{stockCode}, %) /if if testminPrice ! null AND current_price gt; #{minPrice} /if if testmaxPrice ! null AND current_price lt; #{maxPrice} /if /where ORDER BY stock_code /select逻辑说明where 标签会自动处理第一个条件前面的 AND避免出现“WHERE AND”这种语法错误if 标签做非空判断条件不满足时整段 SQL 自动去掉大于号和小于号必须用 和 转义F12 打开页面报 SQL 语法错误时先检查这里。参数说明stockCode 传字符串minPrice 和 maxPrice 传 BigDecimalMyBatis 会根据入参类型自动绑定不需要手动拼 SQL也正因为这样#{ } 才能防 SQL 注入这就是面试题里常问的 #{} 和 ${} 的本质区别。除了动态查询resultMap 也是毕业设计里需要单列出来看的地方。如果实体类属性名和数据库字段名不一致比如数据库字段是 stock_codeJava 属性是 stockCode你需要在 mybatis-config.xml 里开启驼峰映射settings setting namemapUnderscoreToCamelCase valuetrue/ /settings开启后 MyBatis 自动把下划线字段名转成驼峰属性名省去为每个实体类手写 resultMap 的体力活。值得注意的是如果你跑起来后发现查询结果某个字段一直是 null别去翻后端逻辑八成就是这里没配置。这个开关的优先级低于显式声明的 resultMap也就是说写了 resultMap 的查询以 resultMap 为准没写才走自动驼峰换算理解这个优先级排查字段为空时能省半小时。3. 交易流程怎么落不翻车从下单买入到事务回滚的完整设计毕业设计能不能过关键就看核心流程有没有闭环、异常情况下数据会不会出错。股票交易系统的核心闭环就是买入和卖出两条链路买入要校验资金够不够、扣款、加持仓卖出要校验持仓够不够、加钱、减持仓。这两条链路每一环都不能出问题否则就会出现“钱扣了但持仓没变”这种答辩现场翻车事故。3.1 Controller 层只做参数接收和结果封装业务逻辑下沉到 Service很多刚从 Java 基础阶段过来的学生习惯把逻辑全写在 Controller 里这在课程设计里能跑但答辩时会被问“为什么这么设计”。SSM 规范的做法是 Controller 只负责三件事接收前端参数、调用 Service、把结果封装成统一格式返回。具体长这样Controller RequestMapping(/trade) public class TradeController { Autowired private TradeService tradeService; RequestMapping(value /buy, method RequestMethod.POST) ResponseBody public Result buy(RequestParam(stockCode) String stockCode, RequestParam(price) BigDecimal price, RequestParam(quantity) Integer quantity, HttpSession session) { User user (User) session.getAttribute(loginUser); if (user null) { return Result.error(请先登录); } try { tradeService.buy(user.getId(), stockCode, price, quantity); return Result.success(买入成功); } catch (BusinessException e) { return Result.error(e.getMessage()); } } }逻辑说明RequestParam 绑定前端传参缺失参数时 SpringMVC 会自动抛异常并返回 400但要注意前端如果传了空字符串这里拦不住所以 service 层必须再做一次业务参数校验。从 session 里取用户是常规操作说明文档里一般也这么写。返回的 Result 是统一响应体包含 code、msg、data 三个字段方便前端 JSP 页面用 JS 判断后弹提示。参数说明stockCode 是股票代码price 是下单价格quantity 是股数这三个参数对应前端表单里的三个输入框前端传什么类型后端就接收什么类型类型不匹配会直接 400。接口设计上买单和卖单可以共用一个 RequestMapping 前缀 /trade再靠 /buy 和 /sell 区分动作。这样 URL 结构清晰也方便课程设计文档里画接口表格。3.2 Transactional 必须加在 Service 层方法上五步扣款流程别拆开买入操作在数据库层面至少涉及三步查账户余额、扣减余额、插入或更新持仓。这三步必须在一个事务里完成否则第二步成功第三步失败用户的钱就蒸发了。Spring 的声明式事务靠 Transactional 注解实现加在 Service 实现类的方法上。Service public class TradeServiceImpl implements TradeService { Autowired private AccountMapper accountMapper; Autowired private PositionMapper positionMapper; Autowired private TradeRecordMapper tradeRecordMapper; Override Transactional(rollbackFor Exception.class) public void buy(Integer userId, String stockCode, BigDecimal price, Integer quantity) { // 步骤1计算总价并校验 BigDecimal totalAmount price.multiply(new BigDecimal(quantity)); Account account accountMapper.selectByUserIdForUpdate(userId); if (account null) { throw new BusinessException(账户不存在); } if (account.getBalance().compareTo(totalAmount) 0) { throw new BusinessException(余额不足); } // 步骤2扣减余额 int rows accountMapper.deductBalance(userId, totalAmount); if (rows 0) { throw new BusinessException(扣款失败); } // 步骤3更新持仓没有持仓则新增 Position position positionMapper.selectByUserIdAndStock(userId, stockCode); if (position null) { Position newPosition new Position(); newPosition.setUserId(userId); newPosition.setStockCode(stockCode); newPosition.setQuantity(quantity); newPosition.setAvgCost(price); positionMapper.insert(newPosition); } else { // 新总持仓数量 原数量 本次数量 int newQuantity position.getQuantity() quantity; // 新成本价 (原成本*原数量 本次价格*本次数量) / 新总数量 BigDecimal newAvgCost position.getAvgCost() .multiply(new BigDecimal(position.getQuantity())) .add(price.multiply(new BigDecimal(quantity))) .divide(new BigDecimal(newQuantity), 2, RoundingMode.HALF_UP); positionMapper.updateQuantityAndCost(userId, stockCode, newQuantity, newAvgCost); } // 步骤4插入成交记录 TradeRecord record new TradeRecord(); record.setUserId(userId); record.setStockCode(stockCode); record.setPrice(price); record.setQuantity(quantity); record.setTradeType(BUY); tradeRecordMapper.insert(record); } }这段代码有几个值得在答辩时展开讲的设计细节。第一行 Transactional(rollbackFor Exception.class) 必须显式指定 rollbackForSpring 默认只在遇到 RuntimeException 时才回滚而自定义 BusinessException 如果继承的是 Exception不指定 rollbackFor 事务就不会回滚这是整个系统最隐蔽的黑匣子之一。selectByUserIdForUpdate 用了 for update 悲观锁把用户账户行锁住防止并发情况下两个人同时下单导致余额被扣成负数这一点是答辩加分项大多数学生的代码里没有这一行。加权平均成本的计算方法是更新前平均成本乘以更新前数量加上本次买入价乘以本次数量再除以更新后总数量这个公式会出现在利润计算逻辑里。卖出逻辑和买入对称先查持仓数量够不够够则加回余额、扣减持仓数量、插入一条方向为 SELL 的成交记录。需要注意持仓数量减到零时应该删除这条持仓记录而不是保留一条数量为零的行否则持仓列表会出现大量空行。3.3 接口返回与前端 JSP 联动Result 对象的三种状态设计后端接口写好后前端 JSP 页面通过 Ajax 调用。用户点击“买入”按钮后前端拿到 Result 对象根据 code 值决定弹成功还是失败的提示。这里有一个常见的坏味道部分入门的代码把 Result 写死成只有 data 一个字段成功和失败全靠前端判断 data 是否为空一旦逻辑复杂就会翻车。统一返回体建议长这样public class Result { private Integer code; // 200成功500业务失败401未登录 private String msg; // 提示信息 private Object data; // 业务数据 public static Result success(Object data) { Result r new Result(); r.code 200; r.msg 操作成功; r.data data; return r; } public static Result error(String msg) { Result r new Result(); r.code 500; r.msg msg; return r; } }状态码设计尽量贴近 HTTP 语义但不要照搬。200 表示成功500 表示业务校验失败401 是未登录或者 session 过期。有些同学喜欢一个 code 走遍天下前端只能靠 msg 的字符串内容判断这会在后续维护时把自己坑进去。另外全局异常处理可以用 ControllerAdvice 统一拦截未捕获的异常把堆栈信息记录到日志给前端返回“系统繁忙”而不是一长串红色报错页这属于工程化习惯论文里写一行就有亮点。4. 部署与常见问题排查把 zip 变成能展示的系统前面的代码分析得再透彻跑不起来一切都白搭。这一章是实操链路按步骤走完你就能在浏览器里看到登录页。不同源码包的目录结构略有差别但只要是基于 SSM 加 MySQL 的 Maven 项目部署套路是一致的。4.1 从解压到浏览器打开环境准备与五步启动清单先把环境备齐。JDK 必须配置好命令行执行 java -version 能输出版本号MySQL 建议直接装 8.0装的时候注意 root 密码记住如果你还没装过按 mysql 安装教程把服务起起来用命令行能连上再往后走Maven 最好用 IDEA 内置的或者单独装一个并且把仓库镜像换成国内源否则第一次下载依赖会等到怀疑人生。部署步骤按下面走# 1. 解压 zip 包进入根目录确认 pom.xml 存在 unzip 【java毕业设计】股票交易管理系统源码ssmmysql说明文档LW.zip -d stock-project cd stock-project # 2. 用 IDEA 打开项目等待 Maven 自动下载依赖 # 如果下载缓慢修改 ~/.m2/settings.xml 加入 aliyun 镜像mirror idaliyunmaven/id mirrorOfcentral/mirrorOf namealiyun public/name urlhttps://maven.aliyun.com/repository/public/url /mirror第三步是导入数据库。用 MySQL Workbench 或者命令行执行包内的 sql 文件注意先创建数据库再导入mysql -u root -p -e CREATE DATABASE IF NOT EXISTS stock_db DEFAULT CHARACTER SET utf8mb4; mysql -u root -p stock_db 包内的sql文件名.sql第四步是改配置文件里的数据库连接。把 jdbc.properties 里的 username 和 password 改成你自己的url 里的数据库名和 sql 里创建的库名保持一致。第五步是配置 Tomcat 启动。如果你用 IDEA选择 Run 菜单的 Edit Configurations点加号选 Tomcat Server LocalDeployment 选项卡里把 war exploded 加上Application context 建议改成 /这样访问路径不带项目名如果你要打 war 包独立部署命令行执行 mvn clean package然后把 target 目录下的 war 包拷到 Tomcat 的 webapps 目录启动 Tomcat 即可。mvn clean package -DskipTests cp target/stock-manage.war $TOMCAT_HOME/webapps/ cd $TOMCAT_HOME/bin ./startup.sh不要跳过包内说明文档。这类包都会附带说明文档和 LW论文里面通常写了默认管理员账号、测试数据和运行截图。拿到密码之后先登录一次熟悉页面再去看代码。一个常用习惯是先按说明文档正常路径走一遍再故意输错账号看系统有没有友好提示这一步能帮你发现过滤器和拦截器有没有配置。4.2 典型问题排查5 个坑基本能覆盖 80% 的翻车现场第一个高频坑数据库驱动对不上。现象是启动时报 Cannot load driver class 或连不上数据库控制台打印 Communications link failure。原因是包里的 mysql-connector-java 版本是 5.x而你本地装的是 MySQL 8.0两者认证协议不兼容。解决方法是把 pom.xml 里的 mysql 依赖改成 8.0 以上版本同时把驱动类换成 com.mysql.cj.jdbc.Driverurl 加 serverTimezone 参数。第二个高频坑JDK 编译版本和项目要求不符。现象是 IDEA 编译时提示“java: 警告: 源发行版 17 需要目标发行版 17”或者直接报错。这个报错的字面意思是 Maven 编译用的 JDK 版本和项目 pom.xml 里声明的版本不一致。解决方法是检查 File 菜单的 Project Structure 里 SDK 版本、Modules 里的 Language Level、以及 pom.xml 里的 maven.compiler.source 和 target。最省事的方式是直接在 pom.xml 里显式指定properties maven.compiler.source1.8/maven.compiler.source maven.compiler.target1.8/maven.compiler.target /properties如果你本地装的是 JDK 17硬把编译级别压到 1.8 可能会遇到 javax.xml.bind 缺失的问题这时候要么改用 JDK 8 启动项目要么在 pom 里补依赖两种方案选前者最简单毕业设计没必要在高版本上较劲。第三个高频坑页面中文全部变成问号。现象是浏览器里股票名称、用户昵称全是但数据库里直接查是正常的。原因是连接层的 characterEncoding 和数据库表字符集不一致。检查三个地方数据库建表语句是否用 utf8mb4连接 url 是否带 characterEncodingutf8JSP 页面第一行是否有 pageEncodingUTF-8。三个地方统一后刷新页面即可。第四个高频坑Tomcat 端口被占用。现象是启动时显示 Port 8080 was already in useTomcat 起不来。解决方法是换端口或杀掉占用进程。macOS 和 Linux 用 lsof -i:8080 查 PID 后 killWindows 用 netstat -ano | findstr 8080 查出占用进程也可以直接把 conf/server.xml 里的 8080 改成 8081一劳永逸。第五个高频坑Maven 依赖下载失败pom.xml 文件多处标红左下角一直转圈。原因是中央仓库连接不稳定。解决方法是换阿里云镜像就是你前面配置文件里加的那个 mirror改完后在 IDEA 里执行一次 Maven Reload。这个坑在一些网络环境差的区域几乎是必踩别硬等。5. 答辩前再往前一步补上登录拦截、压测数据与三处防御代码跑通只是及格线要拿高分得让答辩老师看到你“除了能跑还考虑过安全问题”。这一章给出三个投入小、见效快的加强点做完之后你对这个系统的理解会明显超过班上照抄的同学。第一处是登录拦截。直接访问 /trade/buy 这个接口如果没登录后端会取不到 session 里的用户。很多源码包这里只有判空返回没有拦 URL。用 SpringMVC 的 HandlerInterceptor 做一个登录校验把不需要拦截的路径放行其余全拦住public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (request.getSession().getAttribute(loginUser) ! null) { return true; } response.sendRedirect(request.getContextPath() /login.jsp); return false; } }然后在 spring-mvc.xml 里注册拦截器禁用拦截 /login、/register、静态资源。这一步做完系统才算有基本的安全边界。第二处是可以量化展示的验证数据。答辩时拿出两张截图比说一百句“系统测试通过”都有说服力。第一张是用 JMeter 并发 50 个线程同时下单的测试报告观察系统有没有出现超卖和负余额即使有少量报错也能说明你发现了问题第二张是 MySQL 慢查询日志show variables like slow_query_log;打开后对你常用的 SQL 执行 EXPLAIN 语句把 type 为 ALL 的查询加个索引再执行一次对比时间。这两张图直接放在论文测试章节属于能问也能答的内容。第三处防御代码是参数校验。买入数量必须是正整数价格必须大于 0卖出数量不能超过持仓数量这些校验既要在前端 JSP 里做一遍也要在后端 Service 里做一遍。前端校验是为了用户体验后端校验才是数据安全底线防止有人绕过页面直接 Post 请求。这些年带过的学生里做得最扎实的一个习惯是跑通系统后先把作者自己写的注释读一遍再删掉核心业务代码自己重写一遍。这比再找十个源码包都管用。系统跑起来只是开始能讲清楚每一行关键代码为什么存在才是这份毕业设计真正值钱的地方。希望帮到你。本文还有配套的精品资源点击获取
返回列表