Initial commit

This commit is contained in:
Docker7530
2026-03-01 01:43:46 +08:00
commit c6125c117b
3840 changed files with 415340 additions and 0 deletions
+425
View File
@@ -0,0 +1,425 @@
Spring Data JPA 提供了多种形式来操作数据库,旨在简化开发者与数据库的交互。以下是 Spring Data JPA 中操作数据库的主要形式,并附上示例说明。这些形式主要围绕其核心抽象——`Repository`(仓库)接口展开,同时结合不同的查询方式和功能。
### 1. **通过方法名自动生成查询**
这是 Spring Data JPA 最常用的方式,开发者只需在 Repository 接口中定义方法,方法名遵循特定的命名约定,Spring Data JPA 会自动根据方法名生成查询语句。
- **原理**Spring Data JPA 会解析方法名,根据命名规则(如 `findBy`, `existsBy`, `countBy` 等)生成对应的 SQL 或 JPQL 查询。
- **适用场景**:适用于简单的查询需求,例如按字段查找、统计、判断是否存在等。
- **示例**
假设有一个实体类 `User`
```java
package com.example.domain;
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
public class User {
@Id
private Long id;
private String name;
private Integer age;
// getters and setters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
```
定义 Repository 接口:
```java
package com.example.repository;
import com.example.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface UserRepository extends JpaRepository<User, Long> {
// 查找名字为指定值的用户
User findByName(String name);
// 查找年龄大于指定值的用户列表
List<User> findByAgeGreaterThan(Integer age);
// 判断是否存在指定名字的用户
boolean existsByName(String name);
// 统计指定年龄的用户数量
long countByAge(Integer age);
}
```
使用方式:
```java
@Autowired
private UserRepository userRepository;
public void test() {
// 查找名字为 "Alice" 的用户
User user = userRepository.findByName("Alice");
// 查找年龄大于 20 的用户
List<User> users = userRepository.findByAgeGreaterThan(20);
// 判断是否存在名字为 "Bob" 的用户
boolean exists = userRepository.existsByName("Bob");
// 统计年龄为 25 的用户数量
long count = userRepository.countByAge(25);
}
```
### 2. **使用 `@Query` 注解自定义查询**
当方法名自动生成的查询无法满足需求时,可以使用 `@Query` 注解自定义 JPQLJava Persistence Query Language)或原生 SQL 查询。
- **原理**:通过 `@Query` 注解直接指定查询语句,Spring Data JPA 不会解析方法名,而是使用注解中的查询逻辑。
- **适用场景**:适用于复杂的查询逻辑,例如多表关联、聚合查询、自定义条件等。
- **示例**
定义 Repository 接口:
```java
package com.example.repository;
import com.example.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface UserRepository extends JpaRepository<User, Long> {
// 使用 JPQL 查询名字包含指定关键字的用户
@Query("SELECT u FROM User u WHERE u.name LIKE %:keyword%")
List<User> findUsersByNameContaining(@Param("keyword") String keyword);
// 使用原生 SQL 查询
@Query(value = "SELECT * FROM user WHERE age > :age", nativeQuery = true)
List<User> findUsersOlderThan(@Param("age") Integer age);
}
```
使用方式:
```java
@Autowired
private UserRepository userRepository;
public void test() {
// 查找名字包含 "li" 的用户
List<User> usersWithKeyword = userRepository.findUsersByNameContaining("li");
// 查找年龄大于 30 的用户(使用原生 SQL)
List<User> olderUsers = userRepository.findUsersOlderThan(30);
}
```
### 3. **使用投影接口(Projection**
投影接口用于只查询实体中的部分字段,避免加载不需要的数据,从而优化性能。
- **原理**:定义一个接口,包含需要查询字段的 getter 方法,Spring Data JPA 会将查询结果映射到这个接口。
- **适用场景**:适用于只需要部分字段的场景,例如列表展示只需要 `id` 和 `name`,而不需要其他字段。
- **示例**
定义投影接口:
```java
package com.example.projection;
public interface UserProjection {
Long getId();
String getName();
}
```
定义 Repository 接口:
```java
package com.example.repository;
import com.example.domain.User;
import com.example.projection.UserProjection;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface UserRepository extends JpaRepository<User, Long> {
// 查询所有用户的 id 和 name
List<UserProjection> findAllProjectedBy();
// 查询指定年龄的用户的 id 和 name
List<UserProjection> findByAge(Integer age);
}
```
使用方式:
```java
@Autowired
private UserRepository userRepository;
public void test() {
// 查询所有用户的 id 和 name
List<UserProjection> projections = userRepository.findAllProjectedBy();
// 查询年龄为 25 的用户的 id 和 name
List<UserProjection> ageProjections = userRepository.findByAge(25);
}
```
### 4. **使用 DTO 投影(结合 `@Query`**
除了投影接口,也可以在 `@Query` 中直接映射查询结果到 DTOData Transfer Object)或 VOView Object)类。
- **原理**:在 `@Query` 中使用 `new` 关键字直接构造 DTO 对象,将查询结果映射到 DTO 的构造函数。
- **适用场景**:适用于需要返回自定义视图对象,且字段可能来自多表或复杂计算的场景。
- **示例**
定义 DTO 类:
```java
package com.example.dto;
public class UserDTO {
private Long id;
private String name;
public UserDTO(Long id, String name) {
this.id = id;
this.name = name;
}
// getters and setters
}
```
定义 Repository 接口:
```java
package com.example.repository;
import com.example.domain.User;
import com.example.dto.UserDTO;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
public interface UserRepository extends JpaRepository<User, Long> {
@Query("SELECT new com.example.dto.UserDTO(u.id, u.name) FROM User u WHERE u.age > :age")
List<UserDTO> findUserDTOsByAgeGreaterThan(Integer age);
}
```
使用方式:
```java
@Autowired
private UserRepository userRepository;
public void test() {
// 查询年龄大于 20 的用户的 id 和 name,返回 DTO
List<UserDTO> userDTOs = userRepository.findUserDTOsByAgeGreaterThan(20);
}
```
### 5. **使用分页和排序(Paging and Sorting**
Spring Data JPA 提供了内置的分页和排序功能,适用于大数据量场景下的分页展示。
- **原理**:通过 `Pageable` 对象指定分页参数和排序规则,Spring Data JPA 自动处理分页查询和结果封装。
- **适用场景**:适用于列表分页展示、数据量大时分批加载。
- **示例**
定义 Repository 接口:
```java
package com.example.repository;
import com.example.domain.User;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface UserRepository extends JpaRepository<User, Long> {
// 分页查询所有用户
Page<User> findAll(Pageable pageable);
// 分页查询指定年龄的用户
Page<User> findByAge(Integer age, Pageable pageable);
// 查询并排序(不分页)
List<User> findByAgeOrderByNameAsc(Integer age);
}
```
使用方式:
```java
@Autowired
private UserRepository userRepository;
public void test() {
// 创建分页参数,第 1 页,每页 10 条,按 name 升序排序
Pageable pageable = PageRequest.of(0, 10, Sort.by("name").ascending());
// 分页查询所有用户
Page<User> userPage = userRepository.findAll(pageable);
// 获取查询结果
List<User> users = userPage.getContent();
long totalElements = userPage.getTotalElements();
int totalPages = userPage.getTotalPages();
// 分页查询年龄为 25 的用户
Page<User> agePage = userRepository.findByAge(25, pageable);
// 查询年龄为 25 的用户并按名字升序排序(不分页)
List<User> sortedUsers = userRepository.findByAgeOrderByNameAsc(25);
}
```
### 6. **使用 Specification 或 Criteria 查询**
Spring Data JPA 提供了 `JpaSpecificationExecutor` 接口,允许开发者动态构建复杂的查询条件(类似 Hibernate 的 Criteria API)。
- **原理**:通过实现 `Specification` 接口,动态拼接查询条件,适用于运行时根据用户输入构建查询。
- **适用场景**:适用于动态查询条件,例如搜索表单中用户可以选择多个过滤条件。
- **示例**
定义 Repository 接口:
```java
package com.example.repository;
import com.example.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
}
```
定义动态查询逻辑:
```java
@Autowired
private UserRepository userRepository;
public List<User> searchUsers(String name, Integer minAge) {
Specification<User> spec = (root, query, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
if (name != null && !name.isEmpty()) {
predicates.add(criteriaBuilder.like(root.get("name"), "%" + name + "%"));
}
if (minAge != null) {
predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("age"), minAge));
}
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
};
return userRepository.findAll(spec);
}
```
### 7. **使用预定义的 CRUD 方法**
Spring Data JPA 提供了 `CrudRepository` 和 `JpaRepository` 接口,内置了许多常用的 CRUD 方法(如 `save`, `findById`, `delete` 等),可以直接使用。
- **原理**:这些方法由 Spring Data JPA 自动实现,无需开发者额外定义。
- **适用场景**:适用于基本的增删改查操作。
- **示例**
定义 Repository 接口:
```java
package com.example.repository;
import com.example.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long> {
}
```
使用方式:
```java
@Autowired
private UserRepository userRepository;
public void test() {
// 保存用户
User user = new User();
user.setName("Alice");
user.setAge(25);
userRepository.save(user);
// 根据 ID 查询用户
Optional<User> optionalUser = userRepository.findById(1L);
if (optionalUser.isPresent()) {
User foundUser = optionalUser.get();
// 处理 foundUser
}
// 删除用户
userRepository.deleteById(1L);
// 查询所有用户
List<User> allUsers = userRepository.findAll();
}
```
### 8. **使用原生 SQL 查询(Native Query**
除了 JPQL,还可以通过 `@Query` 注解使用原生 SQL 查询,直接操作数据库。
- **原理**:设置 `nativeQuery = true`Spring Data JPA 会将查询作为原生 SQL 执行。
- **适用场景**:适用于需要特定数据库功能或性能优化的复杂查询。
- **示例**
```java
@Query(value = "SELECT id, name FROM user WHERE age > :age", nativeQuery = true)
List<Object[]> findUserDataByAge(@Param("age") Integer age);
```
### 总结
Spring Data JPA 操作数据库的主要形式包括:
1. **方法名自动生成查询**:通过命名约定生成简单查询。
2. **@Query 注解自定义查询**:支持 JPQL 和原生 SQL,适用于复杂查询。
3. **投影接口(Projection)**:只查询部分字段,优化性能。
4. **DTO 投影**:结合 `@Query` 将结果映射到自定义 DTO。
5. **分页和排序**:内置分页和排序功能,处理大数据量。
6. **Specification/Criteria 查询**:动态构建查询条件,适用于搜索场景。
7. **预定义 CRUD 方法**:直接使用内置的增删改查方法。
8. **原生 SQL 查询**:直接使用数据库特定的 SQL 语句。
这些方式覆盖了从简单到复杂的各种数据库操作需求,开发者可以根据具体场景选择合适的方式。希望这些示例能帮助你更好地理解和应用 Spring Data JPA!如果有进一步问题,欢迎继续提问。