Spring Boot + Spring AI 从入门到实战:7天转型计划 Day 3 —— 消息表设计 + 级联删除 + 事务管理
Spring Boot + Spring AI 从入门到实战:7天转型计划 Day 3 —— 消息表设计 + 级联删除 + 事务管理需要先看清适用场景和关键步骤,避免只记结论却忽略实际限制。
写在前面
作为一名技术人,我深知学习新框架时的迷茫与焦虑。尤其是当你已经熟悉了一套技术栈,想要转向另一个生态时,那种“无从下手”的感觉尤为强烈。
最近,我开始系统学习Spring Boot生态,并尝试将Spring AI集成到实际项目中。经过一段时间的摸索,我整理出了一份7天学习路线图,目标是让有编程基础的开发者能快速上手Spring Boot + Spring AI,独立完成一个完整的AI对话应用。
这份计划不是零基础教程,而是为有一定后端开发经验(不限于Java)的朋友量身定制的“快速转型指南”。如果你对Python、Node.js或其他后端语言已有了解,这份计划将帮助你在最短时间内建立Spring Boot的核心认知,并跟上AI应用开发的浪潮。
下面,我将这7天的学习中的第三天,消息表设计 + 级联删除 + 事务管理。
一、创建消息表
1.1 执行 SQL
USE chat_db;CREATETABLE IF NOTEXISTS message (id BIGINTNOTNULL AUTO_INCREMENT COMMENT '消息ID',session_id BIGINTNOTNULL COMMENT '会话ID(逻辑外键,关联session表)',role VARCHAR(20) NOTNULL COMMENT '角色:user(用户)/assistant(AI)',content TEXT NOTNULL COMMENT '消息内容',created_at DATETIME NOTNULLDEFAULTCURRENT_TIMESTAMP COMMENT '创建时间',PRIMARY KEY (id),INDEX idx_session_id (session_id),INDEX idx_created_at (created_at)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='消息表';
二、创建 Message 实体类
创建 entity/Message.java:
package com.example.chatapi.entity;import com.baomidou.mybatisplus.annotation.*;import lombok.Data;import java.time.LocalDateTime;publicclassMessage {privateLong id;privateLong sessionId;private String role;private String content;private LocalDateTime createdAt;}
三、创建 MessageMapper
创建 mapper/MessageMapper.java:
packagecom.example.chatapi.mapper;importcom.baomidou.mybatisplus.core.mapper.BaseMapper;importcom.example.chatapi.entity.Message;importorg.apache.ibatis.annotations.*;importjava.util.List;@MapperpublicinterfaceMessageMapperextendsBaseMapper<Message> {@Select("SELECT * FROM message WHERE session_id = #{sessionId} ORDER BY created_at ASC")List<Message>selectBySessionId(@Param("sessionId") Long sessionId);@Delete("DELETE FROM message WHERE session_id = #{sessionId}")int deleteBySessionId(@Param("sessionId") Long sessionId);}
四、创建统一响应类
创建 dto/ApiResponse.java:
package com.example.chatapi.dto;import lombok.Data;publicclassApiResponse<T> {privateInteger code;privateString message;private T data;publicstatic <T>ApiResponse<T>success(T data) {ApiResponse<T> response = newApiResponse<>();response.setCode(200);response.setMessage("success");response.setData(data);return response;}publicstatic <T>ApiResponse<T>error(Integer code, String message) {ApiResponse<T> response = newApiResponse<>();response.setCode(code);response.setMessage(message);return response;}}
创建 dto/MessageSaveRequest.java:
package com.example.chatapi.dto;import lombok.Data;publicclassMessageSaveRequest {privateLong sessionId;private String role;private String content;}
五、创建 MessageService
创建 service/MessageService.java:
package com.example.chatapi.service;import com.example.chatapi.entity.Message;import com.example.chatapi.mapper.MessageMapper;import lombok.RequiredArgsConstructor;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Service;import java.util.List;publicclassMessageService {privatefinal MessageMapper messageMapper;public List<Message>getMessagesBySession(Long sessionId) {log.info("查询会话 {} 的消息列表", sessionId);return messageMapper.selectBySessionId(sessionId);}publicbooleansaveMessage(Message message) {log.info("保存消息: sessionId={}, role={}", message.getSessionId(), message.getRole());introws= messageMapper.insert(message);return rows >0;}}
六、修改 SessionService(添加级联删除)
更新 service/SessionService.java:
package com.example.chatapi.service;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;import com.example.chatapi.entity.Session;import com.example.chatapi.mapper.MessageMapper;import com.example.chatapi.mapper.SessionMapper;import lombok.RequiredArgsConstructor;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;import java.time.LocalDateTime;import java.util.List;publicclassSessionService {privatefinal SessionMapper sessionMapper;privatefinal MessageMapper messageMapper;public List<Session>listSessions() {LambdaQueryWrapper<Session> wrapper = newLambdaQueryWrapper<>();wrapper.orderByDesc(Session::getCreatedAt);return sessionMapper.selectList(wrapper);}public Session createSession(String name) {Sessionsession=newSession();session.setName(name);session.setCreatedAt(LocalDateTime.now());sessionMapper.insert(session);log.info("创建会话成功: id={}, name={}", session.getId(), name);return session;}publicbooleanupdateSession(Long id, String name) {Sessionsession=newSession();session.setId(id);session.setName(name);introws= sessionMapper.updateById(session);return rows >0;}publicbooleandeleteSession(Long id) {log.info("开始删除会话 {} 及其所有消息", id);intdeletedMessages= messageMapper.deleteBySessionId(id);log.info("删除了 {} 条消息", deletedMessages);intdeletedSession= sessionMapper.deleteById(id);return deletedSession >0;}publicbooleanexists(Long id) {return sessionMapper.selectCount(newLambdaQueryWrapper<Session>().eq(Session::getId, id)) >0;}}
- rollbackFor = Exception.class:当发生任何异常时都回滚事务
- 如果不指定,默认只在 RuntimeException 和 Error 时回滚
异常情况(删除消息后出错)
开始事务↓删除消息(成功)✓↓删除会话(失败)✗ 抛出异常↓回滚事务 → 删除的消息恢复,数据回到原状
七、创建 MessageController
创建 controller/MessageController.java:
package com.example.chatapi.controller;import com.example.chatapi.dto.ApiResponse;import com.example.chatapi.dto.MessageSaveRequest;import com.example.chatapi.entity.Message;import com.example.chatapi.service.MessageService;import com.example.chatapi.service.SessionService;import lombok.RequiredArgsConstructor;import lombok.extern.slf4j.Slf4j;import org.springframework.web.bind.annotation.*;import java.util.List;publicclassMessageController {privatefinal MessageService messageService;privatefinal SessionService sessionService;public ApiResponse<List<Message>> getMessages(Long sessionId) {log.info("接收请求:获取会话 {} 的消息列表", sessionId);if (!sessionService.exists(sessionId)) {return ApiResponse.error(404, "会话不存在");}List<Message> messages = messageService.getMessagesBySession(sessionId);return ApiResponse.success(messages);}public ApiResponse<Message> saveMessage( MessageSaveRequest request) {log.info("接收请求:保存消息 sessionId={}, role={}", request.getSessionId(), request.getRole());if (!sessionService.exists(request.getSessionId())) {return ApiResponse.error(404, "会话不存在");}if (!"user".equals(request.getRole()) && !"assistant".equals(request.getRole())) {return ApiResponse.error(400, "角色只能是 user 或 assistant");}Message message = new Message();message.setSessionId(request.getSessionId());message.setRole(request.getRole());message.setContent(request.getContent());boolean success = messageService.saveMessage(message);if (success) {return ApiResponse.success(message);} else {return ApiResponse.error(500, "保存失败");}}}
八、启动项目并测试
8.1 启动项目
运行 ChatApiApplication.java,控制台看到以下日志即成功:
8.2 Postman 测试
POST http://localhost:8080/api/messagesContent-Type: application/json{"sessionId": 1, "role": "user", "content": "什么是Spring Boot?"}
学习建议: 今天的重点是创建一个message表,和级联删除。不要只复制代码,动手敲一遍,再用 Postman 把所有接口测一遍。
-
08.11
《穿越火线:枪战王者》兔囡囡说明
-
08.11
《星布谷地》45道煮锅配方
-
08.11
虚实万象Demo通关结算画面有什么彩蛋-虚实万象Demo通关结算画面彩蛋是什么
-
08.11
金铲铲之战福星2026千珏裁决如何配队-金铲铲之战福星2026千珏裁决配队做法
-
08.11
无限暖暖憩凉小亭设计图怎么获得-憩凉小亭设计图获取攻略
-
08.11
幻灵召唤师三小恶魔如何样-幻灵召唤师三小恶魔强不强
-
-
下载
- |
-
-
下载
- 《行尸走肉第一章》免安装中文汉化硬盘版下载
- 单机|436 MB
- 一款以动作冒险为主题的游戏
-
-
下载
- 《街头霸王X铁拳》免安装中文汉化硬盘版下载
- 单机|111MB
- 一款非常好玩的格斗游戏
-
-
下载
- |
-
-
下载
- 《暗黑破坏神3》免安装繁体中文正式版下载
- 单机|7630 MB
- 一款以角色扮演为主题的游戏
-
-
下载
- 《马克思佩恩3》免安装硬盘版下载
- 单机|27033 MB
- 一款以第三人称射击为主题的游戏