SpringData JPA進階查詢—JPQL/原生SQL查詢、分頁處理、部分字段映射查詢


上一篇介紹了入門基礎篇SpringDataJPA訪問數據庫。本篇介紹SpringDataJPA進一步的定制化查詢,使用JPQL或者SQL進行查詢、部分字段映射、分頁等。本文盡量以簡單的建模與代碼進行展示操作,文章比較長,包含查詢的方方面面。如果能耐心看完這篇文章,你應該能使用SpringDataJPA應對大部分的持久層開發需求。如果你需要使用到動態條件查詢,請查看下一篇博客,專題介紹SpringDataJPA的動態查詢。


一、入門引導與准備


JPQL(JavaPersistence Query Language)是一種面向對象的查詢語言,它在框架中最終會翻譯成為sql進行查詢,如果不知JPQL請大家自行谷歌了解一下,如果你會SQL,了解這個應該不廢吹灰之力。

1.核心注解@Query介紹


使用SpringDataJPA進行JPQL/SQL一般查詢的核心是@Query注解,我們先來看看該注解


   
   
  
  
          
  1. @Retention(RetentionPolicy.RUNTIME)
  2. @Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
  3. @QueryAnnotation
  4. @Documented
  5. public @interface Query {
  6. String value() default "";
  7. String countQuery() default "";
  8. String countProjection() default "";
  9. boolean nativeQuery() default false;
  10. String name() default "";
  11. String countName() default "";
  12. }
該注解使用的注解位置為方法、注解類型,一般我們用於注解方法即可。@QueryAnnotation標識這是一個查詢注解;

@Query注解中有6個參數,value參數是我們需要填入的JPQL/SQL查詢語句;nativeQuery參數是標識該查詢是否為原生SQL查詢,默認為false;countQuery參數為當你需要使用到分頁查詢時,可以自己定義(count查詢)計數查詢的語句,如果該項為空但是如果要用到分頁,那么就使用默認的主sql條件來進行計數查詢;name參數為命名查詢需要使用到的參數,一般配配合@NamedQuery一起使用,這個在后面會說到;countName參數作用與countQuery相似,但是使用的是命名查詢的(count查詢)計數查詢語句;countProjection為涉及到投影部分字段查詢時的計數查詢(count查詢);關於投影查詢,待會會說到。

有了@Query基礎后,我們就可以小試牛刀一把了,對於jar包依賴,我們用的依舊是上一節的依賴,代碼如下:

2.准備實驗環境



   
   
  
  
          
  1. <parent>
  2. <groupId>org.springframework.boot </groupId>
  3. <artifactId>spring-boot-starter-parent </artifactId>
  4. <version>1.4.1.RELEASE </version>
  5. </parent>
  6. <properties>
  7. <project.build.sourceEncoding>UTF-8 </project.build.sourceEncoding>
  8. <java.version>1.8 </java.version>
  9. <springBoot.groupId>org.springframework.boot </springBoot.groupId>
  10. </properties>
  11. <dependencies>
  12. <!-- SpringBoot Start -->
  13. <dependency>
  14. <groupId>${springBoot.groupId} </groupId>
  15. <artifactId>spring-boot-starter-web </artifactId>
  16. </dependency>
  17. <!-- jpa -->
  18. <dependency>
  19. <groupId>${springBoot.groupId} </groupId>
  20. <artifactId>spring-boot-starter-data-jpa </artifactId>
  21. </dependency>
  22. <dependency>
  23. <groupId>${springBoot.groupId} </groupId>
  24. <artifactId>spring-boot-starter-test </artifactId>
  25. </dependency>
  26. <!-- mysql -->
  27. <dependency>
  28. <groupId>mysql </groupId>
  29. <artifactId>mysql-connector-java </artifactId>
  30. </dependency>
  31. <dependency>
  32. <groupId>junit </groupId>
  33. <artifactId>junit </artifactId>
  34. <version>4.12 </version>
  35. </dependency>
  36. </dependencies>

項目結構如下:


JpaConfiguration配置類與上篇的相同:


   
   
  
  
          
  1. @Order(Ordered.HIGHEST_PRECEDENCE)
  2. @Configuration
  3. @EnableTransactionManagement(proxyTargetClass= true)
  4. @EnableJpaRepositories(basePackages={ "org.fage.**.repository"})
  5. @EntityScan(basePackages={ "org.fage.**.entity"})
  6. public class JpaConfiguration {
  7. @Bean
  8. PersistenceExceptionTranslationPostProcessor persistenceExceptionTranslationPostProcessor(){
  9. return new PersistenceExceptionTranslationPostProcessor();
  10. }
  11. }
App類:


   
   
  
  
          
  1. @SpringBootApplication
  2. @ComponentScan( "org.fage.**")
  3. public class App {
  4. public static void main(String[] args) throws Exception {
  5. SpringApplication.run(App.class, args);
  6. }
  7. }

對於實體建模依舊用到上一篇所用的模型Department、User、Role,Department與User為一對多,User與Role為多對多,為了方便后面介紹投影,user多增加幾個字段,代碼如下:


   
   
  
  
          
  1. @Entity
  2. @Table(name = "user")
  3. public class User implements Serializable {
  4. private static final long serialVersionUID = - 7237729978037472653L;
  5. @Id
  6. @GeneratedValue(strategy = GenerationType.IDENTITY)
  7. private Long id;
  8. private String name;
  9. private String password;
  10. @Column(name = "create_date")
  11. @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
  12. @Temporal(TemporalType.TIMESTAMP)
  13. private Date createDate;
  14. private String email;
  15. // 一對多映射
  16. @ManyToOne
  17. @JoinColumn(name = "department_id")
  18. private Department department;
  19. // 多對多映射
  20. @ManyToMany @JsonBackReference
  21. @JoinTable(name = "user_role", joinColumns = { @JoinColumn(name = "user_id") }, inverseJoinColumns = {
  22. @JoinColumn(name = "role_id") })
  23. private List<Role> roles;
  24. //getter and setter .....
  25. }



   
   
  
  
          
  1. @Entity
  2. @Table(name = "department")
  3. public class Department implements Serializable {
  4. /**
  5. *
  6. */
  7. private static final long serialVersionUID = 3743774627141615707L;
  8. @Id
  9. @GeneratedValue(strategy=GenerationType.IDENTITY)
  10. private Long id;
  11. private String name;
  12. @OneToMany(mappedBy = "department")@JsonBackReference
  13. @JsonBackReferenceprivate List <User> users;
  14. //getter and setter
  15. }


   
   
  
  
          
  1. @Entity
  2. @Table(name= "role")
  3. public class Role implements Serializable{
  4. /**
  5. *
  6. */
  7. private static final long serialVersionUID = 1366815546093762449L;
  8. @Id
  9. @GeneratedValue(strategy=GenerationType.IDENTITY)
  10. private Long id;
  11. private String name;
  12. //getter and setter
  13. }


建模成功時,生成的表結構如下:


對於Repository:


   
   
  
  
          
  1. @Repository
  2. public interface DepartmentRepository extends JpaRepository<Department, Long>{}

   
   
  
  
          
  1. @Repository
  2. public interface RoleRepository extends JpaRepository<Role, Long>{}

   
   
  
  
          
  1. @Repository
  2. public interface UserRepository extends JpaRepository<User, Long>{
  3. }

如果以上代碼有看不懂的地方,請移步到上一篇一覽基礎篇。至此,我們已經將環境整理好了,至於表中的數據插入,希望各位參考上一篇文章進行基礎的crud操作將表中數據進行填充,接下來介紹@Query查詢


二、使用JPQL查詢


1 .核心查詢與測試樣例


在UserRepository中增加以下方法:


   
   
  
  
          
  1. //--------------JPQL查詢展示-------------//
  2. //展示位置參數綁定
  3. @Query(value = "from User u where u.name=?1 and u.password=?2")
  4. User findByNameAndPassword(String name, String password);
  5. //展示名字參數綁定
  6. @Query(value = "from User u where u.name=:name and u.email=:email")
  7. User findByNameAndEmail(@Param("name")String name, @Param("email")String email);
  8. //展示like模糊查詢
  9. @Query(value = "from User u where u.name like %:nameLike%")
  10. List<User> findByNameLike(@Param("nameLike")String nameLike);
  11. //展示時間間隔查詢
  12. @Query(value = "from User u where u.createDate between :start and :end")
  13. List<User> findByCreateDateBetween(@Param("start")Date start, @Param("end")Date end);
  14. //展示傳入集合參數查詢
  15. @Query(value = "from User u where u.name in :nameList")
  16. List<User> findByNameIn(@Param("nameList")Collection<String> nameList);
  17. //展示傳入Bean進行查詢(SPEL表達式查詢)
  18. @Query(value = "from User u where u.name=:#{#usr.name} and u.password=:#{#usr.password}")
  19. User findByNameAndPassword(@Param("usr")User usr);
  20. //展示使用Spring自帶分頁查詢
  21. @Query( "from User u")
  22. Page<User> findAllPage(Pageable pageable);
  23. //展示帶有條件的分頁查詢
  24. @Query(value = "from User u where u.email like %:emailLike%")
  25. Page<User> findByEmailLike(Pageable pageable, @Param("emailLike")String emailLike);
TestClass的代碼如下:


   
   
  
  
          
  1. @RunWith(SpringRunner.class)
  2. @SpringBootTest
  3. public class TestClass {
  4. final Logger logger = LoggerFactory.getLogger(TestClass.class);
  5. @Autowired
  6. UserRepository userRepository;
  7. @Test
  8. public void testfindByNameAndPassword(){
  9. userRepository.findByNameAndPassword( "王大帥", "123");
  10. }
  11. @Test
  12. public void testFindByNameAndEmail(){
  13. userRepository.findByNameAndEmail( "張大仙", "2@qq.com");
  14. }
  15. @Test
  16. public void testFindByNameLike(){
  17. List<User> users = userRepository.findByNameLike( "馬");
  18. logger.info(users.size() + "----");
  19. }
  20. @Test
  21. public void testFindByCreateDateBetween() throws ParseException{
  22. List<User> users = userRepository.findByCreateDateBetween( new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss").parse( "2018-01-01 00:00:00"), new Date(System.currentTimeMillis()));
  23. logger.info(users.size() + "----");
  24. }
  25. @Test
  26. public void testFindByNameIn(){
  27. List<String> list = new ArrayList<String>();
  28. list.add( "王大帥");
  29. list.add( "李小三");
  30. userRepository.findByNameIn(list);
  31. }
  32. @Test
  33. public void testfindByNameAndPasswordEntity(){
  34. User u = new User();
  35. u.setName( "李小三");
  36. u.setPassword( "444");
  37. userRepository.findByNameAndPassword(u);
  38. }
  39. @Test
  40. public void testFindAllPage(){
  41. Pageable pageable = new PageRequest( 0, 5);
  42. Page<User> page = userRepository.findAllPage(pageable);
  43. ObjectMapper mapper = new ObjectMapper();
  44. String json = mapper.writeValueAsString(page); 
  45. logger.info(json);
  46. }
  47. @Test
  48. public void findByEmailLike(){
  49. Pageable pageable = new PageRequest( 0, 5, new Sort(Direction.ASC, "id"));
  50. userRepository.findByEmailLike(pageable, "@qq.com");
  51. }
  52. }
至此,顯示了使用JPQL進行單表查詢的絕大多數操作,當你在實體設置了fetch=FetchType.LAZY 或者EAGER時,會有不同的自動連接查詢,鼓勵大家自行嘗試。以上查詢語句有必要對其中幾個進行解釋一下;

對於UserRepository中的第一與第二個方法,目的是為了比較與展示位置綁定與名字綁定的區別,相信根據名稱大家就能判別是什么意思與區別了,位置綁定即是方法參數從左到右第123456...所在位置的參數與查詢語句中的第123456...進行對應。名字綁定即是查詢語句中的參數名稱與方法參數名稱一一對應;對於第三個與第四個查詢例子就不多說了;第五條查詢語句展示的是傳入集合進行in查詢;第六條查詢例子展示的是傳入bean進行查詢,該查詢使用的表達式是Spring的SPEL表達式;

2. 分頁與排序


最后兩條查詢語句展示的是進行分頁查詢、分頁並排序查詢,使用的計數查詢默認使用主查詢語句中的條件進行count, 當Repository接口的方法中含有Pageable參數時,那么SpringData認為該查詢是需要分頁的;org.springframework.data.domain.Pageable是一個接口,接口中定義了分頁邏輯操作,它具有一個間接實現類為PageRequest,我們最需要關注的是PageRequest這個實現類的三個構造方法:


   
   
  
  
          
  1. public class PageRequest extends AbstractPageRequest {
  2. ....
  3. ....
  4. public PageRequest(int page, int size) {
  5. this(page, size, null);
  6. }
  7. public PageRequest(int page, int size, Direction direction, String... properties) {
  8. this(page, size, new Sort(direction, properties));
  9. }
  10. public PageRequest(int page, int size, Sort sort) {
  11. super(page, size);
  12. this.sort = sort;
  13. }
  14. ....
  15. ....
  16. }
page參數為頁碼(查第幾頁,從0頁開始),size為每頁顯示多少條記錄數;
Direction則是一個枚舉,如果該參數被傳入則進行排序,常用的有Direction.ASC/Direction.DESC,即正序排與逆序排,如果排序,需要根據哪個字段排序呢?properties是一個可變長參數,傳入相應字段名稱即可根據該字段排序。還有最后一個參數Sort,Sort這個類中有一個構造方法:public Sort(Direction direction, String... properties),沒錯,我不用說相信大家都已經懂了是干什么用的了。

Pageable與PageRequest的關系解釋完了,那么就該介紹一下最后兩條查詢語句的返回值Page<T>是干什么用的了,讓我們看看倒數第二個測試方法返回的json串結果:


   
   
  
  
          
  1. { "content": [
  2. { "id": 1, "name": "王大帥", "password": "123", "createDate": 1515312688000, "email": "1@qq.com", "department": { "id": 1, "name": "開發部"}},
  3. { "id": 2, "name": "張大仙", "password": "456", "createDate": 1515139947000, "email": "2@qq.com", "department": { "id": 1, "name": "開發部" }},
  4. { "id": 3, "name": "李小三", "password": "789", "createDate": 1514794375000, "email": "3@qq.com", "department": { "id": 1, "name": "開發部" }},
  5. { "id": 4, "name": "馬上來", "password": "444", "createDate": 1512116003000, "email": "4@qq.com", "department": { "id": 1, "name": "開發部" } },
  6. { "id": 5, "name": "馬德華", "password": "555", "createDate": 1515312825000, "email": "5@qq.com", "department": { "id": 1, "name": "開發部"} }],
  7. "last": true,
  8. "totalPages": 1,
  9. "totalElements": 5,
  10. "size": 5,
  11. "number": 0,
  12. "sort": null,
  13. "first": true,
  14. "numberOfElements": 5
  15. }
跟蹤源碼得到結論,Page<T>是一個接口,它的基類接口Slice<T>也是一個接口,而實現類Chunk實現了Slice,實現類PageImpl繼承了Chunk並且實現了Page接口。所以實際上Json輸出的字符串是PageImpl的擁有的所有屬性(包括其父類Chunk)。content屬性是分頁得出的實體集合,類型為List,也就是上面json串中的content。last屬性表示是否為最后一頁,totalPages表示總頁數,totalElements表示總記錄數,size為每頁記錄數大小,number表示當前為第幾頁,numberOfElements表示當前頁所擁有的記錄數,first表示當前是否第一頁,sort為排序信息。

到這里,Page與Pageable都了解了。

3. 關聯查詢與部分字段映射投影


接下來介紹使用JPQL進行關聯查詢與部分字段映射。現在的查詢需求是,查出所有用戶的名字、用戶所屬部門、用戶的email、統計用戶所擁有的角色有多少個,然后將列表結果進行給前端顯示。有的朋友說,那我把關聯到的對象都拿出來不就完了。可是,實際開發中一個表下有幾十個字段會很常見,如果全部都拿出來是沒有必要的,所以我們可以把需要的字段拿出來就可以了,下面介紹兩種方法實現這種需求。

3.1 使用VO(view object)做映射與投影


我們在src/main/java中增加一個org.fage.vo包,該包下存放VO對象,我們在該包下創建一個UserOutputVO:


   
   
  
  
          
  1. public class UserOutputVO {
  2. private String name; //用戶的名字
  3. private String email; //用戶的email
  4. private String departmentName; //用戶所屬的部門
  5. private Long roleNum; //該用戶擁有的角色數量
  6. public UserOutputVO(String name, String email, String departmentName, Long roleNum) {
  7. super();
  8. this.name = name;
  9. this.email = email;
  10. this.departmentName = departmentName;
  11. this.roleNum = roleNum;
  12. }
  13. public UserOutputVO() {
  14. super();
  15. }
  16. //getter and setter and toString
  17. ...
  18. }
在UserRepository中創建查詢方法:


   
   
  
  
          
  1. @Query(value = "select new org.fage.vo.UserOutputVO(u.name, u.email, d.name as departmentName, count(r.id) as roleNum) from User u "
  2. + "left join u.department d left join u.roles r group by u.id")
  3. Page<UserOutputVO> findUserOutputVOAllPage(Pageable pageable);

這里注意一下,VO中的構造方法參數一定要與查詢語句中的查詢字段類型相匹配(包括數量),如果不匹配就會報錯。以下是測試代碼:


   
   
  
  
          
  1. @Test
  2. public void testFindUserOutputVOAllPage(){
  3. Pageable pageable = new PageRequest( 0, 5);
  4. Page<UserOutputVO> page = userRepository.findUserOutputVOAllPage(pageable);
  5. List<UserOutputVO> list = page.getContent();
  6. for(UserOutputVO vo : list)
  7. logger.info(vo.toString());
  8. }
輸出結果:

對於連接查詢,有join、left join 、right join,與sql的類似,但是唯一需要注意的地方就是建模的關系要能連接起來,因為只有這樣才能使用“.”進行連接;就像你想的那樣,它是類似對象導航的,與sql的表連接有些使用上的不同,但是最終的連接結果是相同的。

3.2 使用projection接口做映射與投影


依然使用的是上面查詢VO的需求進行查詢,換成projection的方式,在org.fage.vo中創建一個接口:


   
   
  
  
          
  1. public interface UserProjection {
  2. String getName();
  3. @Value( "#{target.emailColumn}") //當別名與該getXXX名稱不一致時,可以使用該注解調整
  4. String getEmail();
  5. String getDepartmentName();
  6. Integer getRoleNum();
  7. }
在UserRepository中創建查詢語句:


   
   
  
  
          
  1. //故意將email別名為emailColumn,以便講解@Value的用法
  2. @Query(value = "select u.name as name, u.email as emailColumn, d.name as departmentName, count(r.id) as roleNum from User u "
  3. + "left join u.department d left join u.roles r group by u.id")
  4. Page<UserProjection> findUserProjectionAllPage(Pageable pageable);
在TestClass中添加測試方法:


   
   
  
  
          
  1. @Test
  2. public void testFindUserProjectionAllPage(){
  3. Page<UserProjection> page = userRepository.findUserProjectionAllPage( new PageRequest( 0, 5));
  4. Collection<UserProjection> list = page.getContent();
  5. for(UserProjection up : list){
  6. logger.info(up.getName());
  7. logger.info(up.getEmail());
  8. logger.info(up.getDepartmentName());
  9. logger.info(up.getRoleNum()+ "");
  10. }
  11. }
測試結果是成功的。在這里需要注意幾點約束,Projection接口中必須以“getXXX”來命名方法,關於“XXX”則是要與查詢語句中的別名相對應,注意觀察上面的Projection接口與查詢語句就發現了。不難發現,有一個別名為emailColumn,與Projection接口中的getEmail方法並不對應,這種時候可以使用@Value{"${target.xxx}"}注解來調整,注意其中的target不能省略,可以把target看成用別名查出來的臨時對象,這樣就好理解了。

兩種方式都可以,對於到底哪種方式好,這取決於你的需求。


4.命名查詢

如果以上查詢實例都弄懂了,那么命名查詢也是類似的,換湯不換葯;這里簡單的只舉兩個例子,需求變更時請大家自行嘗試。
命名查詢的核心注解是@NamedQueries 與 @NamedQuery;@NamedQueries中只有一個value參數,該參數是@NamedQuery的數組。@NamedQuery注解我們需要關注兩個參數,query參數也就是需要寫入查詢語句的地方;name參數則是給該查詢命名,命名方式一般約定為  “實體類名.實體對應的repository的查詢方法名”,如果看不懂沒關系,請看下面的例子。
在Role實體中增加以下代碼:

    
    
   
   
           
  1. @Entity
  2. @Table(name="role")
  3. @NamedQueries({
  4. @NamedQuery(name = "Role.findById", query = "from Role r where r.id=?1"),
  5. @NamedQuery(name = "Role.findAllPage", query = "from Role r")
  6. //...更多的@NamedQuery
  7. })
  8. public class Role implements Serializable{
  9. private static final long serialVersionUID = 1366815546093762449L;
  10. @Id
  11. @GeneratedValue(strategy=GenerationType.IDENTITY)
  12. private Long id;
  13. private String name;
  14. public Role(){
  15. super();
  16. }
  17. public Role(String name){
  18. this.name = name;
  19. }
  20. //getter and setter
  21. }
對應的RoleRepository代碼:


    
    
   
   
           
  1. @Repository
  2. public interface RoleRepository extends JpaRepository<Role, Long>{
  3. Role findById(Long id);
  4. Page<Role> findAllPage(Pageable pageable);
  5. }
相應的測試代碼:


   
   
  
  
          
  1. @Test
  2. public void testFindRoleById(){
  3. roleRepository.findById( 1l);
  4. }
  5. @Test
  6. public void testFindRoleAllPage(){
  7. roleRepository.findAll( new PageRequest( 0, 5));
  8. }
以上就是命名查詢的常用方式。

5. JPQL方式總結

還是比較建議使用JPQL方式,因為SpringDataJPA各方面(比如分頁排序)、動態查詢等等都支持得比較好,Spring的SPEL表達式還可以擴展到SpringSecurity與SpringDataJPA高級的session用戶查詢方式,后續博客會有對SpringSecurity的介紹,等到那時候在一起講解。

三、使用原生SQL查詢


有些時候,JPQL使用不當會導致轉化成的sql並不如理想的簡潔與優化,所以在特定場合還是得用到原生SQL查詢的,比如當你想優化sql時等等。


1 .一般查詢

使用原生查詢時用的也是@Query注解,此時nativeQuery參數應該設置為true。我們先來看一些簡單的查詢


   
   
  
  
          
  1. @Query(value = "select * from user u where u.id=:id", nativeQuery = true)
  2. User findByIdNative(@Param("id")Long id);
  3. @Query(value = "select * from user", nativeQuery = true)
  4. List<User> findAllNative();
看看測試代碼:


   
   
  
  
          
  1. @Test
  2. @Transactional
  3. public void testFindByIdNative(){
  4. User u = userRepository.findByIdNative( 1l);
  5. logger.info(u.toString());
  6. logger.info(u.getRoles().toString());
  7. }
  8. @Test
  9. public void testFindAllNative(){
  10. List<User> list = userRepository.findAllNative();
  11. for(User u : list){
  12. logger.info(u.toString());
  13. }
  14. }

結果發現當查所有字段的時候,確實能映射成功,並且fetch快加載、懶加載自動關聯也能正常使用。接下來我們換剛才使用JPQL時的查詢需求,看看用SQL時該怎么做。


2.投影與映射分頁查詢


查詢列表的需求依舊是剛才介紹使用JPQL時使用的需求(分頁查出所有用戶的名字、用戶所屬部門、用戶的email、統計用戶所擁有的角色有多少個),在UserRepository中創建代碼片段:


   
   
  
  
          
  1. //展示原生查詢
  2. @Query(value = "select u.name as name, u.email as emailColumn, d.name as departmentName, count(ur.role_id) as roleNum from user u "
  3. + "left join department d on d.id=u.department_id left join user_role ur on u.id=ur.user_id group by u.id limit :start,:size",
  4. nativeQuery = true)
  5. List<Object[]> findUserProjectionAllPageNative( @Param( "start") int start, @Param( "size") int size);
  6. //count語句
  7. @Query(value = "select count(u.id) from user u", nativeQuery = true)
  8. long countById();

在TestClass中創建測試代碼:


   
   
  
  
          
  1. @Test
  2. public void testFindUserProjectionAllPageNative(){
  3. Pageable pageable = new PageRequest( 0, 5);
  4. List<Object []> content = userRepository.findUserProjectionAllPageNative(pageable.getOffset(), pageable.getPageSize());
  5. long total = userRepository.countById();
  6. //查看一下查詢結果
  7. logger.info(content.size() + "");
  8. for(Object[] o : content){
  9. logger.info( "名字:" + o[ 0].toString());
  10. logger.info( "email:" + o[ 1].toString());
  11. logger.info( "所屬部門" + o[ 2].toString());
  12. logger.info( "角色數量" + o[ 3].toString());
  13. }
  14. //如果需要的話,自行封裝分頁信息
  15. Page<Object[]> page = new PageImpl<Object[]>(content, pageable, total);
  16. System.out.println(page);
  17. }

解釋一下上面代碼,由於是原生查詢不支持動態分頁,Page分頁我們只能自己做了,但是依舊使用的是Spring的Page;pageable.getOffset()與pageable.getPageSize()分別對應limit ?, ?的第一與第二個問號。原生查詢得出來的List是包函一堆被封裝成Object的對象數組,每個object數組可以通過數組索引拿出值,也就與需要查的字段一一對應。如果你需要存入VO再帶回給前端,那么你可以自行封裝。對於PageImpl,我們使用了public PageImpl(List<T> content, Pageable pageable, long total) 這個構造方法,第一個參數是查詢到的結果,第二個就不用說了,第三個參數是對主sql的count查詢。當前端需要顯示分頁時,可以這樣進行手動分頁。

3.SQL方式總結

當你需要進行sql優化時,可能用原生sql方式會更好。但是一般需求時候用JPQL還是比較方便的,畢竟這樣比較省事,拿數據總是需要分頁的,有時候只需要拿幾個字段也是這樣。


四、總結


當你在接到一般需求時,使用JPQL的方式其實已經足夠用了。但是如果對sql需要優化的時候,你也可以使用SQL的方式。總而言之,需要根據需求來應變使用的策略。

如果文中有不當的地方歡迎同學們提出建議與修改方案,但是請不要謾罵與辱罵。

下一篇將講解SpringDataJPA根據動態條件進行查詢的方方面面。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM