SSM+MySQL知识产权管理系统实战:从表结构设计到部署上线

发布时间:2026/10/6 2:57:17
SSM+MySQL知识产权管理系统实战:从表结构设计到部署上线
简介一套基于SSM框架与JSP技术的知识产权管理系统适合高校计算机、软件工程专业用于课程设计或毕业设计。项目采用EclipseMySQL搭建包含前台门户和后台管理两大模块前台提供用户注册登录、公告浏览、免费/付费知识产权搜索下载付费下载支持租借时间后台分为管理员、员工与注册用户三类角色涵盖管理员对员工、公告、资料、下载记录的管理员工对申报的审核与失败反馈以及用户在线申报、结果查询、下载记录等功能形成完整业务闭环。压缩包内共1071个文件以122个JSP页面、73个Java源文件及class字节码、80个Jar依赖包为主并包含CSS、JavaScript、XML配置以及287个GIF动图、110个PNG图片等素材整体32.36MB目录结构清晰。已有78人学习。资源另附SQL初始化脚本与支付宝接口示例导入Eclipse并配置MySQL即可运行便于深入理解SSM整合、角色权限控制、文件上传与审核流程等实现。1. 拿到“ssm_152_mysql_知识产权管理系统_.zip”之后先弄明白它在实战里到底是啥做过 Java Web 开发的人对这个命名都不陌生ssm 是 Spring SpringMVC MyBatis 三件套152 是项目编号或者老师布置的作业序号mysql 是说存储层用 MySQL知识产权管理系统则是业务领域。这串字符背后是一门课程设计的标配也是很多外包公司接单时最常见的起步原型——一个覆盖了用户登录、专利/商标/著作权登记、审批流程、数据检索和报表统计的增删改查系统。这类系统真正能帮你解决的是两件事一是把 SSM 框架的调用链彻底跑通从浏览器 URL 一直到 MySQL 的数据行每一步都看得见摸得着二是理解一个带状态流转的业务系统应该怎么设计表结构而不是上来就堆功能。适合准备校招项目、刚入行要接手老系统的开发以及想用最短时间搭一套可演示后台管理界面的从业者。下面前五章按真实交付顺序往下走先建表再搭框架然后写核心功能最后把常见坑和上线部署一次聊透。2. SSM 项目必须先摸清数据模型知识产权管理系统的表结构设计与 MySQL 落地2.1 业务拆解专利、商标、著作权三类对象为什么不放在一张表里知识产权管理系统最核心的实体是“知识产权资产”但实际做表结构时我几乎不会把专利、商标、著作权直接塞进一张大表虽然它们有很多公共字段。原因有三个一是专利要存申请号、发明人、代理机构这些特有字段商标要存尼斯分类、注册有效期著作权则要存作品类型和首次发表日硬塞一张表会让大多数列对多数行是 NULL存储浪费倒是其次关键是 MyBatis 的实体映射会变得非常别扭每次查询都要写一堆if test... ! null二是流程状态机不一样专利要经历受理、实审、授权、年费缴纳商标是申请、初审、公告、注册它们的状态枚举值不能共用一套数字编号三是后端检索逻辑天然按类型分开专利查“IPC 分类号”、商标查“类别号”业务聚合没有一张表的强需求。再加上用户表、角色表、流程记录表这套系统的核心表我一般控制在 6 张左右sys_user、sys_role、ipr_patent、ipr_trademark、ipr_copyright、ipr_process_log。设计原则是尽量少做物理外键只用逻辑关联和索引这一点在后面的数据量上来之后会非常省心。2.2 建库建表六张核心表的 SQL 与字段设计逻辑MySQL 侧我建议直接上 5.7 或 8.0字符集用 utf8mb4排序规则用 utf8mb4_general_ci别再用 utf8。MySQL 的 utf8 实际最多存 3 字节而发明人姓名里一旦出现生僻字或 emoji 就会报“Incorrect string value”这个坑我踩过不止一次。CREATE DATABASE ipr_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE ipr_system; CREATE TABLE sys_user ( id INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 用户ID, username VARCHAR(50) NOT NULL COMMENT 登录名, password VARCHAR(100) NOT NULL COMMENT 密码BCrypt或MD5加盐, real_name VARCHAR(50) DEFAULT NULL, role_id INT UNSIGNED NOT NULL COMMENT 关联sys_role.id, status TINYINT NOT NULL DEFAULT 1 COMMENT 1启用 0禁用, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_username (username), KEY idx_role_id (role_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;注意几点id 用 INT UNSIGNED 而不是 BIGINT因为这套系统撑死几十万用户别学互联网大厂无脑上 BIGINTpassword 字段长度给 100因为后面如果用 BCrypt 加密60 位是常态MD5 加盐也得 40 位以上UNIQUE KEY 加在 username 上是为了登录查询直接走索引create_time 用 DATETIME 而不是 TIMESTAMP因为 TIMESTAMP 在 2038 年会有溢出问题而且 DATETIME 不依赖数据库时区设置少一堆麻烦。接着是核心业务表以专利表为例CREATE TABLE ipr_patent ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, patent_no VARCHAR(32) NOT NULL COMMENT 专利申请号, patent_name VARCHAR(200) NOT NULL COMMENT 专利名称, patent_type TINYINT NOT NULL COMMENT 1发明 2实用新型 3外观设计, applicant VARCHAR(200) NOT NULL COMMENT 申请人, inventor VARCHAR(500) DEFAULT NULL COMMENT 发明人多人用逗号分隔, apply_date DATE NOT NULL COMMENT 申请日, status TINYINT NOT NULL DEFAULT 1 COMMENT 1受理 2实审 3授权 4驳回 5失效, annual_fee_due_date DATE DEFAULT NULL COMMENT 年费缴费截止日, create_by INT UNSIGNED NOT NULL, 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_patent_no (patent_no), KEY idx_apply_date (apply_date), KEY idx_status (status), KEY idx_create_by (create_by) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT专利表;mysql 存储整数数值这块要重点说patent_type 和 status 都用 TINYINT 而不是 VARCHAR 存中文看起来牺牲了可读性但换来了三个好处——MySQL 对整数列的索引和排序性能远好于字符串后端可以用枚举类做类型转换统计 SQL 里 GROUP BY status 出来的数字可以直接映射成图表横轴。mysql 排序 这类需求在报表场景非常频繁如果状态列是中文排序结果就会变成按拼音或编码顺序排列完全不可控。2.3 物理外键与逻辑外键为什么不用 FOREIGN KEY很多课程设计喜欢加 FOREIGN KEY 把表关联起来navicat 画 ER 图也好看但实际维护时会发现 MySQL 的物理外键有三个问题插入和更新时要额外做一致性检查高频写入场景性能损耗明显做数据归档或批量删除时经常被外键约束卡住MySQL 的锁分类 里外键检查还可能导致额外的共享锁竞争。这套系统里我更推荐逻辑外键加索引的方式也就是只存 create_by、role_id 这样的字段加普通 KEY应用层自己保证引用关系。这符合实际项目里“宁可在代码里做事务一致也不用数据库外键锁自己”的普遍做法。建完表之后可以顺带做一次索引评审具体看两点WHERE 条件的列有没有索引ORDER BY 和 GROUP BY 的列有没有索引。比如管理员经常看“按申请日排序的专利列表”idx_apply_date 就能覆盖避免 filesort。mysql 创建索引 要克制核心表索引控制在 5 个以内多了写入慢且占空间。3. SSM 框架整合落地从空目录到跑通第一个请求的完整配置3.1 SSM 三件套到底各管什么版本怎么选SSM 之所以这么多年来一直被用作课程设计和中小型系统的首选是因为分工清晰Spring 管理对象创建和依赖注入SpringMVC 负责 HTTP 层的路由和参数绑定MyBatis 把 SQL 和 Java 方法做映射。三者各管一层出了问题时排查思路非常线性URL 进不来找 SpringMVCBean 注入失败找 SpringSQL 报错找 MyBatis。版本选型上我用得最多的是 Spring 5.3.x SpringMVC 5.3.x MyBatis 3.5.x mybatis-spring 2.0.x 的组合适配 JDK 8 和 Tomcat 8.5/9。别图新鲜上 Spring 6那套要求 JDK 17很多学校的服务器环境和公司老机器根本跑不动。MySQL 驱动用 5.1.49 或 8.0.x 都行但两者在 JDBC URL 的写法上不一样8.0 驱动要求必须带 serverTimezone 参数否则会直接报 CST 时区错误。3.2 工程目录与 pom.xml 的最小依赖集合我习惯按 Maven 标准结构建工程ssm_152 这类 zip 解压后也应该先确认是不是这个结构src/main/java 包名代码 src/main/resourcesspring、mybatis、mapper xml、日志配置 src/main/webapp 前端页面和静态资源 pom.xmlpom.xml 是最容易复制出错的环节依赖少了启动报 ClassNotFound版本错了报 NoSuchMethodError。下面是能跑通这套系统的最小依赖集properties spring.version5.3.20/spring.version /properties dependencies !-- Spring 核心 -- dependency groupIdorg.springframework/groupId artifactIdspring-context/artifactId version${spring.version}/version /dependency !-- SpringMVC -- dependency groupIdorg.springframework/groupId artifactIdspring-webmvc/artifactId version${spring.version}/version /dependency !-- MyBatis 核心 -- dependency groupIdorg.mybatis/groupId artifactIdmybatis/artifactId version3.5.10/version /dependency !-- MyBatis 与 Spring 整合包 -- dependency groupIdorg.mybatis/groupId artifactIdmybatis-spring/artifactId version2.0.7/version /dependency !-- MySQL 驱动 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version5.1.49/version /dependency !-- 连接池 -- dependency groupIdcom.alibaba/groupId artifactIddruid/artifactId version1.2.8/version /dependency !-- Servlet/JSP 依赖打包时可以设为 provided -- dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version3.1.0/version scopeprovided/scope /dependency /dependencies参数说明spring-webmvc 会连带引入 spring-web 和 spring-core不需要重复声明mybatis-spring 负责把 MyBatis 的 SqlSessionFactory 交给 Spring 容器统一管理druid 连接池承担数据库连接复用避免每次请求都新建物理连接。版本号建议用上面的组合经得起实际跑别自己乱升级。3.3 核心配置spring-mvc.xml、spring-mybatis.xml 与 web.xml配置是 SSM 项目的黑匣子多数人启动失败都是因为在三个 XML 之间配乱了。区分方法很简单spring-mvc.xml 只管 Controller 和 URL 映射spring-mybatis.xml 管数据源、事务和 Mapperweb.xml 负责把两者加载进 Tomcat。!-- spring-mvc.xml -- context:component-scan base-packagecom.ipr.controller / mvc:annotation-driven / bean classorg.springframework.web.servlet.view.InternalResourceViewResolver property nameprefix value/WEB-INF/views/ / property namesuffix value.jsp / /beancomponent-scan 只扫 controller 包不要把 Service 和 Repository 也带进来否则事务配置会被绕过或重复代理。InternalResourceViewResolver 是 JSP 视图解析器prefix 指向 WEB-INF 下的 views 目录这样浏览器不能直接访问 JSP 源文件只能通过 Controller 转发进入避免模板逻辑裸露在公网。!-- spring-mybatis.xml -- context:component-scan base-packagecom.ipr.service / bean iddataSource classcom.alibaba.druid.pool.DruidDataSource property namedriverClassName valuecom.mysql.jdbc.Driver / property nameurl valuejdbc:mysql://localhost:3306/ipr_system?useUnicodetrueamp;characterEncodingutf8 / property nameusername valueroot / property namepassword value123456 / /bean bean idsqlSessionFactory classorg.mybatis.spring.SqlSessionFactoryBean property namedataSource refdataSource / property namemapperLocations valueclasspath:mapper/*.xml / /bean bean classorg.mybatis.spring.mapper.MapperScannerConfigurer property namebasePackage valuecom.ipr.dao / /bean tx:annotation-driven transaction-managerdataSourceTransactionManager /url 里的 字符在 XML 里必须写成 这是高频率报错点。characterEncodingutf8 解决中文参数写入和读取乱码。MapperScannerConfigurer 会把 com.ipr.dao 包下所有接口自动生成代理实现类不需要写 DAO 实现类这是 MyBatis-Spring 整合的核心价值。web.xml 中要做三件事配置 DispatcherServlet、加载 spring-mybatis.xml 和 spring-mvc.xml、设置编码过滤器。编码过滤器要放在所有过滤器最前面否则 POST 请求中文乱码。如果启动后 Tomcat 直接报 404优先看 DispatcherServlet 的 url-pattern 是不是配成了/*正确写法是//*会拦截 JSP 请求导致全站打不开。最后再加一条 mybatis-config.xmlconfiguration settings setting namemapUnderscoreToCamelCase valuetrue / setting namelogImpl valueSTDOUT_LOGGING / /settings /configurationmapUndertheScoreToCamelCase 开启后数据库列名 patent_no 会直接映射到 Java 属性 patentNo省掉一大把 resultMap 手写映射。logImpl 设为 STDOUT_LOGGING 是把 SQL 打到控制台开发阶段强烈建议开着可以直观看到 MyBatis 生成的 SQL 是啥样。记得在 spring-mybatis.xml 的 SqlSessionFactoryBean 里通过 configLocation 属性引入这个文件。4. 从登录到专利列表用 SSM 把核心链路跑通并写清楚每个注解的作用4.1 登录认证Session、拦截器与密码存储知识产权管理系统里的登录不只是查一次用户表那么简单。用户登录成功后要把用户对象放进 Session然后通过 SpringMVC 拦截器拦截未登录请求否则直接访问/patent/list这种 URL 就能绕过后台管理页面拿到全部数据。先写 ControllerController RequestMapping(/user) public class UserController { Autowired private UserService userService; RequestMapping(value /login, method RequestMethod.POST) public String login(String username, String password, HttpSession session, Model model) { User user userService.login(username, password); if (user null) { model.addAttribute(error, 用户名或密码错误); return login; } session.setAttribute(loginUser, user); return redirect:/patent/list; } RequestMapping(/logout) public String logout(HttpSession session) { session.invalidate(); return redirect:/user/loginPage; } }逻辑说明login 方法接收表单提交的 username 和 password调用 Service 层校验。userService.login 返回 null 说明账号密码不对回到登录页并带错误提示校验通过就把 user 对象放进 sessionredirect 方式跳转到专利列表避免浏览器刷新时重复提交表单。logout 用 session.invalidate() 把整个会话销毁防止会话固定攻击。拦截器是这节的硬骨头没有它登录等于白做public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session request.getSession(); if (session.getAttribute(loginUser) ! null) { return true; } // 未登录跳回登录页保留来源URL方便登录后跳回 response.sendRedirect(request.getContextPath() /user/loginPage); return false; } }参数说明sendRedirect 是重定向浏览器地址栏会变化getContextPath() 拿到应用根路径这样即使以后改了部署路径也不会写死。然后在 spring-mvc.xml 里注册拦截器excludePathPatterns 必须把登录接口、登录页、静态资源放行mvc:interceptors mvc:interceptor mvc:mapping path/** / mvc:exclude-mapping path/user/loginPage / mvc:exclude-mapping path/user/login / mvc:exclude-mapping path/static/** / /mvc:interceptor /mvc:interceptors拦截器顺序很关键放在异常处理器之前一旦 preHandle 返回 false 就不再执行后续拦截器和 Controller。4.2 专利管理MyBatis 多条件查询与分页业务列表最常见的需求是“按专利类型申请日期范围状态过滤分页查询”——这四个条件一组合SQL 拼接就成了 MySQL 排序 之外最考验人的环节。先定义数据访问层接口public interface PatentDao { ListPatent selectByCondition(Param(type) Integer type, Param(status) Integer status, Param(startDate) String startDate, Param(endDate) String endDate, Param(offset) Integer offset, Param(limit) Integer limit); long countByCondition(Param(type) Integer type, Param(status) Integer status, Param(startDate) String startDate, Param(endDate) String endDate); }对应的 Mapper XMLselect idselectByCondition resultTypecom.ipr.entity.Patent SELECT id, patent_no AS patentNo, patent_name AS patentName, patent_type AS patentType, applicant, apply_date AS applyDate, status, annual_fee_due_date AS annualFeeDueDate FROM ipr_patent where if testtype ! null AND patent_type #{type} /if if teststatus ! null AND status #{status} /if if teststartDate ! null and startDate ! AND apply_date gt; #{startDate} /if if testendDate ! null and endDate ! AND apply_date lt; #{endDate} /if /where ORDER BY apply_date DESC, id DESC LIMIT #{offset}, #{limit} /select逻辑说明where标签会自动去掉第一个 AND这样即使所有条件为空SQL 也不会出现语法错误。日期比较用的是字符串类型因为 MySQL 的 DATE 列和 2024-01-01 字符串可以直接比较MyBatis 不用再做类型转换。ORDER BY 跟了两列apply_date 相同的情况下按 id 降序保证稳定性。LIMIT 的两个参数是 offset 和 limit分页查询的核心。配套的 count 查询别偷懒用 select * —— 大数据量下 count 只需要一行数字select * 会传输全部列浪费 IOselect idcountByCondition resultTypelong SELECT COUNT(*) FROM ipr_patent where if testtype ! nullAND patent_type #{type}/if if teststatus ! nullAND status #{status}/if if teststartDate ! null and startDate ! AND apply_date gt; #{startDate}/if if testendDate ! null and endDate ! AND apply_date lt; #{endDate}/if /where /select注意 count 统计的是过滤后的总行数不是当前页的行数否则前端分页组件算不出总页数。我这里刻意没引入 PageHelper 插件因为新手在没理解分页原理的情况下用 PageHelper 会经常遇到“count 查询被拦截出错”或者“插件版本和 mybatis-spring 冲突”的问题。SSM 场景手写 LIMIT 足够清晰等熟练了再上 PageHelper 才有价值。4.3 事务边界登记专利时同时写业务表和日志表新增专利这个动作牵涉两张表ipr_patent 和 ipr_process_log。如果业务表写成功了、日志表失败系统里就会出现一条没有流程记录的“幽灵专利”——这就是必须用事务处理的理由。Service public class PatentServiceImpl implements PatentService { Autowired private PatentDao patentDao; Autowired private ProcessLogDao processLogDao; Override Transactional(rollbackFor Exception.class) public void addPatent(Patent patent, ProcessLog log) { patentDao.insert(patent); processLogDao.insert(log); } }参数说明Transactional 是 SSM 常用注解 里事务控制的入口rollbackFor Exception.class 表示任何异常都回滚注意一定要这么写。Spring 默认只在 RuntimeException 时回滚而业务层最常见的 SQLException 都是受检异常不指定 rollbackFor 就会事务“成功提交”数据写到一半。另外 Transactional 只能作用于 public 方法且不能通过同一个类内部this.addPatent()形式调用必须走代理对象——这也是很多人配了事务却完全没生效的原因之一。MySQL 事务处理 这一块还要注意隔离级别这套系统用默认的 REPEATABLE_READ 就够。如果后面要做“不同部门同时审批同一个专利”的并发场景才需要考虑把读多写少的查询加上只读事务或者调整隔离级别到 READ_COMMITTED。5. MySQL 与 SSM 接入的十大翻车点从乱码到连接失败的血泪排查5.1 连接失败Unknown database 与 Access denied 是最常见两类现象Tomcat 启动后第一次请求数据库时报Unknown database ipr_system或Access denied for user rootlocalhost。原因前者是建库语句没执行或者连错了数据库名后者是密码不匹配。多数时候不是代码问题而是 MySQL 服务本身的状态问题。Windows 下最常见的情况是 MySQL 服务没有启动命令行执行net start mysql报“服务无法启动”这时先去检查 my.ini 里的 basedir 和 datadir 路径是否和实际安装目录一致mysql 安装配置教程 里的标准流程是先初始化 data 目录再启动服务这一步漏了服务永远起不来。解决先确认服务在线再确认密码最后确认当前连接的库名拼写。顺序不要反过来因为每次排查数据库问题都应该先看服务再查权限。5.2 连接超时wait_timeout 与连接池不匹配现象系统放一晚上第二天访问页面时卡住很久然后报Connection is not available, request timed out after 30000ms。原因MySQL 的 wait_timeout 默认 8 小时超过时间没有新请求的连接会被服务端断开而连接池里的对象还保持着“看起来有效”的旧连接一旦被拿到就是死连接。解决Druid 连接池配置里加连接保活和检测property nametestWhileIdle valuetrue / property namevalidationQuery valueSELECT 1 / property nametimeBetweenEvictionRunsMillis value60000 /这样连接池每 60 秒检测一次空闲连接无效即剔除。同时可以把 MySQL 的 wait_timeout 调到 28800 以上两条路一起走才不容易翻车。5.3 中文乱码改了数据库字符集仍然乱现象插入中文后查询出来是问号或者 JSP 页面显示乱码。原因三个环节中任何一环被跳过都会乱码——页面本身的编码、Tomcat 接收参数的编码、MySQL 连接和存储的编码。很多人只改了数据库的 character_set_database 就以为完事了。解决按顺序检查三处JSP 头部加% page contentTypetext/html;charsetUTF-8 languagejava %web.xml 加 CharacterEncodingFilter 且设为 forceEncoding trueJDBC URL 里带 characterEncodingutf8。填充完这三处绝大多数乱码都会消失。注意 utf8 和 utf8mb4 的连接参数写法相同但建表时用 utf8mb4 才是最终保险。5.4 SUM 函数返回 NULL统计报表显示空白现象专利年度统计页面某一年没有任何数据SUM 的结果不是 0 而是空白。原因MySQL 的聚合函数 SUM 在“无匹配行”时返回 NULLJava 端拿到 null 后 Integer 类型无法显示。解决SELECT COALESCE(SUM(CASE WHEN patent_type 1 THEN 1 ELSE 0 END), 0) AS invention_count FROM ipr_patent WHERE YEAR(apply_date) 2024;COALESCE 把 NULL 转成 0这是 mysql 函数大全 里用得最勤的一个做报表时几乎每个聚合字段都要套一层。5.5 INSERT 时忘记处理 NULL 和空字符串的区别现象新增专利时发明人字段留空数据库存的是 NULL 而页面展示成了“null”字符串。原因很多前端提交时会把空的 input 框提交为空字符串 而后端直接 set 进实体MyBatis 插入时没有把空字符串转成 NULL。解决在实体类的 setter 里做一层防御public void setInventor(String inventor) { this.inventor (inventor null || inventor.trim().isEmpty()) ? null : inventor.trim(); }这个习惯能省掉大量“为什么 null 变成了空字符串”的排查时间。同理日期字段不要允许前端传空字符串给 MySQL 的 DATE 类型会直接报 Incorrect date value。5.6 mysql 的 or 不能去重多条件同字段检索现象查专利类型为“发明”或“实用新型”的数据用 OR 拼接后 LIMIT 分页数据出现 10 条变成 12 条。原因OR 在多列上使用时MySQL 的优化器可能选择全表扫描或索引合并index_merge如果两条索引取出的主键有重复去重不彻底就会出现分页偏移。解决同字段多值用 IN不同字段的 OR 才保留-- 推荐 WHERE patent_type IN (1, 2) -- 不推荐 WHERE patent_type 1 OR patent_type 2IN 会走索引且返回结果天然去重性能比 OR 高一个量级。6. 部署到 Linux 服务器MySQL 安装、服务配置与上线前的必查清单6.1 Linux 下 MySQL 的安装与初始化CentOS 7 系开发时在 Windows 上跑 MySQL 没问题但项目一旦要演示或交付最好放到 Linux 上因为最终运行的服务器几乎都是 Linux。以 CentOS 7 MySQL 5.7.44 为参考常见做法是先卸载系统自带的 mariadb-libs然后用 rpm 方式安装。# 检查是否已存在 mariadb有则卸载 rpm -qa | grep mariadb rpm -e --nodeps mariadb-libs # 使用 rpm 安装 MySQL 5.7 rpm -ivh mysql-community-common-5.7.44-1.el7.x86_64.rpm rpm -ivh mysql-community-libs-5.7.44-1.el7.x86_64.rpm rpm -ivh mysql-community-client-5.7.44-1.el7.x86_64.rpm rpm -ivh mysql-community-server-5.7.44-1.el7.x86_64.rpm安装完成后首次启动前要初始化mysqld --initialize-insecure systemctl start mysqld systemctl enable mysqld注意mysqld --initialize-insecure 会让 root 的初始密码为空便于首次登录后再改密码而不是去翻 /var/log/mysqld.log 里的临时密码。rpm 方式安装的默认 datadir 是 /var/lib/mysql如果之前初始化失败过要清空这个目录再重来否则会报 “already exists” 错误。6.2 MySQL 5.7 与 8.0 的性能调优锁和日志的取舍部署完成后至少要确认两个参数innodb_buffer_pool_size 和 slow_query_log。前者建议设为物理内存的 50%-70%后者是 mysql 性能调优 的第一步——不打开慢查询日志你永远不知道哪些 SQL 在拖后腿。# 查看当前值 SHOW VARIABLES LIKE innodb_buffer_pool_size; SHOW VARIABLES LIKE slow_query_log; # 动态开启慢查询重启后失效需要写进 my.cnf SET GLOBAL slow_query_log ON; SET GLOBAL long_query_time 2;mysql 锁的分类 在这个场景里也应该实际感受一下InnoDB 默认行锁但是查询条件没有走索引时行锁会升级成全表扫描造成锁表。上线前排查慢 SQL 时用 EXPLAIN 看 type 字段如果是 ALL 而数据量又大必须加索引否则高峰期一个更新操作就可能把整张表锁住其他请求全部卡死。6.3 导出导入与备份策略上线前的后悔药最后一道工序是确认数据能安全迁移。导出用 mysqldump导入用 source这一步一定要在交付前做一次完整演练否则换了服务器数据起不来就是灾难现场。# 导出数据库包含建表和插入语句 mysqldump -uroot -p ipr_system ipr_backup.sql # 导入数据库 mysql -uroot -p ipr_backup.sql个人习惯是每天凌晨对 ipr_system 做一次全量导出保留最近 7 天的备份文件。这套系统数据量不大不需要 binlog 增量同步这种重型方案。另外 deliverable 里如果要求给一个 docker-compose 部署方式可以把 MySQL 5.7 和 Tomcat 8.5 各起一个容器但本地调试时我建议直接裸装等 Docker 容器网络问题折腾完黄花菜都凉了。docker 安装 mysql 失败 的常见原因基本都是数据卷映射权限不对有这时间不如先把裸环境跑通再考虑容器化。其实每次交付这类 SSM 管理系统我都发现真正阻碍项目跑起来的不是代码逻辑而是环境MySQL 版本不对、JDBC 驱动和数据库版本不匹配、字符集没统一。早年间我把一个项目从 Windows 搬到 Linux 时就因为 JDBC URL 里忘了加 useSSLfalse 而连不上数据库报错提示又极具迷惑性。后来养成的习惯是每接手一个新环境先写一个最小的 JDBC 连接测试类跑通再启动整个 Web 应用——这一招帮我避开了至少一半的部署问题。希望这些经验对你也有用。本文还有配套的精品资源点击获取