Initial commit
This commit is contained in:
@@ -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` 注解自定义 JPQL(Java 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` 中直接映射查询结果到 DTO(Data Transfer Object)或 VO(View 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!如果有进一步问题,欢迎继续提问。
|
||||
Reference in New Issue
Block a user