1、spring-data-commons项目

  spring-data-commons项目是所有spring-data项目的核心,我们来看一下该项目下的repository包中的接口和注解。

    

2、Repository<T,ID>接口和@RepositoryDefinition注解

  当中最重要的就是Repository接口了。它是做数据库操作的最底层的抽象接口、最顶级的父类,打开Repository接口看其源码,发现里面其实什么方法都没有,仅仅起到一个标识作用。捕获要管理的域类型和域类的id类型。用途是保存类型信息,并能够在类路径扫描期间发现继承该接口的接口,帮助我们创建代理类。

  1. @Indexed
  2. public interface Repository<T, ID> {
  3.  
  4. }

  @Indexed 我们发现在Repository接口上有一个@Indexed 注解,是Spring5提供的注解,用于提升应用启动性能。这个注解单独存在不起作用,要想使其生效的话,要添加spring-context-indexer依赖。在编译时会将@CompoentScan扫描指定package中要生成的bean写在METE-INF/spring.components文件中,当项目启动时,就会读取这个文件,不会再扫描指定的package了,从而提升性能。

  我们只要继承Repository接口,并根据它的规则来命名接口方法,就可以进行数据库操作。(与继承Repository接口相等价的就是,在接口上添加@RepositoryDefinition注解)

示例:

  1. /**
    * 书籍持久层
    * @author caofanqi
    * 使用@RepositoryDefinition注解与继承Repository具有相同的效果
    */
    //@RepositoryDefinition(domainClass = Book.class,idClass = Long.class)
    public interface BookRepository extends Repository<Book,Long>{
  2.  
  3. /**
    * 根据书名查找书籍
    * @param bookName 书籍名称
    * @return 该书籍名称的书列表
    */
    List<Book> findBooksByBookNameContains(String bookName);
  4.  
  5. }
  1. @Transactional
    @Rollback(false)
  2. @SpringBootTest
  3. class BookRepositoryTest {
  4.  
  5. @Resource
  6. private BookRepository bookRepository;
  7.  
  8. @Test
  9. void findBooksByBookNameContains() {
  10. System.out.println("bookRepository : " + bookRepository.getClass().getName());
  11. List<Book> books = bookRepository.findBooksByBookNameContains("Java");
  12. System.out.println(books);
  13. }
  14.  
  15. }

  这样就会根据我们传入的参数去like查询符合条件的书籍,生成的sql语句如下:

     

3、CrudRepository<T,ID>接口与注解@NoRepositoryBean

  CrudRepository是Repository接口的子接口,提供了一套通用的CRUD方法。接口上的@NoRepositoryBean注解的意思是,不让Spring生成该类的代理类。

  1. @NoRepositoryBean //不让Spring为该类生成代理类,仅仅提供一套通用的CRUD方法。
  2. public interface CrudRepository<T, ID> extends Repository<T, ID> {
  3.  
  4. /**
  5.    * 保存方法*/
  6. <S extends T> S save(S entity);
  7.  
  8. /**
  9.    * 保存*/
  10. <S extends T> Iterable<S> saveAll(Iterable<S> entities);
  11.  
  12. /**
  13. * 根据id进行查询*/
  14. Optional<T> findById(ID id);
  15.  
  16. /**
  17. * 判断给定id的数据是否存在*/
  18. boolean existsById(ID id);
  19.  
  20. /**
  21. * 查询全部,数据量很大的时候,谨慎使用*/
  22. Iterable<T> findAll();
  23.  
  24. /**
  25. * 根据给定ids查询符合条件的数据
  26. */
  27. Iterable<T> findAllById(Iterable<ID> ids);
  28.  
  29. /**
  30. * 统计条数*/
  31. long count();
  32.  
  33. /**
  34. * 根据id删除*/
  35. void deleteById(ID id);
  36.  
  37. /**
  38. * 删除给定实体*/
  39. void delete(T entity);
  40.  
  41. /**
  42. * 删除给定实体*/
  43. void deleteAll(Iterable<? extends T> entities);
  44.  
  45. /**
  46. * 删除全部
  47. */
  48. void deleteAll();
  49. }

  我们要想使用这些方法,只需自己的Repository继承该接口即可。

4、PagingAndSortingRepository<T,ID>接口

  是CrudRepository的子接口,也不会生成代理,只是提供分页和排序的方法。

  1. @NoRepositoryBean
  2. public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
  3.  
  4. /**
  5. * 根据sort去所有对象排序的集合*/
  6. Iterable<T> findAll(Sort sort);
  7.  
  8. /**
  9. * 根据pageable进行分页,pageable中可以包含sort*/
  10. Page<T> findAll(Pageable pageable);
  11. }

  分页相关对象:

  Sort查询的排序选项,至少提供一个属性列表来进行排序,不能是null或空,默认是升序ASC;
  可以通过Sort.by 来构建Sort对象:
    public static Sort by(String... properties) 根据给定的属性列表进行升序排序;
    public static Sort by(Direction direction, String... properties) 指定属性和排序方向,Direction.ASC(升序)、Direction.DESC(降序);
    public static Sort by(Order... orders)/public static Sort by(List<Order> orders) 根据给定的一组order进行排序。

  可以通过Sort.and()方法将多个sort组合再一起,通过Sort.ascending()/Sort.descending指定排序方向。
  还可以通过Sort.sort(Class<T> type) 来构造类型安全的排序对象:
    public static <T> TypedSort<T> sort(Class<T> type) 根据指定class类型构造该类型的typeSort排序对象;
    通过TypedSort.by 方法来构建排序字段,通过ascending()/descending指定排序方向,使用and()方法进行连接。

  Order,实现一个排序对,提供方向和属性,为sort提供输入。也就是说,可以针对每一个属性设置不同的升序或降序。
  可以通过一下方式来构建Order对象:
    public static Order by(String property) ,指定属性返回order对象,默认使用升序。
    public static Order asc(String property),返回指定属性升序的order对象。
    public static Order desc(String property),返回指定属性降序的order对象。

  Pageable分页信息的抽象接口,实现类是PageRequest;
  可以通过一下方式来构建Pageable对象:
    public static PageRequest of(int page, int size),创建一个未排序的PageRequest,page从0开始;
    public static PageRequest of(int page, int size, Direction direction, String... properties),创建一个根据给定方向和属性排序的分页对象。
    public static PageRequest of(int page, int size, Sort sort),创建一个根据sort进行排序的分页对象。

  Page,封装分页结果信息,可以通过如下方法获取分页信息。
    page.getContent() ,分页查询结果列表;
    page.getNumberOfElements(),当前分页结果列表中的元素个数;
    page.getTotalElements(),当前条件下总条数;
    page.getTotalPages(),总页数;
    page.getNumber(),我们自己传的page;
    page.getSize(),我们自己传入的size。

 代码示例:

  1. @Test
    void testPagingAndSortingRepository(){
  2.  
  3. // Sort.Order id = Sort.Order.by("id");
    // Sort.Order bookName = Sort.Order.desc("bookName");
    // Sort sort = Sort.by(id,bookName);
  4.  
  5. //等价于上面三句代码
    // Sort sort = Sort.by("id").ascending().and(Sort.by("bookName").descending());
  6.  
  7. //使用类型安全的排序
    Sort.TypedSort<Book> bookTypedSort = Sort.sort(Book.class);
    Sort sort = bookTypedSort.by(Book::getId).ascending()
    .and(bookTypedSort.by(Book::getBookName).descending());
  8.  
  9. Pageable pageable = PageRequest.of(2,2, sort);
  10.  
  11. Page<Book> page = bookRepository.findAll(pageable);
  12.  
  13. System.out.println("分页查询结果列表:" + page.getContent());
    System.out.println("当前分页结果列表中的元素个数:" + page.getNumberOfElements());
    System.out.println("当前条件下总条数:" + page.getTotalElements());
    System.out.println("总页数:" +page.getTotalPages());
    System.out.println("我们自己传的page:" +page.getNumber());
    System.out.println("我们自己传入的size:" +page.getSize());
  14.  
  15. }

5、QueryByExampleExecutor<T>接口

  该接口位于spring-data-commons项目的repository.query包中,允许通过实例来进行查询,可以通过该接口来进行简单的动态查询。使用的话,自己的repository继承QueryByExampleExecutor并指定域类型,就可以使用它提供的功能了。

  1. public interface QueryByExampleExecutor<T> {
  2.  
  3. /**
  4. * 根据Example查找一个对象*/
  5. <S extends T> Optional<S> findOne(Example<S> example);
  6.  
  7. /**
  8. * 根据Example查找一批对象*/
  9. <S extends T> Iterable<S> findAll(Example<S> example);
  10.  
  11. /**
  12. * 根据Example查找一批对象,并排序*/
  13. <S extends T> Iterable<S> findAll(Example<S> example, Sort sort);
  14.  
  15. /**
  16. * 根据Example查找一批对象,并分页排序*/
  17. <S extends T> Page<S> findAll(Example<S> example, Pageable pageable);
  18.  
  19. /**
  20. * 根据Example查找,返回符合条件的对象个数*/
  21. <S extends T> long count(Example<S> example);
  22.  
  23. /**
  24. * 判断符合给定Example的对象是否存在*/
  25. <S extends T> boolean exists(Example<S> example);
  26. }
  1. ExampleMatcher源码分析:
  1. package org.springframework.data.domain;
  2.  
  3. import lombok.AccessLevel;
  4. import lombok.EqualsAndHashCode;
  5. import lombok.RequiredArgsConstructor;
  6. import lombok.experimental.FieldDefaults;
  7.  
  8. import java.util.Collection;
  9. import java.util.LinkedHashMap;
  10. import java.util.Map;
  11. import java.util.Optional;
  12. import java.util.Set;
  13. import java.util.function.Function;
  14.  
  15. import org.springframework.lang.Nullable;
  16. import org.springframework.util.Assert;
  17.  
  18. /**
  19. * 用于示例查询(QBE)的属性路径匹配规范
  20. */
  21. public interface ExampleMatcher {
  22.  
  23. /**
  24. * 创建一个新的匹配器,默认情况下,probe中所有的非空属性都匹配。即所有的属性条件用and连接。
  25. */
  26. static ExampleMatcher matching() {
  27. return matchingAll();
  28. }
  29.  
  30. /**
  31. * 创建一个新的匹配器,probe中所有的非空属性匹配一个即可。即所有的属性条件用or连接。
  32. */
  33. static ExampleMatcher matchingAny() {
  34. return new TypedExampleMatcher().withMode(MatchMode.ANY);
  35. }
  36.  
  37. /**
  38. *创建一个新的匹配器,probe中所有的非空属性都匹配。即所有的属性条件用and连接。
  39. */
  40. static ExampleMatcher matchingAll() {
  41. return new TypedExampleMatcher().withMode(MatchMode.ALL);
  42. }
  43.  
  44. /**
  45. * 返回一个新的匹配器,忽略给定路径的匹配。属性传入这个方法,生成的条件中就不会包含该属性。一般用于基本数据类型。
  46. */
  47. ExampleMatcher withIgnorePaths(String... ignoredPaths);
  48.  
  49. /**
  50. * 返回一个新的匹配器,设置默认的字符串的匹配规则。默认是StringMatcher.DEFAULT(=) 。
  51. *
  52. */
  53. ExampleMatcher withStringMatcher(StringMatcher defaultStringMatcher);
  54.  
  55. /**
  56. * 返回一个新的匹配器,忽略大小写匹配(还要看数据库是否支持大小写区分)。
  57. */
  58. default ExampleMatcher withIgnoreCase() {
  59. return withIgnoreCase(true);
  60. }
  61.  
  62. /**
  63. * 返回一个新的匹配器,设置是否忽略大小写匹配(还要看数据库是否支持大小写区分)。
  64. */
  65. ExampleMatcher withIgnoreCase(boolean defaultIgnoreCase);
  66.  
  67. /**
  68. * 返回一个新的匹配器,设置指定属性的匹配方式(使用lambda方式)。
  69. */
  70. default ExampleMatcher withMatcher(String propertyPath, MatcherConfigurer<GenericPropertyMatcher> matcherConfigurer) {
  71.  
  72. Assert.hasText(propertyPath, "PropertyPath must not be empty!");
  73. Assert.notNull(matcherConfigurer, "MatcherConfigurer must not be empty!");
  74.  
  75. GenericPropertyMatcher genericPropertyMatcher = new GenericPropertyMatcher();
  76. matcherConfigurer.configureMatcher(genericPropertyMatcher);
  77.  
  78. return withMatcher(propertyPath, genericPropertyMatcher);
  79. }
  80.  
  81. /**
  82. * 返回一个新的匹配器,设置指定属性的匹配方式(原始方式)。
  83. */
  84. ExampleMatcher withMatcher(String propertyPath, GenericPropertyMatcher genericPropertyMatcher);
  85.  
  86. /**
  87. * 属性转换器
  88. */
  89. ExampleMatcher withTransformer(String propertyPath, PropertyValueTransformer propertyValueTransformer);
  90.  
  91. /**
  92. * 返回一个新的匹配器,指定属性忽略大小写。
  93. */
  94. ExampleMatcher withIgnoreCase(String... propertyPaths);
  95.  
  96. /**
  97. * 返回一个新的匹配器,将probe中的null属性也作为过滤条件。如:bookName is null。
  98. *
  99. */
  100. default ExampleMatcher withIncludeNullValues() {
  101. return withNullHandler(NullHandler.INCLUDE);
  102. }
  103.  
  104. /**
  105. * 返回一个新的匹配器,将probe中的null属性忽略,不作为过滤条件。
  106. */
  107. default ExampleMatcher withIgnoreNullValues() {
  108. return withNullHandler(NullHandler.IGNORE);
  109. }
  110.  
  111. /**
  112. * 返回一个新的匹配器,设置null值处理器。
  113. */
  114. ExampleMatcher withNullHandler(NullHandler nullHandler);
  115.  
  116. /**
  117. * 获得null处理器。
  118. */
  119. NullHandler getNullHandler();
  120.  
  121. /**
  122. * 获取默认的字符串匹配器。
  123. */
  124. StringMatcher getDefaultStringMatcher();
  125.  
  126. /**
  127. * 如果忽略字符串大小写,返回true。
  128. */
  129. boolean isIgnoreCaseEnabled();
  130.  
  131. /**
  132. * 判断是否是忽略属性。
  133. */
  134. default boolean isIgnoredPath(String path) {
  135. return getIgnoredPaths().contains(path);
  136. }
  137.  
  138. /**
  139. * 获取忽略属性集合。
  140. */
  141. Set<String> getIgnoredPaths();
  142.  
  143. /**
  144. * 属性特定查询方式。
  145. */
  146. PropertySpecifiers getPropertySpecifiers();
  147.  
  148. /**
  149. * 是否是全匹配。
  150. */
  151. default boolean isAllMatching() {
  152. return getMatchMode().equals(MatchMode.ALL);
  153. }
  154.  
  155. /**
  156. * 是否是任意匹配。
  157. */
  158. default boolean isAnyMatching() {
  159. return getMatchMode().equals(MatchMode.ANY);
  160. }
  161.  
  162. /**
  163. * 获取匹配方式。
  164. */
  165. MatchMode getMatchMode();
  166.  
  167. /**
  168. * null处理器枚举。
  169. */
  170. enum NullHandler {
  171.  
  172. INCLUDE, IGNORE
  173. }
  174.  
  175. /**
  176. * 回调配置匹配器。
  177. */
  178. interface MatcherConfigurer<T> {
  179. void configureMatcher(T matcher);
  180. }
  181.  
  182. /**
  183. * 通用属性匹配。
  184. */
  185. @EqualsAndHashCode
  186. class GenericPropertyMatcher {
  187.  
  188. @Nullable StringMatcher stringMatcher = null;
  189. @Nullable Boolean ignoreCase = null;
  190. PropertyValueTransformer valueTransformer = NoOpPropertyValueTransformer.INSTANCE;
  191.  
  192. public GenericPropertyMatcher() {}
  193.  
  194. /**
  195. * 通过字符串匹配器,是否忽略大小写构建GenericPropertyMatcher。
  196. */
  197. public static GenericPropertyMatcher of(StringMatcher stringMatcher, boolean ignoreCase) {
  198. return new GenericPropertyMatcher().stringMatcher(stringMatcher).ignoreCase(ignoreCase);
  199. }
  200.  
  201. /**
  202. *通过字符串匹配器构建GenericPropertyMatcher。
  203. */
  204. public static GenericPropertyMatcher of(StringMatcher stringMatcher) {
  205. return new GenericPropertyMatcher().stringMatcher(stringMatcher);
  206. }
  207.  
  208. /**
  209. * 设置忽略大小写。
  210. */
  211. public GenericPropertyMatcher ignoreCase() {
  212.  
  213. this.ignoreCase = true;
  214. return this;
  215. }
  216.  
  217. /**
  218. * 设置是否忽略大小写。
  219. */
  220. public GenericPropertyMatcher ignoreCase(boolean ignoreCase) {
  221.  
  222. this.ignoreCase = ignoreCase;
  223. return this;
  224. }
  225.  
  226. /**
  227. * 设置区分大小写。
  228. */
  229. public GenericPropertyMatcher caseSensitive() {
  230.  
  231. this.ignoreCase = false;
  232. return this;
  233. }
  234.  
  235. /**
  236. * 包含给定属性值。
  237. */
  238. public GenericPropertyMatcher contains() {
  239.  
  240. this.stringMatcher = StringMatcher.CONTAINING;
  241. return this;
  242. }
  243.  
  244. /**
  245. * 以给定属性值结尾。
  246. */
  247. public GenericPropertyMatcher endsWith() {
  248.  
  249. this.stringMatcher = StringMatcher.ENDING;
  250. return this;
  251. }
  252.  
  253. /**
  254. * 以给定属性值开头。
  255. */
  256. public GenericPropertyMatcher startsWith() {
  257.  
  258. this.stringMatcher = StringMatcher.STARTING;
  259. return this;
  260. }
  261.  
  262. /**
  263. * 精确匹配。
  264. */
  265. public GenericPropertyMatcher exact() {
  266.  
  267. this.stringMatcher = StringMatcher.EXACT;
  268. return this;
  269. }
  270.  
  271. /**
  272. * 默认规则。
  273. */
  274. public GenericPropertyMatcher storeDefaultMatching() {
  275.  
  276. this.stringMatcher = StringMatcher.DEFAULT;
  277. return this;
  278. }
  279.  
  280. /**
  281. * 正则匹配。
  282. */
  283. public GenericPropertyMatcher regex() {
  284.  
  285. this.stringMatcher = StringMatcher.REGEX;
  286. return this;
  287. }
  288.  
  289. /**
  290. * 给定string匹配器。
  291. */
  292. public GenericPropertyMatcher stringMatcher(StringMatcher stringMatcher) {
  293.  
  294. Assert.notNull(stringMatcher, "StringMatcher must not be null!");
  295. this.stringMatcher = stringMatcher;
  296. return this;
  297. }
  298.  
  299. /**
  300. * 设置属性转换器
  301. */
  302. public GenericPropertyMatcher transform(PropertyValueTransformer propertyValueTransformer) {
  303.  
  304. Assert.notNull(propertyValueTransformer, "PropertyValueTransformer must not be null!");
  305. this.valueTransformer = propertyValueTransformer;
  306. return this;
  307. }
  308. }
  309.  
  310. /**
  311. * 用于创建GenericPropertyMatcher。
  312. */
  313. class GenericPropertyMatchers {
  314.  
  315. /**
  316. * 忽略大小写的。
  317. */
  318. public static GenericPropertyMatcher ignoreCase() {
  319. return new GenericPropertyMatcher().ignoreCase();
  320. }
  321.  
  322. /**
  323. * 不忽略大小写的。
  324. */
  325. public static GenericPropertyMatcher caseSensitive() {
  326. return new GenericPropertyMatcher().caseSensitive();
  327. }
  328.  
  329. /**
  330. * 包含。
  331. */
  332. public static GenericPropertyMatcher contains() {
  333. return new GenericPropertyMatcher().contains();
  334. }
  335.  
  336. /**
  337. * 以结尾。
  338. */
  339. public static GenericPropertyMatcher endsWith() {
  340. return new GenericPropertyMatcher().endsWith();
  341.  
  342. }
  343.  
  344. /**
  345. * 以开始。
  346. */
  347. public static GenericPropertyMatcher startsWith() {
  348. return new GenericPropertyMatcher().startsWith();
  349. }
  350.  
  351. /**
  352. * 精确匹配。
  353. */
  354. public static GenericPropertyMatcher exact() {
  355. return new GenericPropertyMatcher().exact();
  356. }
  357.  
  358. /**
  359. * 默认方式。
  360. */
  361. public static GenericPropertyMatcher storeDefaultMatching() {
  362. return new GenericPropertyMatcher().storeDefaultMatching();
  363. }
  364.  
  365. /**
  366. * 正则。
  367. */
  368. public static GenericPropertyMatcher regex() {
  369. return new GenericPropertyMatcher().regex();
  370. }
  371. }
  372.  
  373. /**
  374. * 字符串匹配模式。
  375. */
  376. enum StringMatcher {
  377.  
  378. /**
  379. * 默认,效果同EXACT。
  380. */
  381. DEFAULT,
  382. /**
  383. * 精确,相等。
  384. */
  385. EXACT,
  386. /**
  387. * 开头匹配。
  388. */
  389. STARTING,
  390. /**
  391. * 结尾匹配。
  392. */
  393. ENDING,
  394. /**
  395. * 包含,模糊匹配。
  396. */
  397. CONTAINING,
  398. /**
  399. * 正则匹配。
  400. */
  401. REGEX;
  402. }
  403.  
  404. /**
  405. * 属性转换器,一般不需要设置。
  406. */
  407. interface PropertyValueTransformer extends Function<Optional<Object>, Optional<Object>> {}
  408.  
  409. /**
  410. */
  411. enum NoOpPropertyValueTransformer implements ExampleMatcher.PropertyValueTransformer {
  412.  
  413. INSTANCE;
  414.  
  415. @Override
  416. @SuppressWarnings("null")
  417. public Optional<Object> apply(Optional<Object> source) {
  418. return source;
  419. }
  420. }
  421.  
  422. /**
  423. * 属性特定查询方式
  424. */
  425. @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
  426. @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
  427. @EqualsAndHashCode
  428. class PropertySpecifier {
  429.  
  430. String path;
  431. @Nullable StringMatcher stringMatcher;
  432. @Nullable Boolean ignoreCase;
  433. PropertyValueTransformer valueTransformer;
  434.  
  435. PropertySpecifier(String path) {
  436.  
  437. Assert.hasText(path, "Path must not be null/empty!");
  438. this.path = path;
  439.  
  440. this.stringMatcher = null;
  441. this.ignoreCase = null;
  442. this.valueTransformer = NoOpPropertyValueTransformer.INSTANCE;
  443. }
  444.  
  445. public PropertySpecifier withStringMatcher(StringMatcher stringMatcher) {
  446.  
  447. Assert.notNull(stringMatcher, "StringMatcher must not be null!");
  448. return new PropertySpecifier(this.path, stringMatcher, this.ignoreCase, this.valueTransformer);
  449. }
  450.  
  451. public PropertySpecifier withIgnoreCase(boolean ignoreCase) {
  452. return new PropertySpecifier(this.path, this.stringMatcher, ignoreCase, this.valueTransformer);
  453. }
  454.  
  455. public PropertySpecifier withValueTransformer(PropertyValueTransformer valueTransformer) {
  456.  
  457. Assert.notNull(valueTransformer, "PropertyValueTransformer must not be null!");
  458. return new PropertySpecifier(this.path, this.stringMatcher, this.ignoreCase, valueTransformer);
  459. }
  460.  
  461. public String getPath() {
  462. return path;
  463. }
  464.  
  465. @Nullable
  466. public StringMatcher getStringMatcher() {
  467. return stringMatcher;
  468. }
  469.  
  470. @Nullable
  471. public Boolean getIgnoreCase() {
  472. return ignoreCase;
  473. }
  474.  
  475. public PropertyValueTransformer getPropertyValueTransformer() {
  476. return valueTransformer == null ? NoOpPropertyValueTransformer.INSTANCE : valueTransformer;
  477. }
  478.  
  479. public Optional<Object> transformValue(Optional<Object> source) {
  480. return getPropertyValueTransformer().apply(source);
  481. }
  482. }
  483.  
  484. /**
  485. * 特定属性查询方式集合
  486. */
  487. @EqualsAndHashCode
  488. class PropertySpecifiers {
  489.  
  490. private final Map<String, PropertySpecifier> propertySpecifiers = new LinkedHashMap<>();
  491.  
  492. PropertySpecifiers() {}
  493.  
  494. PropertySpecifiers(PropertySpecifiers propertySpecifiers) {
  495. this.propertySpecifiers.putAll(propertySpecifiers.propertySpecifiers);
  496. }
  497.  
  498. public void add(PropertySpecifier specifier) {
  499.  
  500. Assert.notNull(specifier, "PropertySpecifier must not be null!");
  501. propertySpecifiers.put(specifier.getPath(), specifier);
  502. }
  503.  
  504. public boolean hasSpecifierForPath(String path) {
  505. return propertySpecifiers.containsKey(path);
  506. }
  507.  
  508. public PropertySpecifier getForPath(String path) {
  509. return propertySpecifiers.get(path);
  510. }
  511.  
  512. public boolean hasValues() {
  513. return !propertySpecifiers.isEmpty();
  514. }
  515.  
  516. public Collection<PropertySpecifier> getSpecifiers() {
  517. return propertySpecifiers.values();
  518. }
  519. }
  520.  
  521. /**
  522. * 匹配方式。
  523. */
  524. enum MatchMode {
  525. ALL, ANY;
  526. }
  527. }

示例代码:

  

  1. @Test
  2. void testQueryByExampleExecutor(){
  3.  
  4. Book book = Book.builder().bookName("java").publishDate(LocalDate.of(2019,11,11)).id(1L).build();
  5.  
  6. ExampleMatcher matcher = ExampleMatcher.matching()
  7. .withIgnorePaths("id") //忽略id属性,不管id有没有值,都不作为查询条件。
  8. .withIgnoreNullValues() //忽略属性为null的,不作为查询条件。
  9. .withMatcher("bookName",m -> m.startsWith().ignoreCase()) //设置bookName属性,前包含,忽略大小写。
  10. .withTransformer("publishDate",value -> Optional.of(LocalDate.of(2019,11,12))); //转换属性值
  11.  
  12. Example<Book> example = Example.of(book,matcher);
  13.  
  14. List<Book> books = bookRepository.findAll(example);
  15.  
  16. }

  生成的sql语句:  

    

 Spring-Data-Jpa官网的字符串匹配举例

    

  QueryByExampleExecutor最佳实践:

       首先要判断是否需要我们自己构建匹配器,如果默认匹配器,可以完成,我们就不需要创建。

         判断null值是否要作为条件,一般都是忽略的,如果null值作为条件,将不想作为条件的null属性添加到忽略列表。

       基本类型是有默认值的,如果不作为条件,要加入到忽略列表。

       不同的字符串属性,如果需要不同的匹配方式,进行单独设置。

不是特别复杂的动态查询,使用QBE,还是很方便的。

  1. 源码地址:https://github.com/caofanqi/study-spring-data-jpa

学习Spring-Data-Jpa(六)---spring-data-commons中的repository的更多相关文章

  1. 使用Spring Data JPA的Spring Boot

    本文教你开始使用Spring Data JPA.来自优锐课JAVA架构专业讲师精心整理. 欢迎使用带有Spring Data JPA的Spring Boot教程!在本教程中,我们将看到Spring D ...

  2. Spring Data JPA简介 Spring Data JPA特点

    Spring Data JPA 是Spring基于ORM框架.JPA规范的基础上封装的一套JPA 应用框架,底层使用了Hibernate 的JPA技术实现,可使开发者用极简的代码即可实现对数据的访问和 ...

  3. Spring Data JPA 整合Spring

    1.1   Spring Data JPA 与 JPA和hibernate之间的关系 JPA是一套规范,内部是有接口和抽象类组成的.hibernate是一套成熟的ORM框架,而且Hibernate实现 ...

  4. 整合Spring Data JPA与Spring MVC: 分页和排序

    之前我们学习了如何使用Jpa访问关系型数据库.比较完整Spring MVC和JPA教程请见Spring Data JPA实战入门,Spring MVC实战入门. 通过Jpa大大简化了我们对数据库的开发 ...

  5. 整合Spring Data JPA与Spring MVC: 分页和排序pageable

    https://www.tianmaying.com/tutorial/spring-jpa-page-sort Spring Data Jpa对于分页以及排序的查询也有着完美的支持,接下来,我们来学 ...

  6. Spring Data JPA在Spring Boot中的应用

    1.JPA JPA(Java Persistence API)是Sun官方提出的Java持久化规范.它为Java开发人员提供了一种对象/关联映射工具来管理Java应用中的关系数据.他的出现主要是为了简 ...

  7. <Spring Data JPA>二 Spring Data Jpa

    1.pom依赖 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="htt ...

  8. spring data jpa和spring data redis同时配置时,出现Multiple Spring Data modules found, entering strict repository configuration mode错误

    问题说明 data jpa和data redis同时配置时,出现Spring modules spring Spring Data Release Train <dependencyManage ...

  9. Spring Data JPA 整合Spring 第二篇

    主要是在CustomerDao中去写一些代码,在调用Query中去用SQL 例如 public interface CustomerDao extends JpaRepository<Custo ...

  10. Egret入门学习日记 --- 第十六篇(书中 6.10~7.3节 内容)

    第十六篇(书中 6.10~7.3节 内容) 昨天搞定了6.9节,今天就从6.10节开始. 其实这个蛮简单的. 这是程序员模式. 这是设计师模式. 至此,6.10节 完毕. 开始 6.11节. 有点没营 ...

随机推荐

  1. ubuntu sh脚本激活conda 虚拟环境

    第一步:初始化coda 命令:sudo gedit ~/.bashrc 第二部:复制其中这样一段代码 # >>> conda initialize >>> # !! ...

  2. logrus 剖析之 formatter

    使用 logrus 通过 formatter 来定义输出日志的格式,具体例子如下: package main import ( log "github.com/Sirupsen/logrus ...

  3. vue中$router与$route的区别

    $.router是VueRouter的实例,相当于一个全局的路由器对象.包含很多属性和子对象,例如history对象 $.route表示当前正在跳转的路由对象.可以通过$.route获取到name,p ...

  4. PB数据窗口只存储过程数据源创建

    必须在 Manual Rault Set 上打勾,不然不能设置显示列. 显示列的数据必须和存储过程返回值的顺序一致,否则会出现数据和列名两边不对应的情况

  5. 1.ASP.NET Core介绍

    优点: 1.跨平台,高性能,开源,运行在.Net Core 或.Net Framework框架上(asp.net core 3.0及以后只支持.Net Core). 2.各平台上开发工具支持,能够开发 ...

  6. rabbitmq保证数据不丢失方案

    rabbitmq如何保证消息的可靠性 1.保证消息不丢失 1.1.开启事务(不推荐) 1.2.开启confirm(推荐) 1.3.开启RabbitMQ的持久化(交换机.队列.消息) 1.4.关闭Rab ...

  7. UIAlertController 修改文字显示实现方法

    UIAlertController修改文字显示 不废话先上完整代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 UIAlertControll ...

  8. SQL月度统计

    select Convert ( VARCHAR(7),CreateTime,120) as Date ,sum(Money) as M FROM [LBAmmeterDB].[dbo].Am_Tas ...

  9. php error_log记录日志的使用方法--拿来即用,超简单

    如果本文对你有用,请爱心点个赞,提高排名,帮助更多的人.谢谢大家!❤ 如果解决不了,可以在文末进群交流. 如果对你有帮助的话麻烦点个[推荐]~最好还可以follow一下我的GitHub~感谢观看! 对 ...

  10. Unable to guess the mime type as no guessers are available 2 9

    做了一个上传图片的功能,在本地上传通过,服务器报 bug Unable to guess the mime type as no guessers are available(Did you enab ...