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
}
]
}
]
}