本文目录导读:

- 项目环境配置
- 实体类设计
- 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
你可以根据需要调整和扩展这些功能。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播