JPA 的查詢方式
- 1.使用約定方法名
- 2.用 JPQL 進行查詢
- 3.用原生 SQL 進行查詢
- 3.1 根據 ID 查詢用戶
- 3.2 查詢所有用戶
- 3.3 根據 email 查詢用戶
- 3.4 根據 name 查詢用戶,并返回分頁對象 Page
- 3.5 根據名字來修改 email 的值
- 3.6 使用事務
- 4.用 Specifications 進行查詢
- 5.用 ExampleMatcher 進行查詢
- 6.用謂語 QueryDSL 進行查詢
- 7.用 NamedQuery 進行查詢
1.使用約定方法名
約定方法名一定要根據命名規范來寫,Spring Data 會根據前綴、中間連接詞(Or
、And
、Like
、NotNull
等類似 SQL 中的關鍵詞)、內部拼接 SQL 代理生成方法的實現。
約定方法名的方法見下表(不完全示例):
| | |
---|---|---|
and | findByLastnameAndFirstname | where x.lastname = ?1 and x.firstname = ?2 |
or | findByLastnameOrFirstname | where x.lastname = ?1 or x.firstname = ?2 |
= | findByFirstname / findByFirstnameIs / findByFirstnameEquals | where x.firstname = ?1 |
接口方法的命名規則也很簡單,只要明白 And
、Or
、Is
、Equal
、Greater
、StartingWith
等英文單詞的含義,就可以寫接口方法。具體用法如下:
public interface UserRepository extends Repository<User, Long> {List<User> findByEmailOrName(String email, String name);
}
上述代碼表示,通過 email
或 name
來查找 User 對象。
約定方法名還可以支持以下幾種語法:
User findFirstByOrderByNameAsc()
Page<User> queryFirst100ByName(String name, Pageable pageable)
Slice<User> findTop100ByName(String name, Pageable pageable)
List<User> findFirst100ByName(String name, Sort sort)
List<User> findTop100ByName(String name, Pageable pageable)
2.用 JPQL 進行查詢
JPQL 語言(Java Persistence Query Language
)是一種和 SQL 非常類似的中間性和對象化查詢語言,它最終會被編譯成針對不同底層數據庫的 SQL 語言,從而屏蔽不同數據庫的差異。
JPQL 語言通過 Query 接口封裝執行,Query 接口封裝了執行數據庫查詢的相關方法。調用 EntityManager 的 Query、NamedQuery 及 NativeQuery 方法可以獲得查詢對象,進而可調用 Query 接口的相關方法來執行查詢操作。
JPQL 是面向對象進行查詢的語言,可以通過自定義的 JPQL 完成 UPDATE 和 DELETE 操作。JPQL 不支持使用 INSERT。對于 UPDATE 或 DELETE 操作,必須使用注解 @Modifying 進行修飾。
JPQL 的用法見以下兩段代碼。
(1)下面代碼表示根據 name
值進行查找。
public interface UserRepository extends JpaRepository<User, Long> (@Query("select u from User u where u.name = ?1")User findByName(String name);
}
(2)下面代碼表示根據 name
值進行模糊查找。
public interface UserRepository extends JpaRepository<User, Long> { @Query("select u from User u where u.name like %?1")List<User> findByName(String name);
}
3.用原生 SQL 進行查詢
在使用原生 SQL 查詢時,也使用注解 @Query。此時,nativeQuery 參數需要設置為 true
。 下面先看一些簡單的查詢代碼。
3.1 根據 ID 查詢用戶
public interface UserRepository extends JpaRepository<User, Long> (// 根據 ID 查詢用戶@Query(value = "select * from user u where u.id=:id", nativeQuery = true)User findById(@Param("id")Long id);
)
3.2 查詢所有用戶
public interface UserRepository extends JpaRepository<User, Long> {// 查詢所有用戶@Query(value = "select * from user", nativeQuery = true)List<User> findAllNative();
}
3.3 根據 email 查詢用戶
public interface UserRepository extends JpaRepository<User, Long> (// 根據 email 查詢用戶@Query(value = "select * from user where email= ?1", nativeQuery = true) User findByEmail(String email);
)
3.4 根據 name 查詢用戶,并返回分頁對象 Page
public interface UserRepository extends JpaRepository<User, Long> (@Query(value = "select * from user where name= ?1",countQuery = "select count(*) from user where name= ?1", nativeQuery = true)Page<User> findByName(String name, Pageable pageable);
}
3.5 根據名字來修改 email 的值
@Modifying
@Query("update user set email = :email where name =:name")
Void updateUserEmailByName(@Param("name")String name, @Param("email")String email);
3.6 使用事務
UPDATE 或 DELETE 操作需要使用事務。此時需要先定義 Service 層,然后在 Service 層的方法上添加事務操作。
對于自定義的方法,如果需要改變 Spring Data 提供的事務默認方式,則可以在方法上使用注解 @Transactional,如以下代碼:
@Service
public classUserService {@Autowiredprivate UserRepository userRepository;@Transactionalpublic void updateEmailByName(String name, String email) {userRepository.updateUserEmaiByName(name, email);}
)
測試代碼:
@Test
public void testUsingModifingAnnotation() {userService.updateEmailByName("pipi", "88888888@qq.com");
}
在進行多個 Repository 操作時,也應該使這些操作在同一個事務中。按照分層架構的思想,這些操作屬于業務邏輯層,因此需要在 Service 層實現對多個 Repository 的調用,并在相應的方法上聲明事務。
4.用 Specifications 進行查詢
如果要使 Repository 支持 Specification 查詢,則需要在 Repository 中繼承 JpaSpecificationExecutor 接口,具體使用見如下代碼:
public interface CardRepository extends JpaRepository<Card,Long>, JpaSpecificationExecutor<Card> {Card findById(long id);
}
下面以一個例子來說明 Specifications 的具體用法:
@SpringBootTest
@RunWith(SpringRunner.class)
public class testJpaSpecificationExecutor (@Autowiredprivate CardRepository cardRepository;@Testpublic void testJpaSpecificationExecutor() (int pageNo = 0;int pageSize = 5;PageRequest pageable = PageRequest.of(pageNo, pageSize);// 通常使用 Specification 的匿名內部類Specification<Card> specification = new Specification<Card>() {@Overridepublic Predicate toPredicate(Root<Card> root, CriteriaQuery<?> query, CriteriaBuilder cb) {Path path = root.get("id");// gt 是大于的意思。這里代表 id 大于 2Predicate predicate1 = cb.gt(path, 2);// equal 是等于的意思,代表查詢 num 值為 422803 的數據記錄Predicate predicate2 = cb.equal(root.get("num"), 422803);// 構建組合的 PredicatePredicate predicate = cb.and(predicate1, predicate2); return predicate;}};Page<Card> page = cardRepository.findAll(specification, pageable);System.out.println("總記錄數: " + page.getTotalElements());System.out.println("當前第: " + (page.getNumber() + 1) + "頁");System.out.println("總頁數: " + page.getTotalPages());System.out.println("當前頁面的 List: " + page.getContent());System.out.println("當前頁面的記錄數: "+ page.getNumberOfElements());}
)
代碼解釋如下:
- CriteriaQuery 接口:
specific
的頂層查詢對象,它包含查詢的各個部分,比如,select
、from
、where
、group by
、order by
等。CriteriaQuery 對象只對實體類型或嵌入式類型的 Criteria 查詢起作用。 root
:代表查詢的實體類是 Criteria 查詢的根對象。Criteria 查詢的根定義了實體類型,能為將來的導航獲得想要的結果。它與 SQL 查詢中的 From 子句類似。Root 實例是類型化的, 且規定了 From 子句中能夠出現的類型。查詢根實例通過傳入一個實體類型給 AbstractQuery.from 方法獲得。query
:可以從中得到 Root 對象,即告知 JPA Criteria 查詢要查詢哪一個實體類。還可以添加查詢條件,并結合 EntityManager 對象得到最終查詢的 TypedQuery 對象。- CriteriaBuilder 對象:用于創建 Criteria 相關對象的工廠,可以從中獲取到 Predicate 對象。
- Predicate 類型:代表一個查詢條件。
運行上面的測試代碼,在控制臺會輸出如下結果(確保數據庫已經存在數據):
Hibernate: select card0_.id as id1_0_, card0_.num as num2_0_ from card cardO_ where card0_.id>2 and card0_.num=422803 limit ?
Hibernate: select count(card0_.id) as col_0_0_ from card card0_ where card0_.id>2 and card0_.num=422803
總記錄數: 6
當前第: 1頁
總頁數: 2
當前頁面的 List: [Card(id=4, num=422803), Card(id=8, num=422803), Card(id=10, num=422803), Card(id=20, num=422803), Card(id=23, num=422803)]
當前頁面的記錄數: 5
5.用 ExampleMatcher 進行查詢
Spring Data 可以通過 Example 對象來構造 JPQL 查詢,具體用法見以下代碼:
User user = new User();
//構建查詢條件
user.setName("pipi");
ExampleMatcher matcher = ExampleMatcher.matching()// 創建一個 ExampleMatcher, 不區分大小寫匹配 name.withIgnorePaths("name")// 包括 null 值.withIncludeNullValues()// 執行后綴匹配.withStringMatcherEnding();
// 通過 Example 構建查詢
Example<User> example = Example.of(user, matcher);
List<User> list = userRepository.findALL(example);
默認情況下,ExampleMatcher 會匹配所有字段。
可以指定單個屬性的行為(如 name
或內嵌屬性 name.user
)。如:
withMatcher("name", endsWith())
withMatcher("name", startsWith().ignoreCase())
6.用謂語 QueryDSL 進行查詢
QueryDSL 也是基于各種 ORM 之上的一個通用查詢框架,它與 Spring Data JPA 是同級別的。使用 QueryDSL 的 API 可以寫出 SQL 語句(Java 代碼,非真正標準 SQL),不需要懂 SQL 語句。它能夠構建類型安全的查詢。這與 JPA 使用原生查詢時有很大的不同,可以不必再對 Object[]
進行操作。它還可以和 JPA 聯合使用。
7.用 NamedQuery 進行查詢
官方不推薦使用 NamedQuery,因為它的代碼必須寫在實體類上面,這樣不夠獨立。其使用方法見以下代碼:
?Entity
@NamedQuery(name = "User.findByName", query = "select u from User u where u.name = ?1")
public class User {
}