前言
Spring Data JPA 是基于 JPA 规范的数据访问框架,简化了数据库操作。它通过方法命名约定自动生成查询,让数据访问变得简单优雅。
快速开始
1. 添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
2. 配置数据库
spring:
datasource:
url: jdbc:mysql://localhost:3306/demo?useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
maximum-pool-size: 10
jpa:
hibernate:
ddl-auto: update
show-sql: true
properties:
hibernate:
format_sql: true
dialect: org.hibernate.dialect.MySQLDialect
3. 创建实体类
package com.example.demo.entity;
import jakarta.persistence.*;
import lombok.Data;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import java.time.LocalDateTime;
@Data
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 50)
private String username;
@Column(unique = true, length = 100)
private String email;
private Integer age;
@CreationTimestamp
@Column(updatable = false)
private LocalDateTime createTime;
@UpdateTimestamp
private LocalDateTime updateTime;
}
4. 创建 Repository
package com.example.demo.repository;
import com.example.demo.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// 方法命名查询
Optional<User> findByUsername(String username);
Optional<User> findByEmail(String email);
List<User> findByAgeGreaterThan(int age);
List<User> findByUsernameContaining(String keyword);
}
5. 创建 Service
package com.example.demo.service;
import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
public User findById(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> BusinessException.notFound("用户"));
}
public List<User> findAll() {
return userRepository.findAll();
}
public User create(User user) {
return userRepository.save(user);
}
public User update(Long id, User user) {
User existing = findById(id);
existing.setUsername(user.getUsername());
existing.setEmail(user.getEmail());
return userRepository.save(existing);
}
public void delete(Long id) {
userRepository.deleteById(id);
}
}
查询方法
1. 方法命名约定
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// 等于
User findByUsername(String username);
// 不等于
User findByUsernameNot(String username);
// 大于
List<User> findByAgeGreaterThan(int age);
// 小于
List<User> findByAgeLessThan(int age);
// 包含
List<User> findByUsernameContaining(String keyword);
// 以...开头
List<User> findByUsernameStartingWith(String prefix);
// 以...结尾
List<User> findByUsernameEndingWith(String suffix);
// IN 查询
List<User> findByUsernameIn(List<String> usernames);
// 是否为空
List<User> findByEmailIsNull();
List<User> findByEmailIsNotNull();
// 布尔值
List<User> findByEnabledTrue();
// 排序
List<User> findByAgeOrderByAgeDesc();
// 分页
Page<User> findByAge(int age, Pageable pageable);
}
2. @Query 自定义查询
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
/**
* JPQL 查询
*/
@Query("SELECT u FROM User u WHERE u.username = :username")
User findByUsernameJPQL(@Param("username") String username);
/**
* 原生 SQL 查询
*/
@Query(value = "SELECT * FROM user WHERE username = :username", nativeQuery = true)
User findByUsernameNative(@Param("username") String username);
/**
* 更新操作
*/
@Modifying
@Transactional
@Query("UPDATE User u SET u.email = :email WHERE u.id = :id")
int updateEmail(@Param("id") Long id, @Param("email") String email);
/**
* 删除操作
*/
@Modifying
@Transactional
@Query("DELETE FROM User u WHERE u.age < :age")
int deleteByAgeLessThan(@Param("age") int age);
}
3. 复杂查询
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
/**
* 多条件查询
*/
List<User> findByUsernameAndAge(String username, int age);
List<User> findByUsernameOrEmail(String username, String email);
/**
* 组合条件
*/
List<User> findByAgeGreaterThanAndUsernameContaining(int age, String keyword);
/**
* 子查询
*/
@Query("SELECT u FROM User u WHERE u.age > (SELECT AVG(u2.age) FROM User u2)")
List<User> findAboveAverageAge();
}
分页排序
1. 分页查询
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
public Page<User> pageQuery(int page, int size) {
Pageable pageable = PageRequest.of(page, size);
return userRepository.findAll(pageable);
}
public Page<User> pageQueryCondition(String username, int page, int size) {
Pageable pageable = PageRequest.of(page, size);
return userRepository.findByUsernameContaining(username, pageable);
}
}
2. 排序
@Service
@RequiredArgsConstructor
public class UserService {
/**
* 简单排序
*/
public List<User> sortByAge() {
return userRepository.findAll(Sort.by(Sort.Direction.ASC, "age"));
}
/**
* 多字段排序
*/
public List<User> sortByMultiple() {
return userRepository.findAll(
Sort.by(Sort.Direction.DESC, "createTime")
.and(Sort.by(Sort.Direction.ASC, "age"))
);
}
/**
* 分页 + 排序
*/
public Page<User> pageWithSort(int page, int size) {
Pageable pageable = PageRequest.of(page, size, Sort.by("createTime").descending());
return userRepository.findAll(pageable);
}
}
关联映射
1. 一对多
// 用户(一方)
@Data
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Order> orders = new ArrayList<>();
}
// 订单(多方)
@Data
@Entity
@Table(name = "orders")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String orderNo;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
}
2. 多对多
// 用户
@Data
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
@ManyToMany
@JoinTable(
name = "user_role",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id")
)
private List<Role> roles = new ArrayList<>();
}
// 角色
@Data
@Entity
@Table(name = "role")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany(mappedBy = "roles")
private List<User> users = new ArrayList<>();
}
3. 一对一
// 用户
@Data
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
@OneToOne(mappedBy = "user", cascade = CascadeType.ALL)
private UserProfile profile;
}
// 用户详情
@Data
@Entity
@Table(name = "user_profile")
public class UserProfile {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String phone;
private String address;
@OneToOne
@JoinColumn(name = "user_id")
private User user;
}
审计功能
1. 启用审计
@Configuration
@EnableJpaAuditing
public class JpaConfig {
}
2. 实体类使用
@Data
@Entity
@EntityListeners(AuditingEntityListener.class)
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
@CreatedDate
@Column(updatable = false)
private LocalDateTime createTime;
@LastModifiedDate
private LocalDateTime updateTime;
@CreatedBy
private String createBy;
@LastModifiedBy
private String updateBy;
}
3. 配置审计提供者
@Configuration
@EnableJpaAuditing
public class JpaConfig implements AuditorAware<String> {
@Override
public Optional<String> getCurrentAuditor() {
// 从安全上下文获取当前用户
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()) {
return Optional.empty();
}
return Optional.of(authentication.getName());
}
}
事务管理
1. 声明式事务
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
/**
* 默认事务
*/
@Transactional
public User create(User user) {
return userRepository.save(user);
}
/**
* 只读事务
*/
@Transactional(readOnly = true)
public User findById(Long id) {
return userRepository.findById(id).orElse(null);
}
/**
* 指定隔离级别
*/
@Transactional(isolation = Isolation.READ_COMMITTED)
public void transfer(Long fromId, Long toId, BigDecimal amount) {
// 转账逻辑
}
/**
* 指定超时时间
*/
@Transactional(timeout = 30)
public void batchInsert(List<User> users) {
for (User user : users) {
userRepository.save(user);
}
}
}
2. 事务传播
@Service
public class OrderService {
@Autowired
private UserService userService;
/**
* REQUIRED - 默认,加入现有事务或创建新事务
*/
@Transactional(propagation = Propagation.REQUIRED)
public void createOrder() {
// 创建订单
userService.updateUser(); // 加入同一事务
}
/**
* REQUIRES_NEW - 总是创建新事务
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logOrder() {
// 记录日志,独立事务
}
}
最佳实践
1. 使用 DTO
// ✅ 推荐
@Service
public class UserService {
public UserDTO findById(Long id) {
User user = userRepository.findById(id)
.orElseThrow(() -> BusinessException.notFound("用户"));
return convertToDTO(user);
}
private UserDTO convertToDTO(User user) {
return new UserDTO(user.getId(), user.getUsername(), user.getEmail());
}
}
// ❌ 不推荐
@Service
public class UserService {
public User findById(Long id) {
return userRepository.findById(id).orElse(null);
}
}
2. 懒加载处理
// ✅ 推荐 - 使用@JsonIgnore
@Data
@Entity
public class User {
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
@JsonIgnore
private List<Order> orders;
}
// ✅ 推荐 - 使用 DTO
@Service
public class UserService {
public UserDTO findById(Long id) {
User user = userRepository.findById(id).orElse(null);
return new UserDTO(user.getId(), user.getUsername());
}
}
3. N+1 问题
// ❌ 不推荐 - N+1 问题
List<User> users = userRepository.findAll();
for (User user : users) {
user.getOrders().size(); // 每次访问都会查询数据库
}
// ✅ 推荐 - JOIN FETCH
@Query("SELECT u FROM User u JOIN FETCH u.orders")
List<User> findAllWithOrders();
总结
Spring Data JPA 要点:
- ✅ Repository - JpaRepository、方法命名查询
- ✅ @Query - JPQL、原生 SQL
- ✅ 分页排序 - Pageable、Sort
- ✅ 关联映射 - 一对多、多对多、一对一
- ✅ 审计功能 - @CreatedDate、@LastModifiedDate
- ✅ 事务管理 - @Transactional
Spring Data JPA 让数据访问更加优雅简洁。