Spring Data JPA - 中文手冊(cè)

Spring Data JPA - 中文手冊(cè)

參考文檔:
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

項(xiàng)目中使用spring data jpa

@EnableJpaRepositories
class Config {}

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

  • CREATE 根據(jù)方法的名字直接創(chuàng)建對(duì)應(yīng)的查詢語(yǔ)句
  • USE_DECLARED_QUERY 使用聲明的查詢語(yǔ)句了罪,如果每一找到聲明的語(yǔ)句將會(huì)拋出一個(gè)異常泊藕,聲明一個(gè)語(yǔ)句使用 @NamedQuery
  • CREATE_IF_NOT_FOUND 首先查找是否已經(jīng)有聲明的查詢語(yǔ)句娃圆,如果每一再創(chuàng)建蛾茉,這是默認(rèn)的策略

通過(guò)方法名來(lái)定義語(yǔ)句

public interface PersonRepository extends Repository<User, Long> {

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

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

  // Order By 語(yǔ)句
  List<Person> findByLastnameOrderByFirstnameAsc(String lastname);
  List<Person> findByLastnameOrderByFirstnameDesc(String lastname);
  
  //Person 中有個(gè)Address, Address中有zipCode屬性悦屏,直接通過(guò)zipCode來(lái)查詢Persion
  List<Person> findByAddressZipCode(ZipCode zipCode);
  //和上面的方法完成同樣的功能础爬, 使用 _ 可以用來(lái)區(qū)分屬性,避免生成錯(cuò)誤的語(yǔ)句
  List<Person> findByAddress_ZipCode(ZipCode zipCode);
  
  
  //限制返回?cái)?shù)據(jù)的條數(shù) 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);
  
  //異步查詢,調(diào)用查詢方法后立即返回 Async query results
  @Async
  Future<User> findByFirstname(String firstname);
  @Async
  CompletableFuture<User> findOneByFirstname(String firstname);
  @Async
  ListenableFuture<User> findOneByLastname(String lastname);    
}

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

1. 聲明一個(gè)接口常熙,定義出需要添加的方法
@NoRepositoryBean
public interface MyRepository<T, ID extends Serializable>
  extends PagingAndSortingRepository<T, ID> {

  void sharedCustomMethod(ID id);
}
2. 實(shí)現(xiàn)定義的接口
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.配置實(shí)現(xiàn)類
@Configuration
@EnableJpaRepositories(repositoryBaseClass = MyRepositoryImpl.class)
class ApplicationConfiguration { … }

Web support

@Configuration
@EnableWebMvc
@EnableSpringDataWebSupport
class WebConfiguration { }

@EnableSpringDataWebSupport 將會(huì)開(kāi)啟一些組件,具體可以打開(kāi)源碼查看

  • PageableHandlerMethodArgumentResolver 通過(guò)參數(shù)可以自動(dòng)注入 Pageable 對(duì)象到控制器
    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 通過(guò)參數(shù)可以自動(dòng)注入 Sort 對(duì)象到控制器
    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 傳入一個(gè)對(duì)象Id 就可以直接轉(zhuǎn)換成 需要對(duì)象

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

如果在需要注入對(duì)個(gè)Pageable對(duì)象到controller中,可以使用@Qualifier來(lái)定義前綴聋袋,下劃線分隔穴吹,eg: foo_page=1

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

@PageableDefault 配置默認(rèn)的分頁(yè)港令,如果前端沒(méi)有傳入分頁(yè)信息顷霹,可以使用來(lái)設(shè)置默認(rèn)的分頁(yè)淋淀,默認(rèn)是 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來(lái)聲明語(yǔ)句
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 表示實(shí)體類的名字

使用查詢提示徽缚,具體的查詢提示的值是根據(jù)JPA底層實(shí)習(xí)框架提供的
public interface UserRepository extends Repository<User, Long> {

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

自定義查詢返回的返回結(jié)果

1. 通過(guò)定義接口的方式來(lái)返回?cái)?shù)據(jù)

假如實(shí)體對(duì)象:

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

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

可以這樣定義一個(gè)接口:

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, 返回的對(duì)象將會(huì)用 target 變量替代那婉。如果想要做更加復(fù)雜的編程可以使用下面的方法详炬,使用default定義一個(gè)方法

interface NamesOnly {

  String getFirstname();
  String getLastname();

  default String getFullName() {
    //定義自己的邏輯實(shí)現(xiàn),可以通過(guò)ApplicationContext 來(lái)獲取bean對(duì)象來(lái)調(diào)用方法
    return getFirstname.concat(" ").concat(getLastname());
  }
}

調(diào)用容器中的Bean來(lái)返回結(jié)果的另一種方式@myBean

@Component
class MyBean {

  String getFullName(Person person) {
    …
  }
}

interface NamesOnly {

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

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

interface NamesOnly {

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

args[i] i 表示方法中的第幾個(gè)

2. 通過(guò)定義DTO的方式來(lái)返回?cái)?shù)據(jù)

這種方式底層會(huì)根據(jù)DTO暴露的構(gòu)造放的參數(shù)名字來(lái)加載 需要的字段
定義的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);
}
存儲(chǔ)過(guò)程的使用

參考: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

提供的注解猫妙,在實(shí)體中添加:
@CreatedBy, @LastModifiedBy, @CreatedDate, @LastModifiedDate

實(shí)現(xiàn)接口AuditorAware@CreatedBy, @LastModifiedBy 設(shè)置值

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();
  }
}

最后在實(shí)體上添加注解@EntityListeners(AuditingEntityListener.class)

@Entity
@EntityListeners(AuditingEntityListener.class)
public class MyEntity {
}
獲取 EntityManager 對(duì)象

可以使用@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

更多spring 相關(guān)文章:
http://blog.xianshiyue.com/tag/springboot/
http://blog.xianshiyue.com/tag/spring-cloud/
http://blog.xianshiyue.com/tag/springmvc/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市彼哼,隨后出現(xiàn)的幾起案子沪羔,更是在濱河造成了極大的恐慌象浑,老刑警劉巖愉豺,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異茫因,居然都是意外死亡蚪拦,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)冻押,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)驰贷,“玉大人,你說(shuō)我怎么就攤上這事洛巢±ㄌ唬” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵锹锰,是天一觀的道長(zhǎng)芥炭。 經(jīng)常有香客問(wèn)我,道長(zhǎng)恃慧,這世上最難降的妖魔是什么园蝠? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮痢士,結(jié)果婚禮上彪薛,老公的妹妹穿的比我還像新娘。我一直安慰自己怠蹂,他們只是感情好善延,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著褥蚯,像睡著了一般挚冤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上赞庶,一...
    開(kāi)封第一講書(shū)人閱讀 52,394評(píng)論 1 310
  • 那天训挡,我揣著相機(jī)與錄音,去河邊找鬼歧强。 笑死澜薄,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的摊册。 我是一名探鬼主播肤京,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼茅特!你這毒婦竟也來(lái)了忘分?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤白修,失蹤者是張志新(化名)和其女友劉穎妒峦,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體兵睛,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡肯骇,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了祖很。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片笛丙。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖假颇,靈堂內(nèi)的尸體忽然破棺而出胚鸯,到底是詐尸還是另有隱情,我是刑警寧澤拆融,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布蠢琳,位于F島的核電站啊终,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏傲须。R本人自食惡果不足惜蓝牲,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望泰讽。 院中可真熱鬧例衍,春花似錦、人聲如沸已卸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)疗疟。三九已至,卻和暖如春交洗,著一層夾襖步出監(jiān)牢的瞬間愧哟,已是汗流浹背奥吩。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蕊梧,地道東北人霞赫。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像肥矢,于是被迫代替她去往敵國(guó)和親端衰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容