Skip to content

Spring Data JPA

什么是Spring Data JPA?

Spring Data JPA是Spring Data家族的一部分,专注于简化JPA (Java Persistence API) 的使用。它通过减少样板代码和提供通用的Repository接口,让开发者能够更专注于业务逻辑而不是数据访问细节。

主要特性

  1. 简化的Repository模式:提供了一系列Repository接口,自动实现基本的CRUD操作
  2. 方法名查询:根据方法名自动生成查询逻辑
  3. 自定义查询:支持@Query注解自定义JPQL或原生SQL查询
  4. 分页和排序:内置分页和排序支持
  5. 审计功能:自动管理创建时间、最后修改时间等审计字段
  6. 动态查询:提供Specification和Example API进行动态查询构建

设置与配置

Maven依赖

xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

基本配置(application.properties)

properties
# 数据源配置
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# JPA配置
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect

Java配置

java
@Configuration
@EnableJpaRepositories(basePackages = "com.example.repository")
@EntityScan(basePackages = "com.example.entity")
public class JpaConfig {
    // 可以在这里添加自定义配置
}

实体定义

java
@Entity
@Table(name = "users")
public class User {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(nullable = false, length = 100)
    private String name;
    
    @Column(unique = true, nullable = false)
    private String email;
    
    @CreatedDate
    @Column(updatable = false)
    private LocalDateTime createdAt;
    
    @LastModifiedDate
    private LocalDateTime updatedAt;
    
    // 构造函数、getter和setter方法
}

Repository定义

基本Repository

java
public interface UserRepository extends JpaRepository<User, Long> {
    // 不需要添加任何方法就已经包含了基本的CRUD操作
}

自定义查询方法

java
public interface UserRepository extends JpaRepository<User, Long> {
    // 方法名查询
    User findByEmail(String email);
    List<User> findByNameContaining(String name);
    
    // 组合条件查询
    List<User> findByNameAndEmail(String name, String email);
    List<User> findByCreatedAtBetween(LocalDateTime start, LocalDateTime end);
    
    // 排序
    List<User> findByNameOrderByCreatedAtDesc(String name);
    
    // 自定义查询
    @Query("SELECT u FROM User u WHERE u.email LIKE %:domain")
    List<User> findByEmailDomain(@Param("domain") String domain);
    
    // 原生SQL查询
    @Query(value = "SELECT * FROM users WHERE YEAR(created_at) = :year", nativeQuery = true)
    List<User> findUsersCreatedInYear(@Param("year") int year);
    
    // 更新查询
    @Modifying
    @Transactional
    @Query("UPDATE User u SET u.name = :name WHERE u.id = :id")
    int updateUserName(@Param("id") Long id, @Param("name") String name);
}

分页和排序

java
// Repository方法
Page<User> findByNameContaining(String name, Pageable pageable);

// 使用方式
Pageable pageable = PageRequest.of(0, 10, Sort.by("name").ascending());
Page<User> userPage = userRepository.findByNameContaining("john", pageable);

// 获取结果
List<User> users = userPage.getContent();
long totalElements = userPage.getTotalElements();
int totalPages = userPage.getTotalPages();

使用Specification进行动态查询

java
public List<User> findUsers(String name, String email) {
    return userRepository.findAll((Specification<User>) (root, query, criteriaBuilder) -> {
        List<Predicate> predicates = new ArrayList<>();
        
        if (name != null) {
            predicates.add(criteriaBuilder.like(root.get("name"), "%" + name + "%"));
        }
        
        if (email != null) {
            predicates.add(criteriaBuilder.equal(root.get("email"), email));
        }
        
        return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
    });
}

使用QueryByExample

java
User probe = new User();
probe.setName("John");

Example<User> example = Example.of(probe, 
    ExampleMatcher.matching()
        .withIgnoreCase()
        .withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING));

List<User> users = userRepository.findAll(example);

审计功能

要启用审计功能,需要:

java
@Configuration
@EnableJpaAuditing
public class AuditConfig {
    @Bean
    public AuditorAware<String> auditorProvider() {
        return () -> Optional.ofNullable(SecurityContextHolder.getContext())
                .map(SecurityContext::getAuthentication)
                .filter(Authentication::isAuthenticated)
                .map(Authentication::getName);
    }
}

然后在实体类上使用:

java
@EntityListeners(AuditingEntityListener.class)
@Entity
public class User {
    @CreatedDate
    private LocalDateTime createdAt;
    
    @LastModifiedDate
    private LocalDateTime updatedAt;
    
    @CreatedBy
    private String createdBy;
    
    @LastModifiedBy
    private String updatedBy;
}

关系映射

一对多关系

java
@Entity
public class Author {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;
    
    @OneToMany(mappedBy = "author", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Book> books = new ArrayList<>();
}

@Entity
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String title;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "author_id")
    private Author author;
}

多对多关系

java
@Entity
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;
    
    @ManyToMany
    @JoinTable(
        name = "student_course",
        joinColumns = @JoinColumn(name = "student_id"),
        inverseJoinColumns = @JoinColumn(name = "course_id")
    )
    private Set<Course> courses = new HashSet<>();
}

@Entity
public class Course {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;
    
    @ManyToMany(mappedBy = "courses")
    private Set<Student> students = new HashSet<>();
}

事务管理

Spring Data JPA集成了Spring的事务管理功能:

java
@Service
@Transactional
public class UserService {
    
    private final UserRepository userRepository;
    
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
    
    public User createUser(User user) {
        // 这个方法自动在事务中执行
        return userRepository.save(user);
    }
    
    @Transactional(readOnly = true)
    public List<User> findAllUsers() {
        // 只读事务
        return userRepository.findAll();
    }
    
    // 如果需要更精细的事务控制
    @Transactional(timeout = 10, isolation = Isolation.READ_COMMITTED)
    public void complexOperation() {
        // ...
    }
}

常见问题与最佳实践

  1. N+1查询问题:使用JOIN FETCH或EntityGraph来优化

    java
    @EntityGraph(attributePaths = {"books"})
    List<Author> findAll();
  2. 延迟加载:默认情况下@ManyToOne和@OneToOne是即时加载,@OneToMany和@ManyToMany是延迟加载,可以根据需要调整

  3. 乐观锁:使用@Version注解实现乐观锁

  4. 二级缓存:配置Hibernate二级缓存提升性能

  5. 批量操作:对于大量数据的插入或更新,考虑使用批处理

总结

Spring Data JPA通过提供高级抽象和自动化的Repository实现,极大地简化了数据访问层的开发工作。它不仅减少了样板代码,还提供了强大的查询功能和性能优化选项,适用于各种规模的应用程序。