詳解使用Spring Data repository進行數據層的訪問問題

使用Spring Data repository進行數據層的訪問

抽象出Spring Data repository是因為在開發過程中,常常會為瞭實現不同持久化存儲的數據訪問層而寫大量的大同小異的代碼。

Spring Data repository的目的就是要大幅減少這些重復的代碼。 Spring Data Elasticsearch為文檔的存儲,查詢,排序和統計提供瞭一個高度抽象的模板。

核心概念

Spring Data repository抽象中最核心的接口就是Repository。該接口使用瞭泛型,需要提供兩個類型參數,

  • 第一個是接口處理的域對象類型
  • 第二個是域對象的主鍵類型。

這個接口常被看做是一個標記型接口,用來獲取要操作的域對象類型和幫助開發者識別繼承這個類的接口。在Repository的基礎上,CrudRepository接口提供瞭針對實體類的復雜的CRUD(增刪改查)操作。

public interface CrudRepository<T, ID extends Serializable>
    extends Repository<T, ID> {
    <S extends T> S save(S entity); 
    T findOne(ID primaryKey);       
    Iterable<T> findAll();          
    Long count();                   
    void delete(T entity);          
    boolean exists(ID primaryKey);  
    // … more functionality omitted.
}

PagingAndSortingRepository接口在CrudRepository的基礎上增加瞭一些方法,使開發者可以方便的對實體類進行分頁和排序。

public interface PagingAndSortingRepository<T, ID extends Serializable>
  extends CrudRepository<T, ID> {
  Iterable<T> findAll(Sort sort);
  Page<T> findAll(Pageable pageable);
}

在分頁長度為20的基礎上,想要獲取第二頁的User數據,代碼如下

PagingAndSortingRepository<User, Long> repository = // … get access to a bean
Page<User> users = repository.findAll(new PageRequest(1, 20));

查詢方法

標準的CRUD(增刪改查)功能都要使用查詢語句來查詢數據庫。但通過使用Spring Data,隻要五個步驟就可以實現。

  • 創建一個Domain類
@Entity
@Document
public class Person {
  …
}
  • 聲明一個繼承Repository接口或其子接口的持久層接口。並標明要處理的域對象類型及其主鍵的類型(在下面的例子中,要處理的域對象是Person,其主鍵類型是Long)
interface PersonRepository extends Repository<Person, Long> { … }
  • 在接口中聲明查詢方法(spring會為其生成實現代碼)
interface PersonRepository extends Repository<Person, Long> {
  List<Person> findByLastname(String lastname);
}
  • 讓Spring創建對這些接口的代理實例。

使用JavaConfig的方式

import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@EnableJpaRepositories
class Config {}

使用xml配置的方式

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:jpa="http://www.springframework.org/schema/data/jpa"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans.xsd
     http://www.springframework.org/schema/data/jpa
     http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
   <jpa:repositories base-package="com.acme.repositories"/>
</beans>

註入repository實例,並使用

public class SomeClient {
  @Autowired
  private PersonRepository repository;
  public void doSomething() {
    List<Person> persons = repository.findByLastname("Matthews");
  }
}

定義查詢方法

CREATE

Spring Data repository自帶瞭一個非常有用的查詢構造器。它會從方法名中去掉類似find..By,read…By,query…By,count…By之類的前綴,然後解析剩餘的名字。我們也可以在方法名中加入更多的表達式,比如查詢時需要distinct約束,那麼在方法名中加入Distinct即可。方法名中的第一個By是一個分解符,代表著查詢語句的開始,我們可以用And或Or來將多個查詢條件關聯起來。

public interface PersonRepository extends Repository<User, Long> {
  List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);
  // Enables the distinct flag for the query
  List<Person> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);
  List<Person> findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname);
  // Enabling ignoring case for an individual property
  List<Person> findByLastnameIgnoreCase(String lastname);
  // Enabling ignoring case for all suitable properties
  List<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);
  // Enabling static ORDER BY for a query
  List<Person> findByLastnameOrderByFirstnameAsc(String lastname);
  List<Person> findByLastnameOrderByFirstnameDesc(String lastname);
}

除此之外,我們還可以為方法添加某些特定類型的參數(如:Pageable和Sort)來動態的在查詢中添加分頁和排序。

Page<User> findByLastname(String lastname, Pageable pageable);
Slice<User> findByLastname(String lastname, Pageable pageable);
List<User> findByLastname(String lastname, Sort sort);
List<User> findByLastname(String lastname, Pageable pageable);

USE_DECLARED_QUERY

如果方法通過 @Query 指定瞭查詢語句,則使用該語句創建Query;如果沒有,則查找是否定義瞭符合條件的Named Query,如果找到,則使用該命名查詢;如果兩者都沒有找到,則拋出異常。使用@Query聲明查詢語句的例子如下:

//使用Query註解
@Query("select a from AccountInfo a where a.accountId = ?1")
public AccountInfo findByAccountId(Long accountId);

CREATE_IF_NOT_FOUND

結合瞭CREATE和USE_DECLARED_QUERY 兩種策略,會先嘗試查找聲明好的查詢,如果沒有找到,就按照解析方法名的方式構建查詢。這是默認的查詢策略,如果不更改配置,會一直使用這種策略構建查詢。這種策略支持通過方法名快速定義一個查詢,也允許引入聲明好的查詢。

WEB支持

DomainClassConverter 允許開發者在SpringMVC控制層的方法中直接使用域對象類型(Domain types),而無需通過repository手動查找這個實例。

@Controller
@RequestMapping("/users")
public class UserController {
  @RequestMapping("/{id}")
  public String showUserForm(@PathVariable("id") User user, Model model) {
    model.addAttribute("user", user);
    return "userForm";
  }
}

上面的方法直接接收瞭一個User對象,開發者不需要做任何的搜索操作,轉換器會自動將路徑變量id轉為User對象的id,並且調用瞭findOne()方法查詢出User實體。 註意:當前的Repository 必須實現CrudRepository

HandlerMethodArgumentResolver使開發者可以在controller的方法中使用Pageable和Sort作為參數。

@Controller
@RequestMapping("/users")
public class UserController {
  @Autowired UserRepository repository;
  @RequestMapping
  public String showUsers(Model model, Pageable pageable) {
    model.addAttribute("users", repository.findAll(pageable));
    return "users";
  }
}

通過上面的方法定義,Spring MVC會使用下面的默認配置嘗試從請求參數中得到一個Pageable的實例。

參數名 作用
page 想要獲取的頁數,默認為0
size 獲取頁的大小,默認為20
page 需要排序的屬性,格式為property,property(,ASC/DESC),默認升序排序。支持多個字段排序,比如?sort=firstname&sort=lastname,asc

開發者也可以針對多個表定義多個Pageable或Sort實例,需要使用Spring的@Qualifier註解來區分它們。並且請求參數名要帶有${qualifier}_的前綴。例子如下:

public String showUsers(Model model,
      @Qualifier("foo") Pageable first,
      @Qualifier("bar") Pageable second) { … }

請求中需要帶有foo_page和bar_page等參數。

到此這篇關於使用Spring Data repository進行數據層的訪問的文章就介紹到這瞭,更多相關Spring Data repository數據層訪問內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: