本文目录导读:

- 项目环境配置
- 实体类设计
- Repository层
- Service层
- Controller层
- 启动类 (Application.java)
- 测试类 (UserControllerTest.java)
- 数据库初始化脚本 (schema.sql)
- 项目结构
- 常用API测试
我来为你提供一个完整的Spring Boot整合JPA的案例,包含从环境搭建到具体实现的全部过程。
项目环境配置
1 Maven依赖配置 (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
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.14</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>spring-boot-jpa-demo</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot 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>
<version>8.0.33</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- Validation -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- 测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2 配置文件 (application.yml)
spring:
datasource:
url: jdbc:mysql://localhost:3306/jpa_demo?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update # create/create-drop/update/none
show-sql: true
properties:
hibernate:
format_sql: true
dialect: org.hibernate.dialect.MySQL5Dialect
use_sql_comments: true
server:
port: 8080
实体类设计
1 用户实体 (User.java)
package com.example.jpa.entity;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.Builder;
import javax.persistence.*;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank(message = "用户名不能为空")
@Column(nullable = false, unique = true, length = 50)
private String username;
@NotBlank(message = "密码不能为空")
@Column(nullable = false, length = 100)
private String password;
@NotBlank(message = "邮箱不能为空")
@Email(message = "邮箱格式不正确")
@Column(nullable = false, unique = true, length = 100)
private String email;
@Column(length = 20)
private String phone;
@Column(length = 200)
private String address;
@Column(length = 500)
private String avatar; //头像URL
@Column(name = "created_at", nullable = false)
@Temporal(TemporalType.TIMESTAMP)
private Date createdAt;
@Column(name = "updated_at", nullable = false)
@Temporal(TemporalType.TIMESTAMP)
private Date updatedAt;
@Column(name = "is_active", nullable = false)
private Boolean active = true;
@PrePersist
protected void onCreate() {
createdAt = new Date();
updatedAt = new Date();
if (active == null) {
active = true;
}
}
@PreUpdate
protected void onUpdate() {
updatedAt = new Date();
}
}
2 角色实体 (Role.java)
package com.example.jpa.entity;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.Builder;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "roles")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true, length = 50)
private String roleName;
@Column(length = 200)
private String description;
@ManyToMany(mappedBy = "roles", fetch = FetchType.LAZY)
private Set<User> users = new HashSet<>();
}
3 订单实体 (Order.java)
package com.example.jpa.entity;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.Builder;
import javax.persistence.*;
import java.math.BigDecimal;
import java.util.Date;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "orders")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "order_number", nullable = false, unique = true, length = 50)
private String orderNumber;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;
@Column(name = "total_amount", nullable = false, precision = 10, scale = 2)
private BigDecimal totalAmount;
@Column(name = "status", nullable = false, length = 50)
private String status; // PENDING, PAID, SHIPPED, COMPLETED, CANCELLED
@Column(name = "created_at", nullable = false)
@Temporal(TemporalType.TIMESTAMP)
private Date createdAt;
@PrePersist
protected void onCreate() {
createdAt = new Date();
}
}
Repository层
1 用户Repository (UserRepository.java)
package com.example.jpa.repository;
import com.example.jpa.entity.User;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository
public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
// 根据用户名查找
Optional<User> findByUsername(String username);
// 根据邮箱查找
Optional<User> findByEmail(String email);
// 根据用户名和密码查找
Optional<User> findByUsernameAndPassword(String username, String password);
// 模糊查询
List<User> findByUsernameContains(String keyword);
// 查询活跃用户
List<User> findByActiveTrue();
// 使用JPQL查询
@Query("SELECT u FROM User u WHERE u.email LIKE %:domain%")
List<User> findByEmailDomain(@Param("domain") String domain);
@Query("SELECT u FROM User u WHERE u.active = :active")
Page<User> findByActiveStatus(@Param("active") boolean active, Pageable pageable);
// 使用原生SQL查询
@Query(value = "SELECT * FROM users WHERE created_at > ?1", nativeQuery = true)
List<User> findUsersCreatedAfter(String date);
// 更新操作
@Modifying
@Query("UPDATE User u SET u.active = :active WHERE u.id = :id")
int updateUserStatus(@Param("id") Long id, @Param("active") boolean active);
// 联表查询
@Query("SELECT u FROM User u JOIN u.roles r WHERE r.roleName = :roleName")
List<User> findByRoleName(@Param("roleName") String roleName);
// 统计
@Query("SELECT COUNT(u) FROM User u WHERE u.active = :active")
long countByActive(@Param("active") boolean active);
@Query("SELECT function('MONTH', u.createdAt), COUNT(u) FROM User u GROUP BY function('MONTH', u.createdAt)")
List<Object[]> countUsersByMonth();
// 多条件查询
@Query("SELECT u FROM User u WHERE " +
"(:username IS NULL OR u.username = :username) AND " +
"(:email IS NULL OR u.email = :email)")
List<User> findByCriteria(@Param("username") String username,
@Param("email") String email);
// 排序查询
List<User> findAllByOrderByCreatedAtDesc();
// 分页查询
@Query("SELECT u FROM User u WHERE u.active = :active ORDER BY u.createdAt DESC")
Page<User> findByActiveOrderByCreatedAtDesc(@Param("active") boolean active, Pageable pageable);
// 批量删除
@Modifying
@Query("DELETE FROM User u WHERE u.id IN :ids")
int deleteUserIds(@Param("ids") List<Long> ids);
}
2 订单Repository (OrderRepository.java)
package com.example.jpa.repository;
import com.example.jpa.entity.Order;
import com.example.jpa.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.math.BigDecimal;
import java.util.List;
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
// 根据用户查询订单
List<Order> findByUserOrderByCreatedAtDesc(User user);
// 根据用户ID查询订单
@Query("SELECT o FROM Order o WHERE o.user.id = :userId")
List<Order> findByUserId(@Param("userId") Long userId);
// 根据状态查询
List<Order> findByStatusOrderByCreatedAtDesc(String status);
// 查询指定金额以上的订单
List<Order> findByTotalAmountGreaterThan(BigDecimal amount);
// 统计用户订单总数
long countByUser(User user);
// 统计用户总消费金额
@Query("SELECT SUM(o.totalAmount) FROM Order o WHERE o.user.id = :userId")
BigDecimal sumTotalAmountByUserId(@Param("userId") Long userId);
// 联表查询用户及其订单
@Query("SELECT u, o FROM User u LEFT JOIN Order o ON u.id = o.user.id WHERE u.id = :userId")
List<Object[]> findUserWithOrders(@Param("userId") Long userId);
}
Service层
1 用户Service (UserService.java)
package com.example.jpa.service;
import com.example.jpa.entity.User;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import java.util.List;
import java.util.Optional;
public interface UserService {
User createUser(User user);
Optional<User> getUserById(Long id);
Optional<User> getUserByUsername(String username);
List<User> getAllUsers();
User updateUser(Long id, User userDetails);
void deleteUser(Long id);
Page<User> getUsersByPage(Pageable pageable);
List<User> searchUsers(String keyword);
Optional<User> authenticate(String username, String password);
}
2 用户Service实现 (UserServiceImpl.java)
package com.example.jpa.service.impl;
import com.example.jpa.entity.User;
import com.example.jpa.repository.UserRepository;
import com.example.jpa.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityNotFoundException;
import java.util.List;
import java.util.Optional;
@Service
@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
@Override
public User createUser(User user) {
// 检查用户是否已存在
if (userRepository.findByUsername(user.getUsername()).isPresent()) {
throw new RuntimeException("用户名已存在");
}
if (userRepository.findByEmail(user.getEmail()).isPresent()) {
throw new RuntimeException("邮箱已被注册");
}
return userRepository.save(user);
}
@Override
@Transactional(readOnly = true)
public Optional<User> getUserById(Long id) {
return userRepository.findById(id);
}
@Override
@Transactional(readOnly = true)
public Optional<User> getUserByUsername(String username) {
return userRepository.findByUsername(username);
}
@Override
@Transactional(readOnly = true)
public List<User> getAllUsers() {
return userRepository.findAll();
}
@Override
public User updateUser(Long id, User userDetails) {
User user = userRepository.findById(id)
.orElseThrow(() -> new EntityNotFoundException("用户不存在,ID: " + id));
// 更新用户信息
user.setUsername(userDetails.getUsername());
user.setEmail(userDetails.getEmail());
if (userDetails.getPassword() != null && !userDetails.getPassword().isEmpty()) {
user.setPassword(userDetails.getPassword());
}
user.setPhone(userDetails.getPhone());
user.setAddress(userDetails.getAddress());
user.setAvatar(userDetails.getAvatar());
return userRepository.save(user);
}
@Override
public void deleteUser(Long id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new EntityNotFoundException("用户不存在,ID: " + id));
userRepository.delete(user);
}
@Override
@Transactional(readOnly = true)
public Page<User> getUsersByPage(Pageable pageable) {
return userRepository.findAll(pageable);
}
@Override
@Transactional(readOnly = true)
public List<User> searchUsers(String keyword) {
return userRepository.findByUsernameContains(keyword);
}
@Override
@Transactional(readOnly = true)
public Optional<User> authenticate(String username, String password) {
return userRepository.findByUsernameAndPassword(username, password);
}
}
Controller层
1 用户Controller (UserController.java)
package com.example.jpa.controller;
import com.example.jpa.entity.User;
import com.example.jpa.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
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.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@RestController
@RequestMapping("/api/users")
@Validated
public class UserController {
@Autowired
private UserService userService;
// 创建用户
@PostMapping
public ResponseEntity<?> createUser(@Valid @RequestBody User user) {
try {
User created = userService.createUser(user);
return new ResponseEntity<>(created, HttpStatus.CREATED);
} catch (RuntimeException e) {
return new ResponseEntity<>(Map.of("error", e.getMessage()),
HttpStatus.BAD_REQUEST);
}
}
// 获取所有用户
@GetMapping
public ResponseEntity<List<User>> getAllUsers() {
return new ResponseEntity<>(userService.getAllUsers(), HttpStatus.OK);
}
// 分页获取用户
@GetMapping("/page")
public ResponseEntity<Map<String, Object>> getUsersByPage(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(defaultValue = "id,desc") String[] sort) {
try {
Sort.Direction direction = Sort.Direction.fromString(sort[1]);
Pageable pageable = PageRequest.of(page, size,
Sort.by(direction, sort[0]));
Page<User> userPage = userService.getUsersByPage(pageable);
Map<String, Object> response = new HashMap<>();
response.put("users", userPage.getContent());
response.put("currentPage", userPage.getNumber());
response.put("totalItems", userPage.getTotalElements());
response.put("totalPages", userPage.getTotalPages());
return new ResponseEntity<>(response, HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(Map.of("error", e.getMessage()),
HttpStatus.BAD_REQUEST);
}
}
// 根据ID获取用户
@GetMapping("/{id}")
public ResponseEntity<?> getUserById(@PathVariable Long id) {
Optional<User> user = userService.getUserById(id);
if (user.isPresent()) {
return new ResponseEntity<>(user.get(), HttpStatus.OK);
} else {
return new ResponseEntity<>(Map.of("error", "用户不存在"),
HttpStatus.NOT_FOUND);
}
}
// 根据用户名获取用户
@GetMapping("/username/{username}")
public ResponseEntity<?> getUserByUsername(@PathVariable String username) {
Optional<User> user = userService.getUserByUsername(username);
if (user.isPresent()) {
return new ResponseEntity<>(user.get(), HttpStatus.OK);
} else {
return new ResponseEntity<>(Map.of("error", "用户不存在"),
HttpStatus.NOT_FOUND);
}
}
// 搜索用户
@GetMapping("/search")
public ResponseEntity<List<User>> searchUsers(
@RequestParam(required = false) String keyword) {
if (keyword == null || keyword.isEmpty()) {
return new ResponseEntity<>(userService.getAllUsers(), HttpStatus.OK);
}
return new ResponseEntity<>(userService.searchUsers(keyword), HttpStatus.OK);
}
// 更新用户
@PutMapping("/{id}")
public ResponseEntity<?> updateUser(@PathVariable Long id,
@Valid @RequestBody User user) {
try {
User updated = userService.updateUser(id, user);
return new ResponseEntity<>(updated, HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(Map.of("error", e.getMessage()),
HttpStatus.BAD_REQUEST);
}
}
// 删除用户
@DeleteMapping("/{id}")
public ResponseEntity<?> deleteUser(@PathVariable Long id) {
try {
userService.deleteUser(id);
return new ResponseEntity<>(Map.of("message", "用户删除成功"),
HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(Map.of("error", e.getMessage()),
HttpStatus.BAD_REQUEST);
}
}
}
2 订单Controller (OrderController.java)
package com.example.jpa.controller;
import com.example.jpa.entity.Order;
import com.example.jpa.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
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/orders")
public class OrderController {
@Autowired
private OrderService orderService;
@PostMapping
public ResponseEntity<Order> createOrder(@RequestBody Order order) {
Order created = orderService.createOrder(order);
return new ResponseEntity<>(created, HttpStatus.CREATED);
}
@GetMapping("/user/{userId}")
public ResponseEntity<List<Order>> getOrdersByUser(@PathVariable Long userId) {
return new ResponseEntity<>(orderService.getOrdersByUserId(userId), HttpStatus.OK);
}
@GetMapping("/stats/{userId}")
public ResponseEntity<Map<String, Object>> getOrderStats(@PathVariable Long userId) {
Map<String, Object> stats = new HashMap<>();
stats.put("totalOrders", orderService.countOrdersByUserId(userId));
stats.put("totalAmount", orderService.getTotalAmountByUserId(userId));
return new ResponseEntity<>(stats, HttpStatus.OK);
}
}
启动类 (Application.java)
package com.example.jpa;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@SpringBootApplication
@EnableJpaRepositories(basePackages = "com.example.jpa.repository")
@EnableJpaAuditing
public class SpringBootJpaApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootJpaApplication.class, args);
}
}
测试类 (UserControllerTest.java)
package com.example.jpa;
import com.example.jpa.entity.User;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@SpringBootTest
@AutoConfigureMockMvc
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@Test
void testCreateUser() throws Exception {
User user = new User();
user.setUsername("testuser");
user.setPassword("123456");
user.setEmail("test@example.com");
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(user)))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.username").value("testuser"))
.andExpect(jsonPath("$.email").value("test@example.com"));
}
@Test
void testGetAllUsers() throws Exception {
mockMvc.perform(get("/api/users")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$").isArray());
}
@Test
void testGetUserById() throws Exception {
mockMvc.perform(get("/api/users/1")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(1));
}
@Test
void testUpdateUser() throws Exception {
User user = new User();
user.setUsername("updateduser");
user.setEmail("updated@example.com");
user.setPhone("13800138000");
mockMvc.perform(put("/api/users/1")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(user)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.username").value("updateduser"))
.andExpect(jsonPath("$.email").value("updated@example.com"));
}
@Test
void testDeleteUser() throws Exception {
mockMvc.perform(delete("/api/users/1")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.message").value("用户删除成功"));
}
}
数据库初始化脚本 (schema.sql)
-- 创建数据库
CREATE DATABASE IF NOT EXISTS jpa_demo
DEFAULT CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
USE jpa_demo;
-- 用户表
CREATE TABLE IF NOT EXISTS users (
id BIGINT NOT NULL AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
password VARCHAR(100) NOT NULL,
email VARCHAR(100) NOT NULL,
phone VARCHAR(20),
address VARCHAR(200),
avatar VARCHAR(500),
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY uk_username (username),
UNIQUE KEY uk_email (email)
) ENGINE=InnoDB;
-- 角色表
CREATE TABLE IF NOT EXISTS roles (
id BIGINT NOT NULL AUTO_INCREMENT,
role_name VARCHAR(50) NOT NULL,
description VARCHAR(200),
PRIMARY KEY (id),
UNIQUE KEY uk_role_name (role_name)
) ENGINE=InnoDB;
-- 订单表
CREATE TABLE IF NOT EXISTS orders (
id BIGINT NOT NULL AUTO_INCREMENT,
order_number VARCHAR(50) NOT NULL,
user_id BIGINT NOT NULL,
total_amount DECIMAL(10,2) NOT NULL,
status VARCHAR(50) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY uk_order_number (order_number),
KEY idx_user_id (user_id),
CONSTRAINT fk_orders_user FOREIGN KEY (user_id) REFERENCES users(id)
) ENGINE=InnoDB;
-- 用户角色关联表
CREATE TABLE IF NOT EXISTS user_roles (
user_id BIGINT NOT NULL,
role_id BIGINT NOT NULL,
PRIMARY KEY (user_id, role_id),
KEY idx_role_id (role_id),
CONSTRAINT fk_user_roles_user FOREIGN KEY (user_id) REFERENCES users(id),
CONSTRAINT fk_user_roles_role FOREIGN KEY (role_id) REFERENCES roles(id)
) ENGINE=InnoDB;
项目结构
src/main/java/com/example/jpa/
├── Application.java
├── controller/
│ ├── UserController.java
│ └── OrderController.java
├── service/
│ ├── UserService.java
│ ├── OrderService.java
│ └── impl/
│ ├── UserServiceImpl.java
│ └── OrderServiceImpl.java
├── repository/
│ ├── UserRepository.java
│ └── OrderRepository.java
├── entity/
│ ├── User.java
│ ├── Role.java
│ └── Order.java
└── dto/
└── UserDTO.java
常用API测试
# 创建用户
curl -X POST http://localhost:8080/api/users \
-H "Content-Type: application/json" \
-d '{"username":"john","password":"123456","email":"john@example.com"}'
# 获取所有用户
curl http://localhost:8080/api/users
# 分页获取用户
curl "http://localhost:8080/api/users/page?page=0&size=2&sort=id,desc"
# 根据ID获取用户
curl http://localhost:8080/api/users/1
# 搜索用户
curl "http://localhost:8080/api/users/search?keyword=john"
# 更新用户
curl -X PUT http://localhost:8080/api/users/1 \
-H "Content-Type: application/json" \
-d '{"username":"john_updated","email":"john_updated@example.com"}'
# 删除用户
curl -X DELETE http://localhost:8080/api/users/1
这个完整的Spring Boot JPA案例涵盖了:
- 基本的CRUD操作
- 分页和排序
- 复杂查询(JPQL、原生SQL)
- 联表查询
- 事务管理
- 数据校验
- 单元测试
- 完整的RESTful API
你可以根据需要调整和扩展这些功能。