尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
SSM+MySQL文物管理系统开发实战:表设计、事务与索引优化全解析
简介一份面向毕业设计场景的文物管理系统资料包适合计算机相关专业学生用于选题参考、二次开发或论文对照。系统以SSM框架为基础配合Mysql数据库采用B/S架构并通过JSP完成动态页面后台覆盖用户管理、文物分类、文物信息、文物外借、文物维修、留言板、论坛交流与系统管理前台包含首页、文物信息、文物资讯、留言反馈等模块业务逻辑完整。压缩包内共1331个文件整体约68.17MB其中包含大量Java源代码、JSP页面、CSS样式、JavaScript脚本以及PNG、JPG、GIF图片素材可分别对应后端逻辑、页面显示、前端样式与界面素材同时附有SQL数据库脚本、Word文档、MP4演示视频等关键内容。配套毕业论文围绕研究现状、开发背景、设计目标、需求分析、系统设计与实现、测试等环节展开论述配合PPT和开发文档可支撑完整毕设答辩与文档撰写。目前已有63人学习目录结构清晰适合快速检索代码、文档与多媒体素材作为一套完整的毕业设计方案进行参考。1. 文物管理系统选 SSM MySQL 来做的原因先说清楚再动手文物管理系统这类项目本质是给文物库房做一本“在线台账”每件文物的编号、名称、年代、材质、存放位置要随时查得到借出、归还、修复的状态流转要记得住不同角色管理员、库管员、借展方的权限要分得开。我见过不少翻车案例都是把 Excel 台账直接搬到网页上能录入能导出但一遇到“某件文物现在在谁手里、什么时候该还”就答不上来因为没有一张表把状态闭环起来。这个系统要解决的正是这件事。选择 SSMSpring SpringMVC MyBatis配 MySQL不是因为这套组合新而是因为它的复杂度刚好覆盖这个场景。文物的核心操作就是增删改查加一条借还流水数据量在百万级以内并发通常只有几十个人同时操作一个 Tomcat 实例加一台 MySQL 完全够用。Spring 管对象和事务SpringMVC 接请求和参数MyBatis 把 SQL 写清楚MySQL 用 InnoDB 保证事务和行锁每一层的职责都小而清晰。这套技术栈在今天仍然值得选的原因是它的可解释性。不管是答辩被问“权限怎么设计的、事务怎么控制的”还是面试被问“MyBatis 的动态 SQL 怎么防注入”SSM 的每个决定都能落到具体的类和配置上。微服务和容器编排确实更强但在这个量级的业务里它们引入的复杂度远大于收益。下面按环境搭建、表设计、核心流程落库、排错自检这条链路来讲给出的命令和代码都以实际能跑通为准。2. 搭建 SSM 工程骨架三个框架的分工、Maven 依赖与三份配置文件2.1 Spring 容器管对象SpringMVC 管请求MyBatis 管 SQL 的边界先明确一件事SSM 不是三个框架各写各的而是一条请求从浏览器到数据库再返回的完整链路。Spring 把 Service、Mapper 这些 Java 对象交给 IoC 容器管理并负责给 Service 方法加事务SpringMVC 只关心 HTTP 层从请求里取出参数、调用 Service、把返回值写成 JSON 或渲染页面MyBatis 负责最后一段——把接口方法翻译成 SQL执行后把结果集映射回 Java 对象。这条链路上最容易犯的错误是职责越界。比如在 Controller 里直接写 SQL 逻辑或者把参数校验放在 Service 里用 try/catch 包住再抛出通用异常。常见的规范做法是Controller 只做参数接收、格式转换和错误响应Service 做业务校验并保证事务Mapper XML 只写 SQL不做 if/else 分支业务。这个边界定清楚之后后面加功能、换数据库、写单元测试都会省很多事。MyBatis 与 Spring 的衔接是通过 mybatis-spring 这个桥接包完成的它负责把 SqlSessionFactory 注册进 Spring 容器并通过 MapperFactoryBean 动态生成 Mapper 接口的实现类。这一步不配置好最常见的报错是No qualifying bean of type xxxMapper原因通常是 Mapper 扫描路径和接口包的路径不一致后面 2.3 节会给出具体配置。2.2 Maven 依赖版本对齐是第一步坑先看 pom.xml 里需要哪些依赖。这里给出的是一个能跑文物管理系统的精简集合不要贪多properties spring.version5.3.31/spring.version mybatis.version3.5.16/mybatis.version /properties dependencies dependency groupIdorg.springframework/groupId artifactIdspring-context/artifactId version${spring.version}/version /dependency dependency groupIdorg.springframework/groupId artifactIdspring-webmvc/artifactId version${spring.version}/version /dependency dependency groupIdorg.springframework/groupId artifactIdspring-jdbc/artifactId version${spring.version}/version /dependency dependency groupIdorg.mybatis/groupId artifactIdmybatis/artifactId version${mybatis.version}/version /dependency dependency groupIdorg.mybatis/groupId artifactIdmybatis-spring/artifactId version2.1.2/version /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId version8.0.33/version /dependency dependency groupIdcom.alibaba/groupId artifactIddruid/artifactId version1.2.20/version /dependency dependency groupIdcom.github.pagehelper/groupId artifactIdpagehelper/artifactId version5.3.3/version /dependency dependency groupIdcom.fasterxml.jackson.core/groupId artifactIdjackson-databind/artifactId version2.15.2/version /dependency /dependencies几个容易踩的点第一mybatis-spring 2.x 只支持 Spring 5.x如果你把 spring 升到 6.x就要换 mybatis-spring 3.x这对毕业设计和中小项目来说没必要升级。第二MySQL 8 的驱动包名从mysql-connector-java改成了mysql-connector-j两个都能用但新项目我一般用后者避免旧驱动连接 8.0 数据库时出现Public Key Retrieval is not allowed的报错。第三pagehelper 必须在 mybatis 之后引入它是以 MyBatis 插件形式工作的。2.3 三份核心配置文件applicationContext.xml、springmvc.xml、mybatis-config.xml工程里一般拆三份 XML也可以合并但分开写更清晰。applicationContext.xml 管 Spring 容器、数据源和事务context:component-scan base-packagecom.relic context:exclude-filter typeannotation expressionorg.springframework.stereotype.Controller/ /context:component-scan bean iddataSource classcom.alibaba.druid.pool.DruidDataSource property namedriverClassName valuecom.mysql.cj.jdbc.Driver/ property nameurl valuejdbc:mysql://localhost:3306/relic_db?useUnicodetrueamp;characterEncodingutf8amp;serverTimezoneAsia/Shanghai/ property nameusername valueroot/ property namepassword value你的密码/ property nameinitialSize value5/ property namemaxActive value20/ /bean bean idsqlSessionFactory classorg.mybatis.spring.SqlSessionFactoryBean property namedataSource refdataSource/ property nameconfigLocation valueclasspath:mybatis-config.xml/ property namemapperLocations valueclasspath:mapper/*.xml/ /bean bean classorg.mybatis.spring.mapper.MapperScannerConfigurer property namebasePackage valuecom.relic.mapper/ /bean bean idtransactionManager classorg.springframework.jdbc.datasource.DataSourceTransactionManager property namedataSource refdataSource/ /bean tx:annotation-driven transaction-managertransactionManager/逻辑说明component-scan 用 exclude-filter 排除 Controller是为了避免 Spring 容器和 SpringMVC 容器重复管理同一个 beansqlSessionFactory 里的 mapperLocations 指向 XML 文件目录MapperScannerConfigurer 把接口自动扫描进容器这样 Service 里直接Autowired就能拿到 Mapper。事务管理器接的是同一个 dataSource否则事务不生效。springmvc.xml 只管 Controller 那一层context:component-scan base-packagecom.relic.controller/ mvc:annotation-driven mvc:message-converters bean classorg.springframework.http.converter.json.MappingJackson2HttpMessageConverter/ /mvc:message-converters /mvc:annotation-drivenmybatis-config.xml 管 SQL 的全局行为settings setting namemapUnderscoreToCamelCase valuetrue/ /settings typeAliases package namecom.relic.entity/ /typeAliases plugins plugin interceptorcom.github.pagehelper.PageInterceptor property namehelperDialect valuemysql/ property namereasonable valuetrue/ /plugin /pluginsmapUnderscoreToCamelCase 让storage_loc自动映射成storageLoc可以少写大量 resultMap。typeAliases 让 XML 里写resultTypeRelicDO而不是一长串全限定名。PageHelper 在这里注册为插件比在 Spring 里配置少一步。2.4 MySQL 8 的安装与连接参数把连不上和乱码挡在第一天环境搭建部分我一般直接用 MySQL 8.0 的最新稳定版安装包装完后用 MySQL Workbench 或 Navicat 连接测试。如果你机器上已经有 Docker也可以用一行命令起一个 MySQL 8docker run -d --name relic-mysql \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDroot123 \ -e MYSQL_DATABASErelic_db \ mysql:8.0注意这里的数据没有挂载卷只适合开发环境。真正要长期跑的项目务必加上-v把数据目录挂到宿主机否则容器一删库就没了。连接串是开发期最常见的报错来源。MySQL 8 的 JDBC URL 有四个参数需要固定写对对照下表参数值作用serverTimezoneAsia/Shanghai解决 8 小时时差避免日期字段错一天useUnicode / characterEncodingtrue / utf8中文写入不乱码必须写 UTF-8useSSLfalse本地开发不需要 SSL 握手避开证书报错allowPublicKeyRetrievaltrue配合 caching_sha2_password避免连不上报错把这些写在 2.3 节的 dataSource url 里。如果你看到The server time zone value Öйú±ê׼ʱ¼ä is unrecognized这种乱码报错就是 serverTimezone 没加。3. 文物台账的 MySQL 表设计拆分、字段与索引一次定清楚3.1 拆表思路主表、分类表、借还流水表三张起步文物管理系统的表设计核心矛盾是“文物属性多、查询条件杂”但业务体量又不足以支撑复杂的微服务拆分。常见的做法是拆三张表起步relic_info放文物主信息relic_category放分类borrow_record放借出归还流水。修复记录、盘点记录、操作日志量少但增长快先不建表放在 remark 或系统日志里等功能稳定再拆。为什么不把分类直接塞进主表当冗余字段因为分类要支持“按瓷器、书画、青铜器筛选”还要支持二级分类独立成表用parent_id就能递归表达查询时 LEFT JOIN 一次成本很低。借还流水则必须独立因为一件文物会被多次借出一对多关系如果放主表要么冗余行、要么只能留最后一次记录盘点时完全没法追溯历史。3.2 建表 SQL三张表的字段、类型与设计理由CREATE TABLE relic_category ( id BIGINT UNSIGNED AUTO_INCREMENT COMMENT 分类ID, category_name VARCHAR(64) NOT NULL COMMENT 分类名称如瓷器、书画、青铜器, parent_id BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT 父分类ID0表示一级, sort_order INT NOT NULL DEFAULT 0 COMMENT 排序值越小越靠前, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), UNIQUE KEY uk_category_name (category_name) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT文物分类表; CREATE TABLE relic_info ( id BIGINT UNSIGNED AUTO_INCREMENT COMMENT 文物ID, relic_no VARCHAR(32) NOT NULL COMMENT 文物编号台账唯一, relic_name VARCHAR(128) NOT NULL COMMENT 文物名称, category_id BIGINT UNSIGNED NOT NULL COMMENT 分类ID逻辑关联分类表, era VARCHAR(32) DEFAULT NULL COMMENT 年代/朝代如明朝, material VARCHAR(32) DEFAULT NULL COMMENT 材质, grade TINYINT NOT NULL DEFAULT 3 COMMENT 等级1一级 2二级 3三级 4一般, status TINYINT NOT NULL DEFAULT 1 COMMENT 状态1在库 2借出 3修复 4注销, storage_loc VARCHAR(64) DEFAULT NULL COMMENT 库房位置如A-12-3, entry_date DATE DEFAULT NULL COMMENT 入藏日期, image_url VARCHAR(255) DEFAULT NULL COMMENT 图片路径, description TEXT COMMENT 描述, is_deleted TINYINT NOT NULL DEFAULT 0 COMMENT 逻辑删除0否 1是, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_relic_no (relic_no), KEY idx_category_id (category_id), KEY idx_create_time (create_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT文物主表; CREATE TABLE borrow_record ( id BIGINT UNSIGNED AUTO_INCREMENT COMMENT 流水ID, relic_id BIGINT UNSIGNED NOT NULL COMMENT 文物ID, applicant VARCHAR(64) NOT NULL COMMENT 借用人, apply_dept VARCHAR(64) DEFAULT NULL COMMENT 借用单位/部门, borrow_date DATE NOT NULL COMMENT 实际借出日期, expected_date DATE DEFAULT NULL COMMENT 预计归还日期, actual_date DATE DEFAULT NULL COMMENT 实际归还日期, status TINYINT NOT NULL DEFAULT 1 COMMENT 1借出中 2已归还 3逾期, handler VARCHAR(64) DEFAULT NULL COMMENT 经办人, remark VARCHAR(255) DEFAULT NULL COMMENT 备注, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_relic_id (relic_id), KEY idx_status_expected (status, expected_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT文物借出归还流水表;几个字段设计上的关键决定主键用BIGINT UNSIGNED AUTO_INCREMENT不要用文物编号做主键因为编号可能因账本修订而变化且是字符串InnoDB 聚簇索引对有序整数最友好。relic_no单独建唯一索引对应实物上的编号保证一套账里不重复。不建外键约束只留category_id做逻辑关联理由是文物注销后分类不能随便删但主表记录要保留外键的级联删除行为在这里是负资产。status用 TINYINT 而不是字符串避免“在库、借出、修复”写错字取值做成字典后面代码里用常量引用。类型上时间字段全用DATETIME。TIMESTAMP到 2038 年就溢出而且受时区影响会换算对中文环境的台账系统不友好。entry_date是纯日期用DATE而不是 DATETIME查询“某月入藏几件”时不需要处理时间部分。金额、数量在这个系统里基本用不上如果有预估价值建议用DECIMAL(12,2)而不是 FLOAT避免二进制浮点误差。3.3 索引用在哪三处mysql 创建索引的完整命令上面建表时已经带了几个索引但实际开发过程中经常要补。MySQL 创建索引的常见命令如下-- 组合索引按状态预计归还日查逾期 ALTER TABLE borrow_record ADD INDEX idx_status_expected (status, expected_date); -- 覆盖查询按分类入库时间倒序拿列表 ALTER TABLE relic_info ADD INDEX idx_cat_time (category_id, create_time); -- 查看执行计划确认索引有没有被用上 EXPLAIN SELECT id, relic_name FROM relic_info WHERE category_id 2 ORDER BY create_time DESC;索引选型的判断标准看 where 条件的区分度和组合情况。relic_no唯一索引必建category_id在按分类筛选时用得上create_time配合列表倒序分页。像status这种只有 1 到 4 几个取值的字段单独建索引几乎没有效果但要和expected_date组成组合索引让“逾期未还”这种高频统计走覆盖索引。EXPLAIN 里看到typeref且key不为空基本说明优化器接受了这个索引看到Using filesort就检查 ORDER BY 的字段是不是索引的一部分。另外注意MySQL 里“更新子查询”有一个经典限制不能直接UPDATE relic_info SET ... WHERE id IN (SELECT id FROM relic_info WHERE ...)会报You cant specify target table for update in FROM clause。常见做法是再包一层派生表或者像我 4.3 节那样改成条件更新从根上绕开这个问题。3.4 中文模糊查询like 走不了索引时的替代方案文物名称、描述的中文查询一般写成LIKE %青花%这种前导百分比的写法走普通 B 树索引是没有用的数据量小几千行时无所谓但到十万行级别就会明显变慢。MySQL 8 的 InnoDB 支持中文全文索引用 ngram 解析器按连续两个汉字切词ALTER TABLE relic_info ADD FULLTEXT INDEX ft_relic_name (relic_name) WITH PARSER ngram; SELECT relic_no, relic_name FROM relic_info WHERE MATCH(relic_name) AGAINST(青花 IN NATURAL LANGUAGE MODE);需要注意 ngram 默认ngram_token_size2搜单字“瓷”基本搜不准且全文索引和LIKE的语义不同它按词匹配而非子串匹配。我的建议是列表页模糊查询先用LIKE CONCAT(%, #{keyword}, %)配合 Pagination 的小页面数据量够用当数据量增长、用户明显感觉慢之后再让搜索请求走全文索引或直接上专门的检索组件不要在项目第一天就把索引加满。4. 文物核心流程落库Controller 校验、Service 事务、Mapper 条件更新4.1 文物入库编号查重与业务异常回滚文物入库是最典型的三层写库流程。Controller 接收参数Service 做编号查重和基础校验Mapper 执行插入。代码结构如下RestController RequestMapping(/api/relic) public class RelicController { Autowired private RelicService relicService; PostMapping(/add) public Result add(RequestBody RelicDTO dto) { // DTO 转 DO前端传来的字段不会直接拼进 SQL RelicDO relic new RelicDO(); BeanUtils.copyProperties(dto, relic); relicService.addRelic(relic); return Result.ok(入库成功); } }Service public class RelicServiceImpl implements RelicService { Autowired private RelicMapper relicMapper; Override Transactional(rollbackFor Exception.class) public void addRelic(RelicDO relic) { int count relicMapper.countByNo(relic.getRelicNo()); if (count 0) { throw new BizException(文物编号已存在 relic.getRelicNo()); } relicMapper.insert(relic); } }逻辑说明查重用COUNT而不是查出整行再判断省一次结果集映射事务注解写rollbackFor Exception.class而不是默认的 RuntimeException因为查重抛出的 BizException 如果只继承 Exception不加这个参数就回滚不了参数校验放在 DTO 上做NotBlank等注解校验比在 Service 里 if/else 干净。对应的 Mapper XMLinsert idinsert parameterTypeRelicDO useGeneratedKeystrue keyPropertyid INSERT INTO relic_info (relic_no, relic_name, category_id, era, material, grade, status, storage_loc, entry_date, image_url, description) VALUES (#{relicNo}, #{relicName}, #{categoryId}, #{era}, #{material}, #{grade}, #{status}, #{storageLoc}, #{entryDate}, #{imageUrl}, #{description}) /insert select idcountByNo resultTypeint SELECT COUNT(*) FROM relic_info WHERE relic_no #{relicNo} AND is_deleted 0 /select注意 countByNo 里的is_deleted 0这是 3.2 节逻辑删除字段的正确用法查重和后续所有业务查询都要过滤已删除记录否则删除后再入库同名编号会被误判为已存在。4.2 分页查询PageHelper 的 startPage 用法与 ThreadLocal 边界列表查询是前端的核心交互分页用 PageHelper 插件Service 代码非常短public PageResultRelicVO pageQuery(int pageNum, int pageSize, RelicQuery query) { PageHelper.startPage(pageNum, pageSize); ListRelicVO list relicMapper.pageQuery(query); PageInfoRelicVO pageInfo new PageInfo(list); return new PageResult(pageInfo.getTotal(), pageInfo.getList()); }select idpageQuery resultTypeRelicVO SELECT r.id, r.relic_no, r.relic_name, c.category_name, r.era, r.grade, r.status, r.storage_loc FROM relic_info r LEFT JOIN relic_category c ON r.category_id c.id WHERE r.is_deleted 0 if testkeyword ! null and keyword ! AND r.relic_name LIKE CONCAT(%, #{keyword}, %) /if if testcategoryId ! null AND r.category_id #{categoryId} /if ORDER BY r.create_time DESC /selectPageHelper 的实现原理是 ThreadLocalstartPage把分页参数塞进当前线程下一次执行的 Mapper SQL 会被拦截器改写成分页语句执行完后参数被移除。这带来三个必须遵守的边界也对应三个面试常问点规则原因startPage 后必须紧跟一条查询中间插入其他 SQL 会被这个查询消费掉分页参数同一个线程里不要并发调用两个查询ThreadLocal 不是线程隔离的异步线程会串参数ORDER BY 写在 Mapper XML 里插件在 SQL 末尾追加 LIMIT后加的排序会被挤到错误位置reasonabletrue的效果是页码越界自动收敛到第一页或最后一页不会因为用户手改 URL 而报错。PageInfo 里除了 total、list还有连续页码数组、总页数等字段前端分页组件直接取用不需要再手动拼装。4.3 借出归还一个事务两条 SQL条件更新锁住库存借出归还比入库复杂的地方在于它是“改状态 写流水”两步操作必须在一个事务里完成并且要防止两个人同时借出同一件文物。先看方案Override Transactional(rollbackFor Exception.class) public void borrow(Long relicId, BorrowDTO dto) { // 条件更新只有“在库”才允许借出影响行数作为唯一凭据 int updated relicMapper.updateStatusFromInStock(relicId); if (updated ! 1) { throw new BizException(该文物当前状态不可借出); } int inserted borrowRecordMapper.insertForBorrow(relicId, dto); if (inserted ! 1) { throw new BizException(借出流水写入失败); } }update idupdateStatusFromInStock UPDATE relic_info SET status 2 WHERE id #{relicId} AND status 1 AND is_deleted 0 /update核心思路是“条件更新代替先查后改”。两个请求同时走到这段代码时第一句 UPDATE 会给这一行加行锁第二个事务会阻塞等待第一个事务提交后第二个事务执行 UPDATE 时因status 1条件不满足影响行数为 0直接抛出“不可借出”。用 affected rows 判断结果就从根上消灭了竞态这也是很多并发 bug 的修复方向。MySQL 锁表问题大多不是行锁本身而是事务没及时提交或索引没命中导致锁升级成表锁所以事务方法里尽量只做必要操作不要夹带外部接口调用或长时间循环。归还的逻辑对称处理更新borrow_record的 actual_date 和 status再把relic_info状态改回 1。两条更新同样在一个事务里任何一个失败整体回滚。4.4 逾期统计的查询写法与存储过程的取舍逾期查询用于“到归还日还没还的文物”对borrow_record里的状态做日期过滤SELECT r.relic_no, r.relic_name, b.applicant, b.expected_date FROM borrow_record b JOIN relic_info r ON r.id b.relic_id WHERE b.status 1 AND b.expected_date CURDATE() ORDER BY b.expected_date ASC;这个查询利用组合索引(status, expected_date)status 和 expected_date 都在索引里回表次数可控。至于“每天自动统计逾期”的定时任务我一般用 Spring 的Scheduled在应用层做而不是 MySQL 存储过程。原因很简单存储过程里的逻辑没法用单元测试验证日志不完整改一处要重新执行 DDL应用层定时任务可以打印每批处理结果出问题能快速定位。MySQL 的存储过程不是不能用只是在这个场景里维护成本高过收益。5. 上线自检连接串、乱码、账实核对一次做完5.1 新驱动、时区、SSL 三个连接参数的对照把 2.4 节的参数落到实际报错上排查。看到控制台出现Loading class com.mysql.jdbc.Driver. This is deprecated说明还在用旧驱动类名把 driverClassName 改成com.mysql.cj.jdbc.Driver。出现Public Key Retrieval is not allowed检查 URL 是否带allowPublicKeyRetrievaltrue。出现时间差 8 小时检查是不是漏了serverTimezoneAsia/Shanghai。这三个问题占了 MySQL 8 连接故障的大头自检时按顺序扫一遍即可。5.2 汉字乱码的排查顺序文物名称、库房位置这类中文字段乱码按四层查第一JDBC URL 里 characterEncodingutf8第二数据库和表的 CHARSET 是否 utf8mb4建表 SQL 里已带但手工改过的旧库要复查第三Tomcat 的 URI 编码在 server.xml 里给 Connector 加URIEncodingUTF-8否则 GET 请求的中文参数会乱第四页面响应头 charset。排查时最先看 URL其次看表结构不要一上来就改代码。5.3 账实核对 SQL 与并发借出的验证方法部署完成后建议用一段对账 SQL 验证状态一致性主表显示已借出但流水表里没有对应的未归还记录说明状态流转的 Update 漏了-- 找出状态与流水不一致的文物 SELECT r.id, r.relic_no, r.relic_name FROM relic_info r LEFT JOIN borrow_record b ON b.relic_id r.id AND b.status 1 WHERE r.status 2 AND b.id IS NULL;事务行为的验证不依赖界面在 Service 借出方法里临时让第二条 SQL 抛一个运行时异常跑一遍后检查主表状态是否仍为 1回滚成功说明Transactional配置正确。并发借出同一件文物开两个终端分别执行借出观察只有一个事务成功拿到状态变更另一个抛出“不可借出”说明条件更新的锁行为符合预期。这两条验证通过后再接界面层能省掉大量接口联调时的排查时间。本文还有配套的精品资源点击获取
RELATED

相关推荐

springbootA社区生活服务小程序14485-计算机课程设计、毕业设计

springbootA社区生活服务小程序14485-计算机课程设计、毕业设计

前言 ✨ 博主介绍:一线全栈工程师,毕设实战引路人。技术栈覆盖Java、Python、C#、PHP、Node.js及UniApp跨端开发,擅长多语言项目落地与架构设计。持续分享毕设源码、开题报告、技术选型心得与职场踩坑经验。用工程化思维写代码,帮…

📅 2026/9/12 18:53:33
编译原理作业实战:词法分析、语法分析与错误恢复的完整实现

编译原理作业实战:词法分析、语法分析与错误恢复的完整实现

简介:北京邮电大学计算机科学与技术专业大三上学期编译原理课程作业完整资料包,作业得分97分。内容覆盖词法分析与语法分析两大核心模块,包含可直接运行的源代码、实验报告、文档说明及配套PPT和PDF讲义,适合正在学习编译原理、需…

📅 2026/9/12 18:53:33
51单片机PT100测温系统:ADC0808采集与仿真设计

51单片机PT100测温系统:ADC0808采集与仿真设计

简介:一套基于51单片机的热电偶测温设计资料包,面向电子、自动化、物联网等专业学生及单片机开发者,适配课程设计、毕业设计与项目仿真实训。系统以AT89C51/STC89C52为核心,使用PT100热电偶传感器、TDA2030信号放大电路、ADC0808模…

📅 2026/9/12 18:53:33
MORE NEWS

更多资讯

📰

综述不是“读了多少”,是“看出什么”:书匠策AI把文献变成了可操作的关系网络

官网:www.shujiangce.com | 微信 公众号 :书匠策AI 书匠策AI官网www.shujiangce.com 微信公众号搜一搜 书匠策AI 你花了三天读完三十篇文献,每一篇都做了笔记,摘要划了,结论记了。然后你坐在电脑前,试…

📰

SpringBoot+Vue全栈企业资产管理系统开发实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

DMM5565双显示万用表实战:同步测量电压电流与功率

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

YOLOv13改进策略【卷积层篇】| CVPR 2023 MobileOneBlock 苹果官方重参数化块,一次推理一毫秒

本文基于 YOLOv13 官方仓库(iMoonLab/yolov13,ultralytics 8.3.63 fork) 实测整理,Windows/CPU 全程可跑。重参数化系列的收官篇来自 Apple:MobileOneBlock(Ma et al., CVPR 2023《An Improved One millisecond Mobile Backbone》,官方源码 github.com/apple/ml-mobileo…

📰

YOLOv13改进策略【注意力机制篇】| NeurIPS 2018 A2 双注意力,先聚合再分发的两步全局建模

本文基于 YOLOv13 官方仓库(iMoonLab/yolov13,ultralytics 8.3.63 fork) 实测整理,Windows/CPU 全程可跑。非局部注意力的 (HW) 矩阵一步到位但太贵;A2(Double Attention Network,Chen et al., NeurIPS 2018)拆成两步:先用 B 的空间相似度把特征"聚合"成 c_…

📰

基于OpenCV与PyQt的行人检测系统:本地推理与实时告警实现

简介:成套的毕业设计行人识别检测系统源码,基于OpenCV与PyQt开发,核心场景是车辆行驶中自动探测车前行人,一旦有人进入行进路线立即触发警告,适用于计算机相关专业的学生作为毕业设计、课程设计或期末大作业&#xff0…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬