我们来实现一个更贴近实际开发的 用户管理服务器,包含以下功能:
• 数据库交互(使用 JPA + MySQL)
• 完整的 CRUD 接口(RESTful 风格)
• 参数校验与自定义异常处理
• 分层架构(Controller/Service/Repository)
• DTO 数据传输(避免直接暴露实体类)
一、技术栈与依赖
在 Spring Initializr 中额外添加以下依赖:
• Spring Web(Web 服务器基础)
• Spring Data JPA(数据库操作)
• MySQL Driver(数据库驱动)
• Validation(参数校验)
• Lombok(简化实体类代码,需 IDE 安装 Lombok 插件)
二、项目结构(分层架构)
src/main/java/com/example/usermanage/
├── UserManageApplication.java // 主启动类
├── controller/ // 控制器层(处理HTTP请求)
│ └── UserController.java
├── service/ // 服务层(业务逻辑)
│ ├── UserService.java
│ └── impl/UserServiceImpl.java
├── repository/ // 数据访问层(操作数据库)
│ └── UserRepository.java
├── entity/ // 数据库实体类
│ └── User.java
├── dto/ // 数据传输对象(请求/响应)
│ ├── UserRequest.java // 接收创建/更新用户的请求参数
│ └── UserResponse.java // 返回用户数据
├── exception/ // 异常处理
│ ├── UserNotFoundException.java // 自定义异常
│ ├── GlobalExceptionHandler.java // 全局异常处理器
│ └── ErrorResponse.java // 统一错误响应格式
└── config/ // 配置类
└── JpaConfig.java // JPA 配置(可选)
src/main/resources/
├── application.properties // 全局配置(数据库、端口等)
三、核心代码实现
1. 配置文件(application.properties)
# 服务器端口
server.port=8080
# MySQL 数据库配置(需先在本地创建名为 `usermanage` 的数据库)
spring.datasource.url=jdbc:mysql://localhost:3306/usermanage?useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=你的数据库密码
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# JPA 配置
spring.jpa.hibernate.ddl-auto=update # 自动创建/更新表结构
spring.jpa.show-sql=true # 打印SQL日志
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
spring.jpa.properties.hibernate.format_sql=true # 格式化SQL
# 日志级别(可选)
logging.level.org.springframework.web=INFO
logging.level.com.example.usermanage=DEBUG
2. 实体类(User.java)
package com.example.usermanage.entity;
import jakarta.persistence.*;
import lombok.Data;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import java.time.LocalDateTime;
@Data // Lombok 注解:自动生成 getter/setter/toString 等
@Entity
@Table(name = "t_user") // 数据库表名
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // 自增主键
private Long id;
@Column(unique = true, nullable = false) // 用户名唯一且非空
private String username;
@Column(nullable = false)
private String email;
private Integer age;
@CreationTimestamp // 自动填充创建时间
private LocalDateTime createTime;
@UpdateTimestamp // 自动填充更新时间
private LocalDateTime updateTime;
}
3. DTO(数据传输对象)
UserRequest.java(接收请求参数)
package com.example.usermanage.dto;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@Data
public class UserRequest {
@NotBlank(message = "用户名不能为空") // 参数校验:非空
private String username;
@NotBlank(message = "邮箱不能为空")
@Email(message = "邮箱格式不正确") // 校验邮箱格式
private String email;
@Min(value = 0, message = "年龄不能为负数") // 校验年龄最小值
private Integer age;
}
UserResponse.java(返回响应数据)
package com.example.usermanage.dto;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class UserResponse {
private Long id;
private String username;
private String email;
private Integer age;
private LocalDateTime createTime;
}
4. 数据访问层(UserRepository.java)
package com.example.usermanage.repository;
import com.example.usermanage.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
// 继承 JpaRepository 即可获得 CRUD 方法(无需手动实现)
public interface UserRepository extends JpaRepository<User, Long> {
// 自定义查询:根据用户名查询(用于判断用户名是否已存在)
Optional<User> findByUsername(String username);
}
5. 服务层(Service)
UserService.java(接口)
package com.example.usermanage.service;
import com.example.usermanage.dto.UserRequest;
import com.example.usermanage.dto.UserResponse;
import java.util.List;
public interface UserService {
// 创建用户
UserResponse createUser(UserRequest request);
// 查询所有用户
List<UserResponse> getAllUsers();
// 根据ID查询用户
UserResponse getUserById(Long id);
// 更新用户
UserResponse updateUser(Long id, UserRequest request);
// 删除用户
void deleteUser(Long id);
}
UserServiceImpl.java(实现类)
package com.example.usermanage.service.impl;
import com.example.usermanage.dto.UserRequest;
import com.example.usermanage.dto.UserResponse;
import com.example.usermanage.entity.User;
import com.example.usermanage.exception.UserNotFoundException;
import com.example.usermanage.repository.UserRepository;
import com.example.usermanage.service.UserService;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor // Lombok 自动生成构造方法(注入依赖)
public class UserServiceImpl implements UserService {
private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);
private final UserRepository userRepository; // 注入 Repository
@Override
public UserResponse createUser(UserRequest request) {
// 校验用户名是否已存在
if (userRepository.findByUsername(request.getUsername()).isPresent()) {
log.error("用户名已存在:{}", request.getUsername());
throw new IllegalArgumentException("用户名已存在");
}
// 转换 DTO 为实体类
User user = new User();
user.setUsername(request.getUsername());
user.setEmail(request.getEmail());
user.setAge(request.getAge());
// 保存到数据库
User savedUser = userRepository.save(user);
log.info("创建用户成功:{}", savedUser.getId());
// 转换实体类为响应 DTO 并返回
return convertToResponse(savedUser);
}
@Override
public List<UserResponse> getAllUsers() {
log.info("查询所有用户");
return userRepository.findAll().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
}
@Override
public UserResponse getUserById(Long id) {
log.info("查询用户:{}", id);
User user = userRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException("用户不存在:" + id));
return convertToResponse(user);
}
@Override
public UserResponse updateUser(Long id, UserRequest request) {
log.info("更新用户:{}", id);
// 先查询用户是否存在
User user = userRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException("用户不存在:" + id));
// 校验用户名是否被其他用户占用
if (userRepository.findByUsername(request.getUsername()).isPresent()
&& !user.getUsername().equals(request.getUsername())) {
throw new IllegalArgumentException("用户名已存在");
}
// 更新字段
user.setUsername(request.getUsername());
user.setEmail(request.getEmail());
user.setAge(request.getAge());
User updatedUser = userRepository.save(user);
return convertToResponse(updatedUser);
}
@Override
public void deleteUser(Long id) {
log.info("删除用户:{}", id);
if (!userRepository.existsById(id)) {
throw new UserNotFoundException("用户不存在:" + id);
}
userRepository.deleteById(id);
}
// 实体类转响应 DTO 的工具方法
private UserResponse convertToResponse(User user) {
UserResponse response = new UserResponse();
response.setId(user.getId());
response.setUsername(user.getUsername());
response.setEmail(user.getEmail());
response.setAge(user.getAge());
response.setCreateTime(user.getCreateTime());
return response;
}
}
6. 控制器层(UserController.java)
package com.example.usermanage.controller;
import com.example.usermanage.dto.UserRequest;
import com.example.usermanage.dto.UserResponse;
import com.example.usermanage.service.UserService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/users") // 基础路径
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
// 创建用户(POST 请求)
@PostMapping
public ResponseEntity<UserResponse> createUser(@Valid @RequestBody UserRequest request) {
return new ResponseEntity<>(userService.createUser(request), HttpStatus.CREATED);
}
// 查询所有用户(GET 请求)
@GetMapping
public ResponseEntity<List<UserResponse>> getAllUsers() {
return ResponseEntity.ok(userService.getAllUsers());
}
// 查询单个用户(GET 请求,路径带参数)
@GetMapping("/{id}")
public ResponseEntity<UserResponse> getUserById(@PathVariable Long id) {
return ResponseEntity.ok(userService.getUserById(id));
}
// 更新用户(PUT 请求)
@PutMapping("/{id}")
public ResponseEntity<UserResponse> updateUser(
@PathVariable Long id,
@Valid @RequestBody UserRequest request
) {
return ResponseEntity.ok(userService.updateUser(id, request));
}
// 删除用户(DELETE 请求)
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
return ResponseEntity.noContent().build(); // 返回 204 无内容
}
}
7. 异常处理
自定义异常(UserNotFoundException.java)
package com.example.usermanage.exception;
// 当查询的用户不存在时抛出
public class UserNotFoundException extends RuntimeException {
public UserNotFoundException(String message) {
super(message);
}
}
统一错误响应格式(ErrorResponse.java)
package com.example.usermanage.exception;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class ErrorResponse {
private int status; // 状态码
private String message; // 错误信息
private LocalDateTime timestamp; // 时间戳
public ErrorResponse(int status, String message) {
this.status = status;
this.message = message;
this.timestamp = LocalDateTime.now();
}
}
全局异常处理器(GlobalExceptionHandler.java)
package com.example.usermanage.exception;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice // 全局异常处理
public class GlobalExceptionHandler {
// 处理用户不存在异常
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<ErrorResponse> handleUserNotFound(UserNotFoundException e) {
ErrorResponse error = new ErrorResponse(
HttpStatus.NOT_FOUND.value(), // 404 状态码
e.getMessage()
);
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
// 处理参数校验异常(如邮箱格式错误、用户名为空)
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidationExceptions(MethodArgumentNotValidException e) {
// 获取第一个校验失败的消息
String message = e.getBindingResult().getFieldError().getDefaultMessage();
ErrorResponse error = new ErrorResponse(
HttpStatus.BAD_REQUEST.value(), // 400 状态码
message
);
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}
// 处理其他业务异常(如用户名已存在)
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<ErrorResponse> handleIllegalArgument(IllegalArgumentException e) {
ErrorResponse error = new ErrorResponse(
HttpStatus.BAD_REQUEST.value(),
e.getMessage()
);
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}
}
四、启动与测试
1. 准备工作
• 本地安装 MySQL,创建数据库 usermanage(命令:CREATE DATABASE usermanage;)
• 修改 application.properties 中的 MySQL 用户名和密码为你的本地配置
2. 启动服务器
运行 UserManageApplication.java 主类,控制台显示 Started UserManageApplication 表示启动成功。
3. 测试接口(用 Postman 或 curl)
接口功能 请求方式 地址 请求体示例(JSON)
创建用户 POST http://localhost:8080/api/users {"username":"zhangsan","email":"zhangsan@test.com","age":20}
查询所有用户 GET http://localhost:8080/api/users 无
查询单个用户 GET http://localhost:8080/api/users/1 无(1 为用户 ID)
更新用户 PUT http://localhost:8080/api/users/1 {"username":"zhangsan2","email":"new@test.com","age":21}
删除用户 DELETE http://localhost:8080/api/users/1 无
五、关键扩展点说明
1. 数据库替换:若要使用 MySQL 以外的数据库(如 PostgreSQL),只需修改 application.properties 中的驱动和连接地址,并添加对应依赖。
2. 分页查询:在 UserRepository 中添加 Page<User> findAll(Pageable pageable);,Service 和 Controller 层接收 page 和 size 参数即可实现分页。
3. 认证授权:集成 Spring Security 可添加登录验证、角色权限控制。
4. 日志增强:可通过 logback-spring.xml 配置日志输出格式、文件存储等。
这个例子涵盖了实际开发中的核心流程,包括分层架构、数据校验、异常处理、数据库交互等,你可以基于此进一步扩展功能(如添加缓存、异步任务等)。