SpringBoot整合JPA实现CRUD完整文档
零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目
一、项目简介
零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目
本文档详细介绍如何使用SpringBoot整合JPA实现一个简单的用户信息管理系统。系统包含用户基本信息的增删改查功能,适用于公众号发布的技术教程。
二、技术栈
- Spring Boot 2.7+
- Spring Data JPA
- MySQL 8.0+
- Maven
- Lombok
三、数据库设计
零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目
3.1 用户信息表结构
CREATE TABLE user_info (
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
name VARCHAR(50) NOT NULL COMMENT '姓名',
id_type VARCHAR(20) NOT NULL COMMENT '证件类型',
id_number VARCHAR(50) NOT NULL COMMENT '证件号码',
card_number VARCHAR(30) COMMENT '卡号',
face_image VARCHAR(500) COMMENT '人脸照片路径',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (id),
UNIQUE KEY uk_id_number (id_number),
INDEX idx_name (name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户信息表';
四、项目搭建
零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目
4.1 创建SpringBoot项目
通过Spring Initializr创建项目,选择以下依赖:
- Spring Web
- Spring Data JPA
- MySQL Driver
- Lombok
4.2 pom.xml依赖配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.14</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>jpa-demo</artifactId>
<version>1.0.0</version>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- Web框架 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
4.3 配置文件
application.yml
server:
port: 8080
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/jpa_demo?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root123456
jpa:
hibernate:
ddl-auto: update
show-sql: true
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL8Dialect
format_sql: true
五、核心代码实现
5.1 实体类
UserInfo.java
package com.example.jpademo.entity;
import lombok.Data;
import javax.persistence.*;
import java.time.LocalDateTime;
/**
* 用户信息实体类
*/
@Entity
@Table(name = "user_info")
@Data
public class UserInfo {
/**
* 主键ID
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
* 姓名
*/
@Column(name = "name", nullable = false, length = 50)
private String name;
/**
* 证件类型
*/
@Column(name = "id_type", nullable = false, length = 20)
private String idType;
/**
* 证件号码
*/
@Column(name = "id_number", nullable = false, unique = true, length = 50)
private String idNumber;
/**
* 卡号
*/
@Column(name = "card_number", length = 30)
private String cardNumber;
/**
* 人脸照片路径
*/
@Column(name = "face_image", length = 500)
private String faceImage;
/**
* 创建时间
*/
@Column(name = "create_time")
private LocalDateTime createTime;
/**
* 更新时间
*/
@Column(name = "update_time")
private LocalDateTime updateTime;
/**
* 保存前自动设置时间
*/
@PrePersist
protected void onCreate() {
createTime = LocalDateTime.now();
updateTime = LocalDateTime.now();
}
/**
* 更新前自动设置时间
*/
@PreUpdate
protected void onUpdate() {
updateTime = LocalDateTime.now();
}
}
5.2 数据访问层
UserInfoRepository.java
package com.example.jpademo.repository;
import com.example.jpademo.entity.UserInfo;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
/**
* 用户信息数据访问接口
* 继承JpaRepository,自动获得基本的CRUD方法
*/
@Repository
public interface UserInfoRepository extends JpaRepository<UserInfo, Long> {
/**
* 根据姓名查询用户
*/
List<UserInfo> findByName(String name);
/**
* 根据姓名模糊查询
*/
List<UserInfo> findByNameContaining(String name);
/**
* 根据证件号码查询
*/
Optional<UserInfo> findByIdNumber(String idNumber);
/**
* 根据证件类型查询
*/
List<UserInfo> findByIdType(String idType);
/**
* 根据卡号查询
*/
Optional<UserInfo> findByCardNumber(String cardNumber);
/**
* 根据姓名分页查询
*/
Page<UserInfo> findByNameContaining(String name, Pageable pageable);
/**
* 检查证件号码是否存在
*/
boolean existsByIdNumber(String idNumber);
}
5.3 服务层
UserInfoService.java
package com.example.jpademo.service;
import com.example.jpademo.entity.UserInfo;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import java.util.List;
/**
* 用户信息服务接口
*/
public interface UserInfoService {
/**
* 保存用户信息
*/
UserInfo save(UserInfo userInfo);
/**
* 更新用户信息
*/
UserInfo update(UserInfo userInfo);
/**
* 根据ID删除用户
*/
void deleteById(Long id);
/**
* 根据ID查询用户
*/
UserInfo findById(Long id);
/**
* 查询所有用户
*/
List<UserInfo> findAll();
/**
* 分页查询所有用户
*/
Page<UserInfo> findAll(Pageable pageable);
/**
* 根据姓名查询
*/
List<UserInfo> findByName(String name);
/**
* 根据姓名分页查询
*/
Page<UserInfo> findByName(String name, Pageable pageable);
/**
* 根据证件号码查询
*/
UserInfo findByIdNumber(String idNumber);
/**
* 检查证件号码是否存在
*/
boolean existsByIdNumber(String idNumber);
}
UserInfoServiceImpl.java
package com.example.jpademo.service.impl;
import com.example.jpademo.entity.UserInfo;
import com.example.jpademo.repository.UserInfoRepository;
import com.example.jpademo.service.UserInfoService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Optional;
/**
* 用户信息服务实现
*/
@Service
@RequiredArgsConstructor
public class UserInfoServiceImpl implements UserInfoService {
private final UserInfoRepository userInfoRepository;
@Override
@Transactional
public UserInfo save(UserInfo userInfo) {
// 保存前可以添加业务逻辑
return userInfoRepository.save(userInfo);
}
@Override
@Transactional
public UserInfo update(UserInfo userInfo) {
// 更新前检查用户是否存在
if (!userInfoRepository.existsById(userInfo.getId())) {
throw new RuntimeException("用户不存在,ID:" + userInfo.getId());
}
return userInfoRepository.save(userInfo);
}
@Override
@Transactional
public void deleteById(Long id) {
userInfoRepository.deleteById(id);
}
@Override
public UserInfo findById(Long id) {
return userInfoRepository.findById(id)
.orElseThrow(() -> new RuntimeException("用户不存在,ID:" + id));
}
@Override
public List<UserInfo> findAll() {
return userInfoRepository.findAll();
}
@Override
public Page<UserInfo> findAll(Pageable pageable) {
return userInfoRepository.findAll(pageable);
}
@Override
public List<UserInfo> findByName(String name) {
return userInfoRepository.findByNameContaining(name);
}
@Override
public Page<UserInfo> findByName(String name, Pageable pageable) {
return userInfoRepository.findByNameContaining(name, pageable);
}
@Override
public UserInfo findByIdNumber(String idNumber) {
return userInfoRepository.findByIdNumber(idNumber)
.orElseThrow(() -> new RuntimeException("用户不存在,证件号码:" + idNumber));
}
@Override
public boolean existsByIdNumber(String idNumber) {
return userInfoRepository.existsByIdNumber(idNumber);
}
}
5.4 控制层
UserInfoController.java
package com.example.jpademo.controller;
import com.example.jpademo.entity.UserInfo;
import com.example.jpademo.service.UserInfoService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 用户信息控制器
*/
@RestController
@RequestMapping("/api/user-info")
@RequiredArgsConstructor
public class UserInfoController {
private final UserInfoService userInfoService;
/**
* 创建用户
*/
@PostMapping
public ResponseEntity<Map<String, Object>> create(@RequestBody UserInfo userInfo) {
// 检查证件号码是否已存在
if (userInfoService.existsByIdNumber(userInfo.getIdNumber())) {
return ResponseEntity.badRequest()
.body(Map.of("message", "证件号码已存在"));
}
UserInfo saved = userInfoService.save(userInfo);
return ResponseEntity.ok(Map.of(
"code", 200,
"message", "创建成功",
"data", saved
));
}
/**
* 更新用户
*/
@PutMapping("/{id}")
public ResponseEntity<Map<String, Object>> update(
@PathVariable Long id,
@RequestBody UserInfo userInfo) {
userInfo.setId(id);
UserInfo updated = userInfoService.update(userInfo);
return ResponseEntity.ok(Map.of(
"code", 200,
"message", "更新成功",
"data", updated
));
}
/**
* 删除用户
*/
@DeleteMapping("/{id}")
public ResponseEntity<Map<String, Object>> delete(@PathVariable Long id) {
userInfoService.deleteById(id);
return ResponseEntity.ok(Map.of(
"code", 200,
"message", "删除成功"
));
}
/**
* 根据ID查询用户
*/
@GetMapping("/{id}")
public ResponseEntity<Map<String, Object>> getById(@PathVariable Long id) {
UserInfo userInfo = userInfoService.findById(id);
return ResponseEntity.ok(Map.of(
"code", 200,
"message", "查询成功",
"data", userInfo
));
}
/**
* 查询所有用户
*/
@GetMapping("/all")
public ResponseEntity<Map<String, Object>> getAll() {
List<UserInfo> list = userInfoService.findAll();
return ResponseEntity.ok(Map.of(
"code", 200,
"message", "查询成功",
"data", list,
"total", list.size()
));
}
/**
* 分页查询用户
*/
@GetMapping("/page")
public ResponseEntity<Map<String, Object>> getPage(
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer size) {
Pageable pageable = PageRequest.of(page - 1, size, Sort.by("createTime").descending());
Page<UserInfo> pageResult = userInfoService.findAll(pageable);
Map<String, Object> result = new HashMap<>();
result.put("code", 200);
result.put("message", "查询成功");
result.put("data", pageResult.getContent());
result.put("page", page);
result.put("size", size);
result.put("total", pageResult.getTotalElements());
result.put("totalPages", pageResult.getTotalPages());
return ResponseEntity.ok(result);
}
/**
* 根据姓名查询
*/
@GetMapping("/search/name")
public ResponseEntity<Map<String, Object>> searchByName(
@RequestParam String name,
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer size) {
Pageable pageable = PageRequest.of(page - 1, size);
Page<UserInfo> pageResult = userInfoService.findByName(name, pageable);
Map<String, Object> result = new HashMap<>();
result.put("code", 200);
result.put("message", "查询成功");
result.put("data", pageResult.getContent());
result.put("total", pageResult.getTotalElements());
return ResponseEntity.ok(result);
}
/**
* 根据证件号码查询
*/
@GetMapping("/search/id-number")
public ResponseEntity<Map<String, Object>> searchByIdNumber(@RequestParam String idNumber) {
UserInfo userInfo = userInfoService.findByIdNumber(idNumber);
return ResponseEntity.ok(Map.of(
"code", 200,
"message", "查询成功",
"data", userInfo
));
}
}
5.5 全局异常处理
GlobalExceptionHandler.java
package com.example.jpademo.handler;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.Map;
/**
* 全局异常处理器
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理运行时异常
*/
@ExceptionHandler(RuntimeException.class)
public ResponseEntity<Map<String, Object>> handleRuntimeException(RuntimeException e) {
return ResponseEntity.badRequest()
.body(Map.of(
"code", 400,
"message", e.getMessage()
));
}
/**
* 处理所有异常
*/
@ExceptionHandler(Exception.class)
public ResponseEntity<Map<String, Object>> handleException(Exception e) {
return ResponseEntity.internalServerError()
.body(Map.of(
"code", 500,
"message", "服务器内部错误"
));
}
}
六、API接口说明
6.1 接口列表
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/user-info | 创建用户 |
| PUT | /api/user-info/{id} | 更新用户 |
| DELETE | /api/user-info/{id} | 删除用户 |
| GET | /api/user-info/{id} | 根据ID查询用户 |
| GET | /api/user-info/all | 查询所有用户 |
| GET | /api/user-info/page | 分页查询用户 |
| GET | /api/user-info/search/name | 根据姓名查询 |
| GET | /api/user-info/search/id-number | 根据证件号码查询 |
6.2 请求示例
创建用户:
curl -X POST http://localhost:8080/api/user-info \
-H "Content-Type: application/json" \
-d '{
"name": "张三",
"idType": "身份证",
"idNumber": "110101199001011234",
"cardNumber": "88888888",
"faceImage": "/images/face1.jpg"
}'
分页查询:
curl "http://localhost:8080/api/user-info/page?page=1&size=10"
根据姓名搜索:
curl "http://localhost:8080/api/user-info/search/name?name=张三&page=1&size=10"
七、启动项目
7.1 主启动类
package com.example.jpademo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class JpaDemoApplication {
public static void main(String[] args) {
SpringApplication.run(JpaDemoApplication.class, args);
}
}
7.2 启动命令
# 通过Maven启动
mvn spring-boot:run
# 或者打包后运行
mvn clean package
java -jar target/jpa-demo-1.0.0.jar
八、测试验证
8.1 使用Postman测试
- 启动应用,确保MySQL数据库已启动
- 使用Postman测试各个接口
- 验证数据库数据是否正确
8.2 单元测试示例
UserInfoServiceTest.java
package com.example.jpademo.service;
import com.example.jpademo.entity.UserInfo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
@Transactional
class UserInfoServiceTest {
@Autowired
private UserInfoService userInfoService;
@Test
void testSaveAndFind() {
// 创建用户
UserInfo userInfo = new UserInfo();
userInfo.setName("测试用户");
userInfo.setIdType("身份证");
userInfo.setIdNumber("110101199001011234");
userInfo.setCardNumber("88888888");
// 保存
UserInfo saved = userInfoService.save(userInfo);
assertNotNull(saved.getId());
// 查询
UserInfo found = userInfoService.findById(saved.getId());
assertEquals("测试用户", found.getName());
assertEquals("110101199001011234", found.getIdNumber());
}
@Test
void testDelete() {
UserInfo userInfo = new UserInfo();
userInfo.setName("删除测试");
userInfo.setIdType("身份证");
userInfo.setIdNumber("110101199001011235");
UserInfo saved = userInfoService.save(userInfo);
userInfoService.deleteById(saved.getId());
assertThrows(RuntimeException.class, () -> {
userInfoService.findById(saved.getId());
});
}
}
九、个人总结
通过以上步骤,我们完成了SpringBoot整合JPA实现CRUD的完整示例。这个项目具有以下特点:
- 结构清晰:分层明确,符合MVC架构
- 功能完整:实现了增删改查所有基本操作
- 代码简洁:使用Lombok简化代码,逻辑清晰
- 易于扩展:可以根据需要添加更多功能和字段
- 文档齐全:包含完整的API文档和测试方法
这个项目非常适合初学者学习SpringBoot和JPA的基本使用,也可以作为实际项目的起点进行扩展。