学习目标与前置
建议用时:90 分钟准备:本地 MySQL、Spring Boot 项目
- 创建 todo 表并完成 CRUD。
- 配置 HikariCP 数据源。
- 了解分页查询、事务和常见数据库异常。
Step 1:准备数据库
- 启动 MySQL,创建数据库:
create database demo default charset utf8mb4; - 创建表:
CREATE TABLE todo (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(255) NOT NULL,
done TINYINT(1) DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
字段说明:id 自增主键;title 必填;done 布尔;created_at 记录创建时间。
Step 2:配置数据源(application.yaml)
spring:
datasource:
url: jdbc:mysql://localhost:3306/demo?useSSL=false&serverTimezone=UTC
username: root
password: 123456
hikari:
maximum-pool-size: 10
minimum-idle: 2
connection-timeout: 30000
修改用户名/密码与实际一致。Hikari 参数:最大连接数、最小空闲、连接超时。
方案 A:MyBatis 示例
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
@Data
public class Todo {
private Long id;
private String title;
private Boolean done;
private LocalDateTime createdAt;
}
@Mapper
public interface TodoMapper {
@Insert("insert into todo(title, done) values(#{title}, #{done})")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insert(Todo todo);
@Select("select * from todo order by id desc limit #{offset}, #{size}")
List<Todo> page(@Param("offset") int offset, @Param("size") int size);
@Update("update todo set done=#{done} where id=#{id}")
int updateDone(@Param("id") long id, @Param("done") boolean done);
@Delete("delete from todo where id=#{id}")
int delete(@Param("id") long id);
}
分页 offset 计算:(page) * size,若从 0 开始。
方案 B:JPA 示例(可选)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
@Entity
@Table(name = "todo")
public class TodoEntity {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private Boolean done;
private LocalDateTime createdAt;
}
public interface TodoRepo extends JpaRepository<TodoEntity, Long> {
Page<TodoEntity> findByDone(boolean done, Pageable pageable);
}
@Service
public class TodoService {
private final TodoRepo repo;
public TodoService(TodoRepo repo) { this.repo = repo; }
public TodoEntity create(TodoEntity t) { return repo.save(t); }
public Page<TodoEntity> page(int page, int size) { return repo.findAll(PageRequest.of(page, size)); }
}
Step 3:Controller 接入分页
flowchart LR A[Controller] --> B[Service/Mapper] B --> C[连接池 HikariCP] C --> D[(MySQL)] D --> B B --> E[统一响应返回分页数据]
@GetMapping("/api/todos")
public ApiResp<List<Todo>> list(@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
int offset = page * size;
return ApiResp.ok(todoMapper.page(offset, size));
}
注意校验 page/size 非负,避免大数攻击。
Step 4:事务与异常(简单版)
@Service
public class TodoAppService {
private final TodoMapper mapper;
public TodoAppService(TodoMapper mapper) { this.mapper = mapper; }
@Transactional
public void markDoneAndLog(long id) {
mapper.updateDone(id, true);
// 可在此处写入日志表,若异常会整体回滚
}
}
常见异常:重复键(唯一索引)、连接超时、SQL 语法错误;可用 try-catch 转为业务错误码。
常见问题与排查
- 连接失败:检查 URL、用户名密码、防火墙/端口。
- 中文乱码:URL 参数
useUnicode=true&characterEncoding=utf8(新驱动默认支持 utf8mb4)。 - 分页数据错误:确认 offset 计算;检查排序字段。
课堂练习
- 实现“标记完成”接口,调用 updateDone 返回影响行数。
- 在列表接口增加 done 过滤参数(true/false)。
课后巩固
- 为 title 增加唯一索引,尝试插入重复数据,捕获异常并返回 409。
- 撰写说明:Hikari 常用参数(max pool size、idle timeout)含义与推荐值。