springboot+mybatis-plus演示做题功能后端实现
简介
本文讲解,如何使用springboot+mybatis-plus实现做题功能后端,文本最大的两点之处,演示如何通过后端代码实现多表查询,使得查询试卷的时候,会把对应的题目也查询过去,而且进行全部试卷的查询的时候,会更新试卷的分数。
数据库设计
数据库代码
-- ---------------------------- -- Table structure for exam -- ---------------------------- DROP TABLE IF EXISTS `exam`; CREATE TABLE `exam` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '主键,考试编号', `exam_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '考试名称', `start_time` datetime NOT NULL COMMENT '考试开始时间', `end_time` datetime NOT NULL COMMENT '考试结束时间', `total_score` int NOT NULL DEFAULT 0 COMMENT '总分数', `questions_list` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '题目编号集合', `exam_type` enum('月考','期中','期末','测验') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '月考' COMMENT '考试类型', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '考试表' ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of exam -- ---------------------------- INSERT INTO `exam` VALUES (1, '2021年6月份语文月考', '2021-06-15 08:00:00', '2021-06-15 10:00:00', 36, '1,2,3,4,5', '月考'); INSERT INTO `exam` VALUES (2, '2021年7月份物理小检测', '2021-07-10 14:00:00', '2021-07-10 14:30:00', 24, '7,9,11,13,15', '测验'); INSERT INTO `exam` VALUES (3, '2021年秋季学期期末考试', '2022-01-05 09:00:00', '2022-01-06 17:00:00', 38, '1,4,8,12,16', '期末'); -- ---------------------------- -- Table structure for question -- ---------------------------- DROP TABLE IF EXISTS `question`; CREATE TABLE `question` ( `id` int NOT NULL AUTO_INCREMENT, `content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '题目内容', `question_type` enum('填空题','判断题','简答题','选择题') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '选择题' COMMENT '题目类型', `option_a` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT 'A选项,仅适用于选择题', `option_b` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT 'B选项,仅适用于选择题', `option_c` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT 'C选项,仅适用于选择题', `option_d` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT 'D选项,仅适用于选择题', `answer` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '答案或解析', `score` int NOT NULL DEFAULT 0 COMMENT '分数', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 18 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '题目表' ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of question -- ---------------------------- INSERT INTO `question` VALUES (1, '世界上最高峰是哪座山?', '选择题', '珠穆朗玛峰', '乔戈里峰', '干城章嘉峰', '喜马拉雅山', 'A', 5); INSERT INTO `question` VALUES (2, '地球上有多少个大洲?', '简答题', NULL, NULL, NULL, NULL, '7个大洲', 10); INSERT INTO `question` VALUES (3, '月亮的表面看起来像什么?', '简答题', NULL, NULL, NULL, NULL, '一块巨大的石头', 8); INSERT INTO `question` VALUES (4, '甲骨文是我国古代哪个时期的文字?', '填空题', NULL, NULL, NULL, NULL, '商周时期', 7); INSERT INTO `question` VALUES (5, '收入超过年度设定值的纳税义务人称为什么?', '选择题', '自然人', '法人', '社会团体', '企业', 'D', 6); INSERT INTO `question` VALUES (6, '中国哪个省份是最大的投资来源地?', '填空题', NULL, NULL, NULL, NULL, '广东省', 7); INSERT INTO `question` VALUES (7, '铁氧体产生电磁吸声效应的主要原因是:', '选择题', '铁氧体的导磁率较高', '铁氧体抗腐蚀性能好', '铁氧体功能稳定且防潮湿', '铁氧体具备收音的功能', 'A', 4); INSERT INTO `question` VALUES (8, '以下哪个科学家是牛顿的老师?', '简答题', NULL, NULL, NULL, NULL, 'Isaac Barrow', 9); INSERT INTO `question` VALUES (9, '西安市区南郊外有一座世界文化遗产,它是我国唐朝的皇陵,请问这是哪座皇陵?', '选择题', '秦始皇陵', '汉景帝陵', '唐太宗陵', '唐玄宗陵', 'D', 5); INSERT INTO `question` VALUES (10, '西班牙语中1255这个数怎么读?', '填空题', NULL, NULL, NULL, NULL, 'mil doscientos cincuenta y cinco', 6); INSERT INTO `question` VALUES (11, '在现代交通工具中,离合器适用于哪些车辆?', '选择题', '汽车和摩托车', '电动车和自行车', '飞机和直升机', '火车和地铁', 'A', 4); INSERT INTO `question` VALUES (12, '在三大赛制的欧洲足球锦标赛中,每组小组赛有多少支球队?', '填空题', NULL, NULL, NULL, NULL, '4支球队', 8); INSERT INTO `question` VALUES (13, '雅各布圆是什么?', '选择题', '圆上所有点到某一点的距离相等的图形', '三条互相垂直的直线', '顶部半圆的椎形物体', '大半径和短半径不相等的图形', 'A', 6); INSERT INTO `question` VALUES (14, '人的肝在哪个器官的下方?', '填空题', NULL, NULL, NULL, NULL, '膈肌之上', 7); INSERT INTO `question` VALUES (15, '动画电影《寻梦环游记》的主人公米格尔的最想成为音乐家,但他出生于一个世代不准许做音乐家的家庭。请问该片讲述的背景地区是哪里?', '选择题', '墨西哥', '美国', '巴西', '加拿大', 'A', 5); INSERT INTO `question` VALUES (16, '「山东临沂市费县境内的著名景区,是集山水秀美、人文风景、历史文化、科学考察等为一体的国家级重点风景名胜区」。这个景区叫什么?', '简答题', NULL, NULL, NULL, NULL, '庙峪口', 9); INSERT INTO `question` VALUES (17, '下列哪个事件被誉为珍珠港事件之前打响了太平洋战争的第一枪?', '选择题', '华盛顿塔克特会议', '柏林德意志运动大会', '萨拉热窝事件', '北京事变', 'C', 6);
后端代码
项目结构
application.properties
# ????? spring.datasource.url=jdbc:mysql://localhost:3306/exam5?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai spring.datasource.username=root spring.datasource.password=123456 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver # ??????? server.port=8080 # MyBatis-Plus ?? mybatis-plus.mapper-locations=classpath:/mapper/*.xml mybatis-plus.type-aliases-package=com.example.demo.entity mybatis-plus.global-config.db-config.id-type=auto mybatis-plus.configuration.map-underscore-to-camel-case=true mybatis-plus.configuration.use-generated-keys=true mybatis-plus.configuration.map-enum-as-ordinal=false mybatis-plus.configuration.enum-handler=com.baomidou.mybatisplus.extension.handlers.MybatisEnumTypeHandler
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.11</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>examAndQuestion</artifactId> <version>0.0.1-SNAPSHOT</version> <name>examAndQuestion</name> <description>examAndQuestion</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.7.6</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> <version>2.5.6</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>5.6.3.Final</version> </dependency> <dependency> <groupId>javax.persistence</groupId> <artifactId>javax.persistence-api</artifactId> <version>2.2</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.3.0</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.3.0</version> </dependency> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.2</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.8.0</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.2</version> </dependency> <!-- 如果要使用 jjwt 的实现,还需要添加以下依赖 --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.11.2</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>0.11.2</version> <scope>runtime</scope> </dependency> <!-- Hibernate Validator --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.8.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>
Result
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor public class Result<T> { /** * 状态码,0表示成功,其他表示失败 */ private int code; /** * 提示信息 */ private String message; /** * 返回的数据 */ private T data; /** * 成功时的构造函数 * * @param data 数据 */ public Result(T data) { this.code = 200; this.message = "success"; this.data = data; } public Result(T data, Boolean success, String message) { if (success) { this.code = 200; this.message = "success"; } else { this.code = 405; this.message = message; } this.data = data; } /** * 失败时的构造函数 * * @param code 状态码 * @param message 提示信息 */ public Result(int code, String message) { this.code = code; this.message = message; this.data = null; } }
question表
Question
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; @Data @TableName(value = "question") public class Question { @TableId(type = IdType.AUTO) private Integer id; private String content; private String questionType; private String optionA; private String optionB; private String optionC; private String optionD; private String answer; private Integer score; }
QuestionMapper
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.examandquestion.Bean.Question; import org.apache.ibatis.annotations.Mapper; import org.springframework.stereotype.Repository; @Mapper public interface QuestionMapper extends BaseMapper<Question> { }
QuestionService
import com.baomidou.mybatisplus.extension.service.IService; import com.example.examandquestion.Bean.Question; public interface QuestionService extends IService<Question> { }
QuestionServiceImpl
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.examandquestion.Bean.Question; import com.example.examandquestion.Mapper.QuestionMapper; import com.example.examandquestion.Service.QuestionService; import org.springframework.stereotype.Service; @Service public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question> implements QuestionService { }
QuestionController
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.example.examandquestion.Bean.Question; import com.example.examandquestion.Bean.Result; import com.example.examandquestion.Service.QuestionService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping("/questions") public class QuestionController { @Autowired private QuestionService questionService; // 根据题目类型查询所有题目 @GetMapping public Result<List<Question>> getAllQuestions(@RequestParam(required = false) String type) { QueryWrapper<Question> wrapper = new QueryWrapper<>(); if (type != null) { wrapper.eq("question_type", type); } List<Question> questions = questionService.list(wrapper); return new Result<>(questions); } // 根据id查询题目 @GetMapping("/{id}") public Result<Question> getQuestionById(@PathVariable Integer id) { Question question = questionService.getById(id); if (question != null) { return new Result<>(question); } else { return new Result<>(null, false, "Question not found."); } } // 添加新的题目 @PostMapping public Result<Boolean> addQuestion(@RequestBody Question question) { boolean result = questionService.save(question); if (result) { return new Result<>(true); } else { return new Result<>(false, false, "Failed to add the question."); } } // 根据id更新题目内容 @PutMapping("/{id}") public Result<Boolean> updateQuestion(@PathVariable Integer id, @RequestBody Question question) { question.setId(id); boolean result = questionService.updateById(question); if (result) { return new Result<>(true); } else { return new Result<>(false, false, "Failed to update the question."); } } // 根据id删除题目 @DeleteMapping("/{id}") public Result<Boolean> deleteQuestion(@PathVariable Integer id) { boolean result = questionService.removeById(id); if (result) { return new Result<>(true); } else { return new Result<>(false, false, "Failed to delete the question."); } } }
Exan表
Exam
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import javax.validation.constraints.Min; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import java.util.Date; @Data @TableName("exam") public class Exam { @TableId(value = "id", type = IdType.AUTO) private Integer id; @NotBlank(message = "考试名称不能为空") private String examName; @NotNull(message = "考试开始时间不能为空") private Date startTime; @NotNull(message = "考试结束时间不能为空") private Date endTime; @NotNull(message = "总分数不能为空") @Min(value = 0, message = "总分数不能小于0") private Integer totalScore; @NotBlank(message = "题目编号集合不能为空") private String questionsList; private String examType; // 考试类型 }
ExamIncludeQuestion
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.util.ArrayList; /* * 我们获得试卷之后,试卷里面装着的是题目的序号,不是玩着的题目,这样前端还需要多处理,现在选择加一个封装类的方式 * 一键完成这个功能 里面包含对应的试卷和试卷对应的题目 * */ @Data @AllArgsConstructor @NoArgsConstructor public class ExamIncludeQuestion { private Exam exam; private ArrayList<Question> questionArrayList; }
ExamMapper
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.examandquestion.Bean.Exam; import org.apache.ibatis.annotations.Mapper; @Mapper public interface ExamMapper extends BaseMapper<Exam> { }
ExamService
import com.baomidou.mybatisplus.extension.service.IService; import com.example.examandquestion.Bean.Exam; import java.util.List; public interface ExamService extends IService<Exam> { }
ExamServiceImpl
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.examandquestion.Bean.Exam; import com.example.examandquestion.Mapper.ExamMapper; import com.example.examandquestion.Service.ExamService; import org.springframework.stereotype.Service; import java.util.List; @Service public class ExamServiceImpl extends ServiceImpl<ExamMapper, Exam> implements ExamService { }
Postman测试
这里就简单的测试一下,试卷的查询
Get请求 测试API:http://localhost:8080/exams
请求结果
{ "code": 200, "message": "success", "data": [ { "exam": { "id": 1, "examName": "2021年6月份语文月考", "startTime": "2021-06-15T00:00:00.000+00:00", "endTime": "2021-06-15T02:00:00.000+00:00", "totalScore": 36, "questionsList": "1,2,3,4,5", "examType": "月考" }, "questionArrayList": [ { "id": 1, "content": "世界上最高峰是哪座山?", "questionType": "选择题", "optionA": "珠穆朗玛峰", "optionB": "乔戈里峰", "optionC": "干城章嘉峰", "optionD": "喜马拉雅山", "answer": "A", "score": 5 }, { "id": 2, "content": "地球上有多少个大洲?", "questionType": "简答题", "optionA": null, "optionB": null, "optionC": null, "optionD": null, "answer": "7个大洲", "score": 10 }, { "id": 3, "content": "月亮的表面看起来像什么?", "questionType": "简答题", "optionA": null, "optionB": null, "optionC": null, "optionD": null, "answer": "一块巨大的石头", "score": 8 }, { "id": 4, "content": "甲骨文是我国古代哪个时期的文字?", "questionType": "填空题", "optionA": null, "optionB": null, "optionC": null, "optionD": null, "answer": "商周时期", "score": 7 }, { "id": 5, "content": "收入超过年度设定值的纳税义务人称为什么?", "questionType": "选择题", "optionA": "自然人", "optionB": "法人", "optionC": "社会团体", "optionD": "企业", "answer": "D", "score": 6 } ] }, { "exam": { "id": 2, "examName": "2021年7月份物理小检测", "startTime": "2021-07-10T06:00:00.000+00:00", "endTime": "2021-07-10T06:30:00.000+00:00", "totalScore": 24, "questionsList": "7,9,11,13,15", "examType": "测验" }, "questionArrayList": [ { "id": 7, "content": "铁氧体产生电磁吸声效应的主要原因是:", "questionType": "选择题", "optionA": "铁氧体的导磁率较高", "optionB": "铁氧体抗腐蚀性能好", "optionC": "铁氧体功能稳定且防潮湿", "optionD": "铁氧体具备收音的功能", "answer": "A", "score": 4 }, { "id": 9, "content": "西安市区南郊外有一座世界文化遗产,它是我国唐朝的皇陵,请问这是哪座皇陵?", "questionType": "选择题", "optionA": "秦始皇陵", "optionB": "汉景帝陵", "optionC": "唐太宗陵", "optionD": "唐玄宗陵", "answer": "D", "score": 5 }, { "id": 11, "content": "在现代交通工具中,离合器适用于哪些车辆?", "questionType": "选择题", "optionA": "汽车和摩托车", "optionB": "电动车和自行车", "optionC": "飞机和直升机", "optionD": "火车和地铁", "answer": "A", "score": 4 }, { "id": 13, "content": "雅各布圆是什么?", "questionType": "选择题", "optionA": "圆上所有点到某一点的距离相等的图形", "optionB": "三条互相垂直的直线", "optionC": "顶部半圆的椎形物体", "optionD": "大半径和短半径不相等的图形", "answer": "A", "score": 6 }, { "id": 15, "content": "动画电影《寻梦环游记》的主人公米格尔的最想成为音乐家,但他出生于一个世代不准许做音乐家的家庭。请问该片讲述的背景地区是哪里?", "questionType": "选择题", "optionA": "墨西哥", "optionB": "美国", "optionC": "巴西", "optionD": "加拿大", "answer": "A", "score": 5 } ] }, { "exam": { "id": 3, "examName": "2021年秋季学期期末考试", "startTime": "2022-01-05T01:00:00.000+00:00", "endTime": "2022-01-06T09:00:00.000+00:00", "totalScore": 38, "questionsList": "1,4,8,12,16", "examType": "期末" }, "questionArrayList": [ { "id": 1, "content": "世界上最高峰是哪座山?", "questionType": "选择题", "optionA": "珠穆朗玛峰", "optionB": "乔戈里峰", "optionC": "干城章嘉峰", "optionD": "喜马拉雅山", "answer": "A", "score": 5 }, { "id": 4, "content": "甲骨文是我国古代哪个时期的文字?", "questionType": "填空题", "optionA": null, "optionB": null, "optionC": null, "optionD": null, "answer": "商周时期", "score": 7 }, { "id": 8, "content": "以下哪个科学家是牛顿的老师?", "questionType": "简答题", "optionA": null, "optionB": null, "optionC": null, "optionD": null, "answer": "Isaac Barrow", "score": 9 }, { "id": 12, "content": "在三大赛制的欧洲足球锦标赛中,每组小组赛有多少支球队?", "questionType": "填空题", "optionA": null, "optionB": null, "optionC": null, "optionD": null, "answer": "4支球队", "score": 8 }, { "id": 16, "content": "「山东临沂市费县境内的著名景区,是集山水秀美、人文风景、历史文化、科学考察等为一体的国家级重点风景名胜区」。这个景区叫什么?", "questionType": "简答题", "optionA": null, "optionB": null, "optionC": null, "optionD": null, "answer": "庙峪口", "score": 9 } ] } ] }