数据访问与数据库基础(零基础详细版)

目标:会创建数据库表、配置数据源,使用 MyBatis 或 JPA 完成 CRUD,理解连接池与简单事务。

← 返回一级入口

学习目标与前置

建议用时:90 分钟准备:本地 MySQL、Spring Boot 项目

Step 1:准备数据库

  1. 启动 MySQL,创建数据库:create database demo default charset utf8mb4;
  2. 创建表:
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 转为业务错误码。

常见问题与排查

课堂练习

课后巩固