查詢方法
CrudRepository中只定義了通過主鍵查詢的方式,這顯然不能滿足需求。Spring提供了查詢方法來解決這樣問題。查詢方法包括: find...By get...By read...By
將返回單個結果T,或者多個結果Iterable<T>, List<T>, Collection<T>,Page<T>。下面是例子:
public interface BookRepository extends PagingAndSortingRepository<Book, Long>{ // 在PagingAndSortingRepository外增加三個查詢方法,對應UNIQUE KEY和KEY的查詢。無需編寫任何實現代碼,Spring data會自動實現的。 Book findByIsbn(String isbn); List<Book> findByAuthor(String author); List<Book> findByPublisher(String publisher); // find...By,By後面的是條件,而find和By之前一般無特定意義,除非是SQL語句中的特定含義 // 中間的Book無意義,只是給方法名字更好的語義,等同於findByIsbn() Book findBookByIsbn(String isbn); // 這裡面的distinct作為保留字有意義,Books無意義,相當於SELECT DISTICT * FROM Book Where Publisher=。, // 假定entity映射的表格為Book,屬性publisher對應的列名了Publisher List<Book> findDistinctBooksByPublisher(String publisher); }作者和出版商都可能有很多的書,需要進行分頁處理,我們改為:
public interface BookRepository extends PagingAndSortingRepository<Book, Long>{ Book findByIsbn(String isbn); Page<Book> findByAuthor(String author, Pageable instructions); Page<Book> findByPublisher(String publisher, Pageable instructions);}上面例子中Entity的屬性都是簡單類型,也可以是複雜類型,例如PersonEntity中含有Address address,而Address又具有若干屬性。我們同樣可以通過前面介紹的方式匹配具體的Address,但是如果我們只需要匹配Address裡面的某個屬性,例如郵編。可以寫為:
List<Person> findByAddressPostalCode(PostalCode code); //匹配address屬性裡面的postalCode屬性,這種寫法容易引起歧義,最好採用底線方式Page<Person> findByAddress_PostalCode(PostalCode code, Pageable instructions);
By後面放置查詢條件,我們具體瞭解如何設定:
// ➤ By後面預設是等於,也就是Is或者Equal,下面等同與Book findByIsbn(String isbn); Book findByIsbnIs(String isbn);Book findByIsbnEqual(String isbn);// ➤ NOT,IsNOT表示不等於 List<Book> findByIsbnIsNot(Strign isbn);List<Book> findByIsbnIsNotEqual(Strign isbn);// ➤ OR 和 AND的條件組合 List<Book> findByAuthorAndPublisher(String author, String publisher);List<Book> findByAuthorOrPublisher(String author, String publisher);// ➤ 大小寫不敏感:資料庫中有些列的匹配已經是設定為忽略大小寫,我們也可以在方法中明確聲明。但從查詢效率,我們應答在表格設計中體現,例如某個KEY是大小寫不敏感的。 //只是publisher忽略大小寫List<Book> findByAuthorAndPublisherIgnoreCase(String author, String publisher); //author和publisher忽略大小寫List<Book> findByAuthorIgnoreCaseAndPublisherIgnoreCase(String author, String publisher); //所有均忽略大小寫List<Book> findByAuthorAndPublisherAllIgnoreCase(String author, String publisher); // ➤ After, IsAfter,Befor,IsBefor用於對時間或日期的操作 List<Book> findByDateFoundedIsAfter(Date date);// ➤ 對於%的處理:Contains,Containing, IsContaining,StartsWith,StartingWith, IsStartingWith,EndsWith,EndingWith, IsEndingWith // 下面相當於WHERE title = '%value%' List<Book> findByTitleContains(String value);// ➤ Like: 下面相當於WHERE title = 'value',注意這和Contain等很相似,但是萬用字元%要放入到value之中 List<Book> findByTitleLike(String value);// ➤ Between,IsBetween List<Book> findByDateFoundedBetween(Date start, Date end);// ➤ Exists:相當於SQL中的EXISTS // ➤ True,IsTrue,False,IsFalse:對boolean的屬性的檢查 List<Book> findByApprovedIsFalse();// ➤ GreaterThan,IsGreaterThan,GreaterThanEqual,IsGreaterThanEqual,LessThan,IsLessThan,LessThanEqual,IsLessThanEqual// ➤ In:表示屬性的值必須等於裡面的某個值,必須採用Iterable來表述 List<Book> findByAuthorIn(Iterable<String> authors);// ➤ Null,IsNull:值為null,方法不應帶有參數,應為已經對值表述清楚 // ➤ Near, IsNear, Within, IsWithin常用於NoSQL,不在JPA中使用 // ➤ Regex,MatchesRegex,Matches用於Regex List<Book> findByTitleRegex(String regex);
自訂的方法
Spring data很強大,但不一定能完全覆蓋我們的需要,在某些情況,我們需要自訂方法。 為某個倉庫提供自訂方法
1、我們定義某個倉庫介面,在裡面提供定義自訂的方法。
public interface TestRepositoryCustom {List<TestEntity> test(int page, int perPageNum, Predicate ... restrictions);}
2、Spring Data的倉庫介面將extends含有自訂的方法的介面
public interface TestRepository extends CrudRepository<TestEntity, Long>,TestRepositoryCustom{TestEntity findByUserName(String userName);}
3、實現自訂的方法。注意,如果這個類無需加上@Repository,但是類名必須為Spring data倉庫介面加上Impl,Spring data掃描到TestRepository後,將會在同一package下面尋找TestRepositoryImpl,如有,將作為Spring bean具體執行個體化(也就是為何我們無需加上@Repository),我們應在此具體實現自訂的方法。至於自訂的介面的名字無任何要求。
public class TestRepositoryImpl implements TestRepositoryCustom{@PersistenceContext private EntityManager entityManager;@Overridepublic List<TestPage> test(int page, int numPerPage, Predicate ... restrictions) {// 具體的代碼 ......}}
為所有倉庫提供自訂方法
這應屬於很罕見的情況,我們需要為所有的倉庫都提供同樣的自訂方法。
1、自訂的介面。這裡extends了JpaRepository(屬於Spring data,其extends了CrudRepository和PagingAndSortingRepository),以便Spring data可以掃描到。標記@NoRepositoryBean是告知Spring data不要為這個介面產生一個實現,因為只是我們通用的自訂介面,不對應具體的倉庫。
@NoRepositoryBeanpublic interface CustomRepository<T, ID extends Serializable> extends JpaRepository<T, ID>{ public void customOperation(T entity);}
2、提供實現,我們必須繼承Spring data JAP提供的基礎倉庫類SimpleJpaRepository。SimpleJpaRepository提供了預定義的介面方法,如findOne(ID),save(T)。如果需要提供Querydsl,則應extends替換為QueryDslJpaRepository。
public class CustomRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements CustomRepository<T, ID>{ private Class<T> domainClass; private EntityManager entityManager; public CustomRepositoryImpl(Class<T> domainClass, EntityManager entityManager){ super(domainClass, entityManager); this.domainClass = domainClass; this.entityManager = entityManager; } public CustomRepositoryImpl(JpaEntityInformation<T, ?> information,EntityManager entityManager){ super(information, entityManager); this.domainClass = information.getJavaType(); this.entityManager = entityManager; } public void customOperation(T){ // code to implement custom operation }}
3、讓所有倉庫支援這些自訂的方法。由於我們標記了@NoRepositoryBean,Spring Data JPA不會自動掃描CustomRepositoryImpl,需要建立一個factory bean替代預設的factory bean來完成這項工作,讓掃描倉庫介面後,不將倉庫介面+Impl作為其實現,而指定為CustomRepositoryImpl。
public class CustomRepositoryFactoryBean<R extends JpaRepository<T, ID>, T,ID extends Serializable> extends JpaRepositoryFactoryBean<R, T, ID>{ @Override protected RepositoryFactorySupport createRepositoryFactory(EntityManager e){ return new CustomRepositoryFactory<T, ID>(e); } private static class CustomRepositoryFactory<T, ID extends Serializable> extends JpaRepositoryFactory{ private EntityManager entityManager; public CustomRepositoryFactory(EntityManager entityManager){ super(entityManager); this.entityManager = entityManager; } @Override @SuppressWarnings("unchecked") protected Object getTargetRepository(RepositoryMetadata metadata){ return new CustomRepositoryImpl<T, ID>((Class<T>) metadata.getDomainType(), this.entityManager); } @Override protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata){ return CustomRepositoryImpl.class; } }}4、將預設的JpaRepositoryFactoryBean修改為CustomRepositoryFactoryBean
@EnableJpaRepositories(basePackages = "cn.wei.flowingflying.chapter22.site.repositories", //Returns the FactoryBean class to be used for each repository instance. Defaults to JpaRepositoryFactoryBean. repositoryFactoryBeanClass = CustomRepositoryFactoryBean.class)
相關連結:我的Professional Java for Web Applications相關文章