ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SpringBoot+MyBatis打造保险合同管理系统:从数据库设计到部署全解析

SpringBoot+MyBatis打造保险合同管理系统:从数据库设计到部署全解析 简介一套面向Java毕业设计或课程设计的保险合同管理系统基于SpringBootMyBatisMavenMySQL构建前端采用Vue与ElementUI覆盖用户登录注册、客户管理、合同签订与文件下载、公告留言、统计报表等核心模块区分管理员与普通用户两种角色能完整体现从权限控制到业务流转的管理闭环。压缩包共727个文件约15.66MB源码结构采用前后端分离思路java目录承载后端业务vue/js/css等组成前端页面sql脚本一键还原MySQL库bat脚本辅助Windows环境下的构建与启动同时附带任务书和毕业论文文档可直接导入IntelliJ IDEA或Eclipse运行调试便于二次开发。已有65人学习/浏览。对于需要参考合同电子化管理、统计可视化以及快速交付毕设演示的读者这套资源免去环境摸索的麻烦能直接对照源码理解模块关系与关键实现。1. 当保险合同还在用Excel登记这套系统解决了什么做过保险业务系统的人都知道保险合同的管理难点不在“增删改查”而在“状态的持续跟踪”一份合同从投保、核保、承保到续保、理赔、退保中间涉及缴费记录、被保险人变更、受益人等一连串关联数据。用Excel登记时合同状态靠人工维护到期提醒靠日历理赔记录靠翻聊天记录数据一多就是对账和审计的灾难。本文要拆的这套基于 SpringBoot MyBatis Maven MySQL 的保险合同管理系统正是把“合同生命周期”从散落的表格收拢到一个可查询、可回溯的Web应用里。它适合两类人一是正在选毕设题目的Java学生想用一套覆盖前后端、数据库、打包部署全流程的项目交差二是刚入行、想了解SpringBootMyBatis这套组合在真实业务里怎么落地的开发。下文从技术选型讲起给出可直接复用的表结构、核心代码和排错经验。2. SpringBoot MyBatis Maven MySQL这套组合为什么是毕设和中小项目的首选2.1 四个组件各管一段职责边界先划清SpringBoot、MyBatis、Maven、MySQL这四个词经常一起出现但很多人只会在脚手架里把它们拼起来说不清分工。以这套合同管理系统为例职责划分是这样的SpringBoot负责“编排”接收HTTP请求、做参数校验、事务控制、把异常翻译成可读的报错。它内置Tomcat解决了传统SSH项目里配置一堆XML才能启动的问题。MyBatis负责“SQL”它是一个半自动ORM框架你写SQL它把结果集映射成Java对象。相比JPA的自动建表MyBatis在复杂查询比如合同和缴费记录的多表关联上更可控。Maven负责“项目生命周期”下载依赖、编译、测试、打包全是它管。没有Maven你要手动下载Spring的jar包再一个个扔进lib目录。MySQL负责“最终落盘”合同数据、用户数据、操作日志都存在这里。这套组合对毕设项目的核心竞争力在于它同时覆盖了“框架应用能力”和“SQL功底”两个考核点答辩时既能谈IOC/AOP又能讲索引和连表查询。2.2 初始化项目时的版本陷阱springboot版本太高会怎样这里先踩一个最常见的坑springboot版本太高导致项目起不来。很多新手去start.spring.io拉最新版SpringBoot比如3.x然后发现mybatis-spring-boot-starter版本不兼容或者JDK版本对不上。我建议毕设项目做成下面这套版本组合稳定且资料多组件推荐版本说明JDK1.8兼容性最好答辩机器上没装JDK17也能跑SpringBoot2.7.x最后一个默认支持JDK8的常规大版本mybatis-spring-boot-starter2.3.x配合SpringBoot 2.xmysql-connector-java8.0.3x使用com.mysql.cj.jdbc.Driver驱动Maven3.8.xIDEA自带的也行但建议命令行也装一个2.2.1 用IDEA创建SpringBoot项目时的关键勾选项创建项目时不要全默认。Group填com.example或com.cmsArtifact填insurance-contract。依赖勾选Spring Web、MySQL DriverMyBatis Framework如果IDEA版本支持可以直接勾不支持就手动加依赖。打包方式选JarJava版本选8。2.3 pom.xml依赖声明与Maven仓库镜像生成的项目里pom.xml是Maven的核心。一个能跑通合同管理系统的pom.xml关键部分长这样parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version relativePath/ /parent dependencies !-- Web层依赖SpringMVC 内置Tomcat -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- MyBatis和SpringBoot的桥接依赖 -- dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version2.3.1/version /dependency !-- MySQL驱动 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency !-- Lombok减少getter/setter代码量 -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies这段配置里有三个参数值得展开说spring-boot-starter-parent它锁定了SpringBoot各依赖的版本号所以你不需要给mysql-connector-java写版本号。mybatis-spring-boot-starter必须写版本号因为它不在SpringBoot父工程的版本管理范围里。scoperuntime表示MySQL驱动只在运行时需要编译期不参与。但有了依赖声明还不够国内网络下载Maven依赖经常超时。解决办法是在~/.m2/settings.xml里配置maven镜像mirror idaliyun/id mirrorOfcentral/mirrorOf urlhttps://maven.aliyun.com/repository/public/url /mirrormirrorOf填central表示只接管中央仓库不影响你自己公司内网私服的正常使用。settings.xml配好后不需要重启IDEA点一下Maven面板的刷新按钮即可。3. 数据库设计与MyBatis映射保险合同领域怎么把ER图变成可查询的SQL3.1 从业务对象到表合同、客户、缴费记录的关系梳理合同管理系统的数据模型围绕“一张合同自出生到终止的全部痕迹”来设计。最核心的是这三张表contract保存合同的主键、编号、投保人证件号、保额、合同状态等属于合同自身的信息。customer客户主档保存客户姓名、身份证、手机号。之所以单独拆表是因为一个客户可能买多份保险不拆的话客户信息会冗余。payment_record缴费记录表。合同每期缴费都会在此新增一条记录同时更新contract表的last_pay_date和next_pay_date。这三张表的关联逻辑是contract表的customer_id指向customer表的主键payment_record表的contract_id指向contract表的主键。这是一种典型的“主表-从表”结构。3.2 核心建表SQL与索引设计的关键取舍下面是contract表的精简版建表SQLCREATE TABLE contract ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 主键ID, contract_no VARCHAR(32) NOT NULL COMMENT 合同编号全局唯一, customer_id BIGINT NOT NULL COMMENT 投保人客户ID, product_name VARCHAR(64) NOT NULL COMMENT 保险产品名称, amount DECIMAL(12, 2) NOT NULL COMMENT 保额, premium DECIMAL(10, 2) NOT NULL COMMENT 每期保费, pay_period VARCHAR(16) DEFAULT YEAR COMMENT 缴费周期YEAR/MONTH/ONCE, status TINYINT NOT NULL DEFAULT 0 COMMENT 合同状态0待核保 1有效 2已终止 3已理赔, effective_date DATE NULL COMMENT 生效日期, expire_date DATE NULL COMMENT 到期日期, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_contract_no (contract_no), KEY idx_customer_id (customer_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT保险合同主表;这段SQL里有三个设计细节值得说清楚status用TINYINT而不是VARCHAR是因为合同状态是一个有限的枚举集合数字比较比字符串比较更快而且后端可以用常量类统一管理状态码。contract_no必须建唯一索引这是业务上的强约束——同一份合同编号不能出现两次。唯一索引在这里同时起到了“约束”和“查询加速”的双重作用。customer_id建普通索引就可以了因为一个客户可以有多条合同customer_id不唯一。但这个字段会频繁出现在WHERE条件里没有索引就会全表扫描。payment_record表的建表SQL里有一个容易被忽略的点缴费日期字段pay_date要建索引因为“查某个时间段内到期的合同”是这类系统最常见的报表需求CREATE TABLE payment_record ( id BIGINT NOT NULL AUTO_INCREMENT, contract_id BIGINT NOT NULL COMMENT 关联合同表ID, pay_no VARCHAR(32) NOT NULL COMMENT 缴费单号, pay_date DATE NOT NULL COMMENT 实缴日期, amount DECIMAL(10, 2) NOT NULL COMMENT 实缴金额, PRIMARY KEY (id), UNIQUE KEY uk_pay_no (pay_no), KEY idx_contract_paydate (contract_id, pay_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT缴费记录表;这里的idx_contract_paydate是联合索引列顺序是contract_id然后pay_date。联合索引要遵守“最左前缀原则”查询条件带contract_id时索引生效只带pay_date时不走这个索引。所以如果系统里有“按时间范围统计所有合同的缴费记录”这种查询还需要单独给pay_date建一个单列索引。3.3 MyBatis的resultMap与实体类映射细节表结构建好后要回答的一个问题是MyBatis怎么把payment_record表里查出来的行变成Java对象的。这就涉及resultMap。以下是ContractMapper.xml里定义的一对多关系映射resultMap idContractWithPaymentsMap typecom.cms.entity.Contract id propertyid columnid / result propertycontractNo columncontract_no / result propertystatus columnstatus / result propertyamount columnamount / collection propertypaymentRecords ofTypecom.cms.entity.PaymentRecord id propertyid columnpay_id / result propertypayNo columnpay_no / result propertypayDate columnpay_date / result propertyamount columnpay_amount / /collection /resultMap使用collection标签时需要注意字段别名。如果payment_record表和contract表都有amount字段MyBatis在做自动映射时后面的字段会覆盖前面的值。所以上面的SQL里缴费金额必须起别名pay_amount并在resultMap里用columnpay_amount对应。这一点是mybatis新手最容易踩的坑之一。另外Contract实体类的paymentRecords字段要写成private ListPaymentRecord paymentRecords;不加List的话collection映射会直接报TypeException。4. 核心功能落地保单查询到续保提醒的全链路编码4.1 分层架构的包结构与职责划分后端代码建议按controller、service、mapper、entity、dto这五个包来组织。很多学生喜欢把业务逻辑全写在controller里图省事但一旦出现事务比如同时插入缴费记录和更新合同状态没有service层的包就会力不从心。一个标准的包结构长这样com.cms ├── controller // 接收参数返回JSON ├── service // 业务逻辑事务边界 ├── mapper // MyBatis数据访问接口 ├── entity // 对应数据表的实体类 ├── dto // 接口入参和出参的对象封装 └── common // 统一返回结果、异常处理、常量类common包里的R类统一返回体是这套系统的门面它保证前端拿到的JSON结构永远一致public class RT { private Integer code; // 0表示成功非0表示业务异常 private String msg; // 提示信息 private T data; // 业务数据 public static T RT ok(T data) { RT r new R(); r.setCode(0); r.setMsg(success); r.setData(data); return r; } public static T RT fail(String msg) { RT r new R(); r.setCode(-1); r.setMsg(msg); return r; } }这里用泛型T让R可以承载任何类型的数据。列表查询返回RListContract导出PDF返回RString文件路径结构统一后前端axios封装的拦截器只需要判断code是否为0即可不用每个接口单独写错误处理。4.2 保单列表查询的Controller-Service-Mapper全链路以“按合同编号和状态分页查询保单列表”这个最常见的功能为例从Controller到Mapper逐层写。Controller层RestController RequestMapping(/api/contract) public class ContractController { Autowired private ContractService contractService; GetMapping(/page) public RPageResultContractVO page( RequestParam(defaultValue 1) int pageNum, RequestParam(defaultValue 10) int pageSize, RequestParam(required false) String contractNo, RequestParam(required false) Integer status) { ContractQuery query new ContractQuery(); query.setPageNum(pageNum); query.setPageSize(pageSize); query.setContractNo(contractNo); query.setStatus(status); return R.ok(contractService.pageQuery(query)); } }所有HTTP参数只在Controller层出现RequestParam把URL里的参数绑定到方法变量上。defaultValue 1是安全的兜底防止前端没传时MyBatis分页插件收到一个null导致异常。Service层Service public class ContractService { Autowired private ContractMapper contractMapper; public PageResultContractVO pageQuery(ContractQuery query) { PageContractVO page new Page(query.getPageNum(), query.getPageSize()); // PageHelper的静态方法紧跟着的查询会自动被分页 PageContractVO result contractMapper.pageQuery(page, query); PageResultContractVO pageResult new PageResult(); pageResult.setTotal(result.getTotal()); pageResult.setList(result.getRecords()); return pageResult; } }Service层的另一个职责是处理Maven依赖里没有的PageResult和Page对象。这里用了PageHelper分页插件它的原理坑和用法在第4.3节细说。Mapper层public interface ContractMapper { // 分页查询SQL写在XML文件里 ListContractVO pageQuery(PageContractVO page, Param(query) ContractQuery query); }Mapper接口只定义方法签名SQL写在同名的XML文件中。Param注解必须加否则XML里#{query.contractNo}取不到值。4.3 MyBatis动态SQL解决多条件检索的N1问题Mapper XML里用动态SQL处理“有condition才拼where条件”的场景select idpageQuery resultTypecom.cms.dto.ContractVO SELECT c.id, c.contract_no, c.amount, c.status, p.name AS customer_name FROM contract c LEFT JOIN customer p ON c.customer_id p.id where if testquery.contractNo ! null and query.contractNo ! AND c.contract_no LIKE CONCAT(%, #{query.contractNo}, %) /if if testquery.status ! null AND c.status #{query.status} /if /where ORDER BY c.create_time DESC /select这里有两个要注意的细节where标签会自动去掉开头的AND所以不需要在if标签里手写WHERE 11这种难看的写法。LIKE CONCAT(%, #{xxx}, %)不要写LIKE %${query.contractNo}%${}是字符串拼接会有SQL注入风险而#{}是预编译占位符。查询状态为1的合同前端传status1XML里#{query.status}比较的是Integer不会有mybatis 单个数字字符比较时“数字被当作字符”导致匹配不到的问题。如果用了String类型的1去和数据库的TINYINT比较MySQL会做隐式类型转换仍然能查到但走不上索引。分页逻辑上PageHelper的坑也要提一下它生效的前提是“PageHelper.startPage()必须紧跟第一个查询”。如果在startPage()和mapper.pageQuery()之间插了其他数据库操作分页会作用到错误的SQL上。正确做法是把startPage放在Service层紧接着Mapper调用的地方或者在API上直接用PageContractVO作为Mapper方法的第一参数。5. Maven灵活打包与部署检查把毕设交付物变成可运行的代码5.1 Maven多环境配置与打包瘦身开发时连接本地MySQL答辩部署时换成云服务器MySQL数据库地址、账号密码全不同。如果每次手动改application.yml再打包容易改错。Maven的profile机制可以把环境抽离成独立配置文件。在pom.xml里定义两个profileprofiles profile iddev/id activation activeByDefaulttrue/activeByDefault /activation properties activatedPropertiesdev/activatedProperties /properties /profile profile idprod/id properties activatedPropertiesprod/activatedProperties /properties /profile /profiles在application.yml里通过占位符引用这个参数spring: profiles: active: activatedProperties然后在application-dev.yml和application-prod.yml里分别配置不同的数据库连接串、server.port和日志级别。用activatedProperties是maven的resource filtering机制它会读取当前激活的profile的properties值并替换到yml文件里。生产环境打包时执行mvn clean package -DskipTests -Pprodclean删除上次的target目录-DskipTests跳过单元测试因为很多打包机器上没有MySQL环境测试跑不过-Pprod激活生产环境配置。打成jar之后用java -jar target/insurance-contract-0.0.1-SNAPSHOT.jar就能启动。如果想调整并发参数在后面加--server.tomcat.threads.max200即可这个优先级高于配置文件。5.2 交付前必查的三个验证点拿到手一套完整系统不急着点功能先做三个静态检查和一个运行时检查。第一个静态检查是确认Maven仓库完整mvn dependency:tree执行不报错说明所有依赖都拉下来了搬到没有联网的答辩机器上可以用mvn package离线段打包。第二个是确认MySQL连接串没有写死IP项目里的jdbc:mysql://地址应该用变量或profile区分否则换一台机器跑就得改代码重新编译。第三个是确认数据库初始化脚本的字符集建表SQL开头必须有CREATE DATABASE IF NOT EXISTS insurance DEFAULT CHARSET utf8mb4不指定utf8mb4会导致中文乱码答辩时被评委当场指出很尴尬。运行时检查要重点看启动日志里有没有下面这行Tomcat started on port(s): 8080 (http)另外确认mapper.xml有没有被扫描到否则MyBatis会在请求时报Invalid bound statement (not found)。检查扫描配置是否正确入口类上要加MapperScan(com.cms.mapper)这个注解是mybatis-spring-boot-starter提供的配置之前记得确认依赖里确实引入了这个starter而不是只引入了mybatis核心包。最后的验证手段是对接mybatis log plugin这类IDEA插件打开SQL日志在页面点一次查询看控制台打印的SQL和参数是否与预期一致。确认单表查询、连表查询、分页查询三条SQL都走对了这套系统的核心部分基本就算验收通过了。本文还有配套的精品资源点击获取
返回列表