Spring Data JPA 學習筆記(一)中文手冊

原文鏈接:https://www.jianshu.com/p/aebc011fcb7d

Spring Data JPA - 中文手冊

參考文檔:
http://blog.csdn.net/liuchuanhong1/article/details/70244261?utm_source=gold_browser_extension
https://docs.spring.io/spring-data/jpa/docs/1.11.9.RELEASE/reference/html/#specifications
https://www.v2ex.com/t/350737

項目中使用spring data jpa

@EnableJpaRepositories
class Config {}

查詢方法的生成策略 Query lookup strategies

  • CREATE 根據方法的名字直接創建對應的查詢語句
  • USE_DECLARED_QUERY 使用聲明的查詢語句,如果每一找到聲明的語句將會拋出一個異常,聲明一個語句使用 @NamedQuery
  • CREATE_IF_NOT_FOUND 首先查找是否已經有聲明的查詢語句,如果每一再創建,這是默認的策略

通過方法名來定義語句

public interface PersonRepository extends Repository<User, Long> {

  // 使用 distinct 關鍵字
  List<Person> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);
  List<Person> findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname);

  // 忽略大小寫進行匹配 IgnoreCase
  List<Person> findByLastnameIgnoreCase(String lastname);
  // 對所有的查詢條件進行忽略大小寫進行匹配 IgnoreCase
  List<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);

  // Order By 語句
  List<Person> findByLastnameOrderByFirstnameAsc(String lastname);
  List<Person> findByLastnameOrderByFirstnameDesc(String lastname);
  
  //Person 中有個Address, Address中有zipCode屬性,直接通過zipCode來查詢Persion
  List<Person> findByAddressZipCode(ZipCode zipCode);
  //和上面的方法完成同樣的功能, 使用 _ 可以用來區分屬性,避免生成錯誤的語句
  List<Person> findByAddress_ZipCode(ZipCode zipCode);
  
  
  //限制返回數據的條數 Limiting query results
  User findFirstByOrderByLastnameAsc();
  User findTopByOrderByAgeDesc();
  
  Page<User> queryFirst10ByLastname(String lastname, Pageable pageable);
  Slice<User> findTop3ByLastname(String lastname, Pageable pageable);
  
  List<User> findFirst10ByLastname(String lastname, Sort sort);
  List<User> findTop10ByLastname(String lastname, Pageable pageable);
  
  //異步查詢,調用查詢方法後立即返回 Async query results
  @Async
  Future<User> findByFirstname(String firstname);
  @Async
  CompletableFuture<User> findOneByFirstname(String firstname);
  @Async
  ListenableFuture<User> findOneByLastname(String lastname);    
}

爲所有生成的Repository添加一些額外的方法

1. 聲明一個接口,定義出需要添加的方法

@NoRepositoryBean
public interface MyRepository<T, ID extends Serializable>
  extends PagingAndSortingRepository<T, ID> {

  void sharedCustomMethod(ID id);
}

2. 實現定義的接口

public class MyRepositoryImpl<T, ID extends Serializable>
  extends SimpleJpaRepository<T, ID> implements MyRepository<T, ID> {

  private final EntityManager entityManager;

  public MyRepositoryImpl(JpaEntityInformation entityInformation,
                          EntityManager entityManager) {
    super(entityInformation, entityManager);

    // Keep the EntityManager around to used from the newly introduced methods.
    this.entityManager = entityManager;
  }

  public void sharedCustomMethod(ID id) {
    // implementation goes here
  }
}

3.配置實現類

@Configuration
@EnableJpaRepositories(repositoryBaseClass = MyRepositoryImpl.class)
class ApplicationConfiguration { … }

Web support

@Configuration
@EnableWebMvc
@EnableSpringDataWebSupport
class WebConfiguration { }

@EnableSpringDataWebSupport 將會開啓一些組件,具體可以打開源碼查看

  • PageableHandlerMethodArgumentResolver 通過參數可以自動注入 Pageable 對象到控制器
    page -> Page you want to retrieve, 0 indexed and defaults to 0. |
    size -> Size of the page you want to retrieve, defaults to 20. |

  • SortHandlerMethodArgumentResolver 通過參數可以自動注入 Sort 對象到控制器
    sort -> Properties that should be sorted by in the format property,property(,ASC|DESC). Default sort direction is ascending. Use multiple sort parameters if you want to switch directions, e.g. ?sort=firstname&sort=lastname,asc. |

  • DomainClassConverter 傳入一個對象Id 就可以直接轉換成 需要對象

 @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";
      }
    }

如果在需要注入對個Pageable對象到controller中,可以使用@Qualifier來定義前綴,下劃線分隔,eg: foo_page=1

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

@PageableDefault 配置默認的分頁,如果前端沒有傳入分頁信息,可以使用來設置默認的分頁,默認是 new PageRequest(0, 20)

Supported keywords inside method names

Keyword Sample JPQL snippet
And findByLastnameAndFirstname … where x.lastname = ?1 and x.firstname = ?2
Or findByLastnameOrFirstname … where x.lastname = ?1 or x.firstname = ?2
Is,Equals findByFirstname,findByFirstnameIs,findByFirstnameEquals … where x.firstname = ?1
Between findByStartDateBetween … where x.startDate between ?1 and ?2
LessThan findByAgeLessThan … where x.age < ?1
LessThanEqual findByAgeLessThanEqual … where x.age <= ?1
GreaterThan findByAgeGreaterThan … where x.age > ?1
GreaterThanEqual findByAgeGreaterThanEqual … where x.age >= ?1
After findByStartDateAfter … where x.startDate > ?1
Before findByStartDateBefore … where x.startDate < ?1
IsNull findByAgeIsNull … where x.age is null
IsNotNull,NotNull findByAge(Is)NotNull … where x.age not null
Like findByFirstnameLike … where x.firstname like ?1
NotLike findByFirstnameNotLike … where x.firstname not like ?1
StartingWith findByFirstnameStartingWith … where x.firstname like ?1 (parameter bound with appended %)
EndingWith findByFirstnameEndingWith … where x.firstname like ?1 (parameter bound with prepended %)
Containing findByFirstnameContaining … where x.firstname like ?1 (parameter bound wrapped in %)
OrderBy findByAgeOrderByLastnameDesc … where x.age = ?1 order by x.lastname desc
Not findByLastnameNot … where x.lastname <> ?1
In findByAgeIn(Collection<Age> ages) … where x.age in ?1
NotIn findByAgeNotIn(Collection<Age> ages) … where x.age not in ?1
True findByActiveTrue() … where x.active = true
False findByActiveFalse() … where x.active = false
IgnoreCase findByFirstnameIgnoreCase … where UPPER(x.firstame) = UPPER(?1)

使用@Query來聲明語句

public interface UserRepository extends JpaRepository<User, Long> {

  @Query(value = "SELECT * FROM USERS WHERE LASTNAME = ?1",
    countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1",
    nativeQuery = true)
  Page<User> findByLastname(String lastname, Pageable pageable);
}

Using SpEL expressions

public interface UserRepository extends JpaRepository<User,Long> {

  @Query("select u from #{#entityName} u where u.lastname = ?1")
  List<User> findByLastname(String lastname);
}

entityName 表示實體類的名字

使用查詢提示,具體的查詢提示的值是根據JPA底層實習框架提供的

public interface UserRepository extends Repository<User, Long> {

  @QueryHints(value = { @QueryHint(name = "name", value = "value")},
              forCounting = false)
  Page<User> findByLastname(String lastname, Pageable pageable);
}

自定義查詢返回的返回結果

1. 通過定義接口的方式來返回數據

假如實體對象:

class Person {
  @Id UUID id;
  String firstname, lastname;
  Address address;

  static class Address {
    String zipCode, city, street;
  }
}

可以這樣定義一個接口:

interface PersonSummary {
  String getFirstname();
  String getLastname();
  AddressSummary getAddress();

  interface AddressSummary {
    String getCity();
  }
}

查詢方法的定義:

interface PersonRepository extends Repository<Person, UUID> {
  Collection<PersonSummary> findByLastname(String lastname);
}

這樣就可以完成自定義查詢,還有其他方式定義接口

interface NamesOnly {

  @Value("#{target.firstname + ' ' + target.lastname}")
  String getFullName();
  …
}

組合了firstname lastname, 返回的對象將會用 target 變量替代。如果想要做更加複雜的編程可以使用下面的方法,使用default定義一個方法

interface NamesOnly {

  String getFirstname();
  String getLastname();

  default String getFullName() {
    //定義自己的邏輯實現,可以通過ApplicationContext 來獲取bean對象來調用方法
    return getFirstname.concat(" ").concat(getLastname());
  }
}

調用容器中的Bean來返回結果的另一種方式@myBean

@Component
class MyBean {

  String getFullName(Person person) {
    …
  }
}

interface NamesOnly {

  @Value("#{@myBean.getFullName(target)}")
  String getFullName();
  …
}

如果想要使用 方法中的參數可以用這種方式

interface NamesOnly {

  @Value("#{args[0] + ' ' + target.firstname + '!'}")
  String getSalutation(String prefix);
}

args[i] i 表示方法中的第幾個

2. 通過定義DTO的方式來返回數據

這種方式底層會根據DTO暴露的構造放的參數名字來加載 需要的字段
定義的DTO

class NamesOnly {
  private final String firstname, lastname;

  NamesOnly(String firstname, String lastname) {
    this.firstname = firstname;
    this.lastname = lastname;
  }
  String getFirstname() {
    return this.firstname;
  }
  String getLastname() {
    return this.lastname;
  }
  // equals(…) and hashCode() implementations
}

定義的Repository

interface PersonRepository extends Repository<Person, UUID> {
  Collection<T> findByLastname(String lastname, Class<T> type);
}

使用方式如下:

void someMethod(PersonRepository people) {
  Collection<Person> aggregates = people.findByLastname("Matthews", Person.class);
  Collection<NamesOnly> aggregates = people.findByLastname("Matthews", NamesOnly.class);
}

存儲過程的使用

參考:https://docs.spring.io/spring-data/jpa/docs/1.11.9.RELEASE/reference/html/#jpa.stored-procedures

Specifications

public class CustomerSpecs {

  public static Specification<Customer> isLongTermCustomer() {
    return new Specification<Customer>() {
      public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query,
            CriteriaBuilder builder) {

         LocalDate date = new LocalDate().minusYears(2);
         return builder.lessThan(root.get(_Customer.createdAt), date);
      }
    };
  }

  public static Specification<Customer> hasSalesOfMoreThan(MontaryAmount value) {
    return new Specification<Customer>() {
      public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query,
            CriteriaBuilder builder) {

         // build query here
      }
    };
  }
}

List<Customer> customers = customerRepository.findAll(isLongTermCustomer());

Locking

interface UserRepository extends Repository<User, Long> {
  // Plain query method
  @Lock(LockModeType.READ)
  List<User> findByLastname(String lastname);
}

Auditing

提供的註解,在實體中添加:
@CreatedBy, @LastModifiedBy, @CreatedDate, @LastModifiedDate

實現接口AuditorAware@CreatedBy, @LastModifiedBy 設置值

class SpringSecurityAuditorAware implements AuditorAware<User> {
  public User getCurrentAuditor() {
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    if (authentication == null || !authentication.isAuthenticated()) {
      return null;
    }
    return ((MyUserDetails) authentication.getPrincipal()).getUser();
  }
}

最後在實體上添加註解@EntityListeners(AuditingEntityListener.class)

@Entity
@EntityListeners(AuditingEntityListener.class)
public class MyEntity {
}

獲取 EntityManager 對象

可以使用@PersistenceContext. spring data jpa 還提供了一種方式:

class UserRepositoryImpl implements UserRepositoryCustom {
  private final EntityManager em;
  @Autowired
  public UserRepositoryImpl(JpaContext context) {
    this.em = context.getEntityManagerByManagedType(User.class);
  }
}

https://docs.spring.io/spring-data/jpa/docs/1.11.9.RELEASE/reference/html/#appendix



作者:9527華安
鏈接:https://www.jianshu.com/p/aebc011fcb7d
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯繫作者獲得授權並註明出處。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章