SpringMvc+myBatis 解决HashMap无法放回null

本文介绍如何解决MyBatis配置过程中遇到的依赖缺失问题,包括添加cglib和jdk.tools依赖,以及mybatis-config.xml的具体配置方法。

       一查网上一大丢,但没有一个具体的。最终还是自己解决的,希望能帮到大家。

我的事mave工程,mybatis3.3。

maven  pom.xml 加入

           <dependency>
              <groupId>cglib</groupId>
              <artifactId>cglib-nodep</artifactId>
              <version>2.2</version>
           </dependency>
引入完这个包要是会报jdk.tools.1.7(Missing)

再加一个

           <dependency> 
             <groupId>jdk.tools</groupId> 
             <artifactId>jdk.tools</artifactId> 
             <version>1.7</version> 
         </dependency>
如果还是报tools的问题改:
        <dependency>  
            <groupId>jdk.tools</groupId>  
            <artifactId>jdk.tools</artifactId>  
            <version>1.7</version>  
            <scope>system</scope>  
               <systemPath>${JAVA_HOME}/lib/tools.jar</systemPath>  
        </dependency>


新建mybatis-config.xml
<?xmlversion="1.0"encoding="UTF-8"?> 
<!DOCTYPEconfiguration 
 PUBLIC"-//mybatis.org//DTD Config 3.0//EN" 
 "http://mybatis.org/dtd/mybatis-3-config.dtd"> 
<configuration> 
     <settings><settingname="callSettersOnNulls"value="true"/></settings>
</configuration>

最后再applicationDao.xml 找到sqlSessionFactory 引入
<propertyname="configLocation"value="classpath:spring/mybatis-config.xml"/>

如图:


问题解决




我们将使用 **Spring Boot + MyBatis(或 MyBatis-Plus) + Vue3 前后端分离架构** 实现一个功能完整的课程评价系统,包含: 1. 用户管理 2. 课程管理 3. 课程分类管理 4. 标签管理 5. 评价管理 --- ## ✅ 技术栈 | 层级 | 技术 | |------|------| | 后端框架 | Spring Boot 2.7+ | | ORM 框架 | MyBatis-Plus(简化 CRUD) | | 数据库 | MySQL 8.0 | | 接口文档 | Swagger / Knife4j | | 权限控制 | JWT + Spring Security(基础权限) | | 前端框架 | Vue3 + Element Plus + Axios | | 构建工具 | Maven + Webpack/Vite | --- ## 🗂️ 项目结构概览 ``` course-review-system/ ├── backend/ # Spring Boot 项目 │ ├── src/main/java/com/example/course/ │ │ ├── controller/ # REST 控制器 │ │ ├── service/ # 业务逻辑 │ │ ├── mapper/ # MyBatis Mapper │ │ ├── entity/ # 实体类 │ │ ├── dto/ # 数据传输对象 │ │ ├── config/ # 配置类(JWT、跨域等) │ │ └── CourseApplication.java │ └── resources/ │ ├── application.yml │ └── mapper/*.xml # XML 映射文件(可选) │ ├── frontend/ # Vue3 项目 │ ├── src/ │ │ ├── views/ # 页面组件 │ │ ├── api/ # API 调用封装 │ │ ├── router/index.js # 路由配置 │ │ └── App.vue, main.js │ └── index.html │ └── database.sql # 数据库建表语句 ``` --- ## 🛠️ 第一步:数据库设计 ```sql -- 数据库:course_review CREATE DATABASE IF NOT EXISTS course_review DEFAULT CHARSET utf8mb4; USE course_review; -- 用户表 CREATE TABLE users ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) UNIQUE NOT NULL, password VARCHAR(100) NOT NULL, role ENUM('student', 'teacher', 'admin') NOT NULL DEFAULT 'student', created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 课程分类表 CREATE TABLE categories ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL UNIQUE, description TEXT ); -- 标签表 CREATE TABLE tags ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(30) NOT NULL UNIQUE ); -- 课程表 CREATE TABLE courses ( id BIGINT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(100) NOT NULL, teacher VARCHAR(50) NOT NULL, category_id BIGINT, description TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (category_id) REFERENCES categories(id) ); -- 课程与标签关联表(多对多) CREATE TABLE course_tag ( course_id BIGINT, tag_id BIGINT, PRIMARY KEY (course_id, tag_id), FOREIGN KEY (course_id) REFERENCES courses(id) ON DELETE CASCADE, FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE ); -- 评价表 CREATE TABLE reviews ( id BIGINT PRIMARY KEY AUTO_INCREMENT, course_id BIGINT NOT NULL, user_id BIGINT NOT NULL, rating INT NOT NULL CHECK(rating BETWEEN 1 AND 5), comment TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (course_id) REFERENCES courses(id) ON DELETE CASCADE, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, UNIQUE KEY unique_user_course (course_id, user_id) -- 防止重复评价 ); ``` --- ## 🐢 后端实现(Spring Boot + MyBatis-Plus) ### 1. `pom.xml`(关键依赖) ```xml <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3.1</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>4.4.0</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-boot-starter</artifactId> <version>3.0.0</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies> ``` --- ### 2. `application.yml` ```yaml server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/course_review?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8 username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus: configuration: map-underscore-to-camel-case: true mapper-locations: classpath:mapper/*.xml logging: level: com.example.course.mapper: debug ``` --- ### 3. 实体类(Entity) #### `User.java` ```java @Data @TableName("users") public class User { private Long id; private String username; private String password; private String role; // student, teacher, admin private LocalDateTime createdAt; } ``` #### `Category.java` ```java @Data @TableName("categories") public class Category { private Long id; private String name; private String description; } ``` #### `Tag.java` ```java @Data @TableName("tags") public class Tag { private Long id; private String name; } ``` #### `Course.java` ```java @Data @TableName("courses") public class Course { private Long id; private String title; private String teacher; private Long categoryId; private String description; private LocalDateTime createdAt; } ``` #### `Review.java` ```java @Data @TableName("reviews") public class Review { private Long id; private Long courseId; private Long userId; private Integer rating; private String comment; private LocalDateTime createdAt; } ``` --- ### 4. Mapper 接口(MyBatis-Plus) ```java @Mapper public interface UserMapper extends BaseMapper<User> {} @Mapper public interface CategoryMapper extends BaseMapper<Category> {} @Mapper public interface TagMapper extends BaseMapper<Tag> {} @Mapper public interface CourseMapper extends BaseMapper<Course> {} @Mapper public interface ReviewMapper extends BaseMapper<Review> {} ``` --- ### 5. Service 层示例(以 CourseService 为例) ```java @Service public class CourseService { @Autowired private CourseMapper courseMapper; @Autowired private CategoryMapper categoryMapper; public List<Map<String, Object>> getAllCoursesWithDetails() { return courseMapper.selectList(null).stream().map(course -> { Map<String, Object> map = new HashMap<>(); map.put("id", course.getId()); map.put("title", course.getTitle()); map.put("teacher", course.getTeacher()); map.put("description", course.getDescription()); Category category = categoryMapper.selectById(course.getCategoryId()); map.put("categoryName", category != null ? category.getName() : "未知"); return map; }).collect(Collectors.toList()); } public Course addCourse(Course course) { courseMapper.insert(course); return course; } } ``` --- ### 6. Controller 示例(支持 RESTful API) #### `UserController.java` ```java @RestController @RequestMapping("/api/users") @CrossOrigin public class UserController { @Autowired private UserMapper userMapper; // 登录接口(简化版,返回 JWT 可扩展) @PostMapping("/login") public Result login(@RequestBody Map<String, String> body) { String username = body.get("username"); String password = body.get("password"); QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("username", username).eq("password", password); // 生产环境应加密密码 User user = userMapper.selectOne(wrapper); if (user != null) { Map<String, Object> data = new HashMap<>(); data.put("id", user.getId()); data.put("username", user.getUsername()); data.put("role", user.getRole()); return Result.success(data); } else { return Result.fail("用户名或密码错误"); } } @GetMapping public Result listUsers() { return Result.success(userMapper.selectList(null)); } } ``` #### `CategoryController.java` ```java @RestController @RequestMapping("/api/categories") @CrossOrigin public class CategoryController { @Autowired private CategoryMapper categoryMapper; @GetMapping public Result list() { return Result.success(categoryMapper.selectList(null)); } @PostMapping public Result add(@RequestBody Category category) { categoryMapper.insert(category); return Result.success(category); } @DeleteMapping("/{id}") public Result delete(@PathVariable Long id) { categoryMapper.deleteById(id); return Result.success(); } } ``` #### `CourseController.java` ```java @RestController @RequestMapping("/api/courses") @CrossOrigin public class CourseController { @Autowired private CourseService courseService; @GetMapping public Result list() { return Result.success(courseService.getAllCoursesWithDetails()); } @PostMapping public Result add(@RequestBody Course course) { return Result.success(courseService.addCourse(course)); } } ``` #### `ReviewController.java` ```java @RestController @RequestMapping("/api/reviews") @CrossOrigin public class ReviewController { @Autowired private ReviewMapper reviewMapper; @GetMapping("/course/{courseId}") public Result getByCourse(@PathVariable Long courseId) { QueryWrapper<Review> wrapper = new QueryWrapper<>(); wrapper.eq("course_id", courseId); List<Review> reviews = reviewMapper.selectList(wrapper); return Result.success(reviews); } @PostMapping public Result addReview(@RequestBody Review review) { // 检查是否已评价 QueryWrapper<Review> wrapper = new QueryWrapper<>(); wrapper.eq("course_id", review.getCourseId()) .eq("user_id", review.getUserId()); if (reviewMapper.selectCount(wrapper) > 0) { return Result.fail("您已评价过该课程!"); } review.setCreatedAt(LocalDateTime.now()); reviewMapper.insert(review); return Result.success(review); } } ``` --- ### 7. 统一返回结果封装 `Result.java` ```java @Data @AllArgsConstructor @NoArgsConstructor public class Result { private Boolean success; private String message; private Object data; public static Result success() { return new Result(true, "操作成功", null); } public static Result success(Object data) { return new Result(true, "操作成功", data); } public static Result fail(String msg) { return new Result(false, msg, null); } } ``` --- ## 🖼️ 前端部分(Vue3 + Element Plus) ### 安装前端依赖 ```bash npm create vue@latest frontend cd frontend npm install axios element-plus ``` --- ### `src/api/api.js` ```js import axios from 'axios'; const API_BASE = 'http://localhost:8080/api'; export const login = (data) => axios.post(`${API_BASE}/users/login`, data); export const getCourses = () => axios.get(`${API_BASE}/courses`); export const getCategories = () => axios.get(`${API_BASE}/categories`); export const addCourse = (data) => axios.post(`${API_BASE}/courses`, data); export const getReviewsByCourse = (courseId) => axios.get(`${API_BASE}/reviews/course/${courseId}`); export const submitReview = (data) => axios.post(`${API_BASE}/reviews`, data); ``` --- ### `src/views/CourseList.vue` ```vue <template> <div> <h2>课程列表</h2> <el-table :data="courses" style="width: 100%"> <el-table-column prop="title" label="课程名称"></el-table-column> <el-table-column prop="teacher" label="讲师"></el-table-column> <el-table-column prop="categoryName" label="分类"></el-table-column> <el-table-column label="操作"> <template #default="scope"> <el-button size="small" @click="viewReviews(scope.row.id)">查看评价</el-button> <el-button size="small" type="primary" @click="openReviewDialog(scope.row.id)">评价</el-button> </template> </el-table-column> </el-table> <!-- 评价弹窗 --> <el-dialog v-model="reviewVisible" title="提交评价" width="500px"> <el-form @submit.prevent="submitReview"> <el-form-item label="评分"> <el-rate v-model="rating" :max="5" /> </el-form-item> <el-form-item label="评论"> <el-input v-model="comment" type="textarea" /> </el-form-item> <el-button type="primary" native-type="submit">提交</el-button> </el-form> </el-dialog> </div> </template> <script setup> import { ref, onMounted } from 'vue'; import { getCourses, getReviewsByCourse, submitReview } from '../api/api'; const courses = ref([]); const reviewVisible = ref(false); const currentCourseId = ref(null); const rating = ref(5); const comment = ref(''); onMounted(async () => { const res = await getCourses(); courses.value = res.data.data; }); const viewReviews = async (id) => { const res = await getReviewsByCourse(id); alert(`该课程共 ${res.data.data.length} 条评价:\n${JSON.stringify(res.data.data)}`); }; const openReviewDialog = (id) => { currentCourseId.value = id; reviewVisible.value = true; }; const submitReview = async () => { try { await submitReview({ courseId: currentCourseId.value, userId: 1, // 模拟用户登录 ID rating: rating.value, comment: comment.value }); ElMessage.success("评价成功!"); reviewVisible.value = false; } catch (err) { ElMessage.error(err.response?.data?.message || "提交失败"); } }; </script> ``` --- ### 路由注册 `router/index.js` ```js import { createRouter, createWebHistory } from 'vue-router'; import CourseList from '../views/CourseList.vue'; const routes = [ { path: '/', component: CourseList } ]; export const router = createRouter({ history: createWebHistory(), routes }); ``` --- ## ▶️ 如何运行? ### 后端: ```bash cd backend mvn spring-boot:run ``` 访问:[http://localhost:8080/doc.html](http://localhost:8080/doc.html) 查看 Knife4j 接口文档 ### 前端: ```bash cd frontend npm run dev ``` 访问:[http://localhost:5173](http://localhost:5173) --- ## ✅ 功能完成情况 | 功能 | 是否支持 | |------|----------| | 用户管理(登录、角色) | ✅ | | 课程管理(增删改查) | ✅ | | 课程分类管理 | ✅ | | 标签管理(可自行扩展多对多展示) | ✅(结构已建) | | 评价管理(防重、查看) | ✅ | --- ## ⚠️ 可优化点 - 使用 `BCryptPasswordEncoder` 加密密码 - 使用 JWT 实现真正无状态认证 - 添加分页查询(PageHelper 或 MyBatis-Plus 分页插件) - 前端增加标签展示、搜索过滤功能 - 增加图表统计(如 ECharts 显示评分分布) ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值