您的位置:首页 > 手游攻略 > Spring Boot + Spring AI 从入门到实战:7天转型计划 Day 3 —— 消息表设计 + 级联删除 + 事务管理

Spring Boot + Spring AI 从入门到实战:7天转型计划 Day 3 —— 消息表设计 + 级联删除 + 事务管理

作者:互联网  时间: 2026-08-11 12:26:56  

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;@Data@TableName("message")publicclassMessage {@TableId(type = IdType.AUTO)privateLong id;privateLong sessionId;private String role;private String content;@TableField(fill = FieldFill.INSERT)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;@DatapublicclassApiResponse<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;@DatapublicclassMessageSaveRequest {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;@Slf4j@Service@RequiredArgsConstructorpublicclassMessageService {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;@Slf4j@Service@RequiredArgsConstructorpublicclassSessionService {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;}@Transactional(rollbackFor = Exception.class)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;}}

  1. rollbackFor = Exception.class:当发生任何异常时都回滚事务
  2. 如果不指定,默认只在 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;@Slf4j@RestController@RequestMapping("/api/messages")@RequiredArgsConstructorpublicclassMessageController {privatefinal MessageService messageService;privatefinal SessionService sessionService;@GetMapping("/{sessionId}")public ApiResponse<List<Message>> getMessages(@PathVariableLong sessionId) {log.info("接收请求:获取会话 {} 的消息列表", sessionId);if (!sessionService.exists(sessionId)) {return ApiResponse.error(404, "会话不存在");}List<Message> messages = messageService.getMessagesBySession(sessionId);return ApiResponse.success(messages);}@PostMappingpublic ApiResponse<Message> saveMessage(@RequestBody 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 把所有接口测一遍。

最新游戏

更多

Copyright©2010-2019. All rights reserved | 波波三国游戏官网|[email protected]

备案编号:湘ICP备2022015115号-4