This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Starter

Spring Starter
    <dependency>
      <groupId>tw.com.softleader.data.jakarta</groupId>
      <artifactId>specification-mapper-starter</artifactId>
      <version>${specification-mapper.version}</version>
    </dependency>
    

    specification-mapper-starter 整合了 specification-mapperSpring Data JPA, 並提供了 Query by Spec 的查詢方式等

    Query by Spec (QBS) 是一個 user-friendly 的查詢方式, 可以動態的建立查詢條件 (Specifications), 透過 QBS interface 就可以執行查詢語句!

    Getting Started

    只要在 pom.xml 中加入 dependency, 此 Starter 在 Spring Boot 啟動過程就會自動的配置一切, 讓你可以零配置的就開始使用, 包含了:

    自動配置預設是啟用的, 你可以透過 properties 中的 spec.mapper.enabled 控制, 如要關閉則:

    spec:
      mapper:
        enabled: false
    

    Query by Spec

    Query by Spec (QBS) 提供了 QueryBySpecExecutor<T> 包含了許多查詢方法:

    public interface QueryBySpecExecutor<T> {
    
      List<T> findBySpec(Object spec);
      
      List<T> findBySpec(Object spec, Sort sort);
      
      Page<T> findBySpec(Object spec, Pageable pageable);
      
      // … more functionality omitted.
    }
    

    只要在原本的 repository interface 中去繼承 QueryBySpecExecutor<T> 就可以直接使用了:

    public interface PersonRepository 
      extends JpaRepository<Person, Long>, QueryBySpecExecutor<Person> {
      ...
    }
    
    @Service
    public class PersonService {
    
      @Autowired PersonRepository personRepository;
    
      public List<Person> findPeople(PersonCriteria criteria) {
        return personRepository.findBySpec(criteria);
      }
    }
    

    Customize the QBS Base Repository

    在配置的過程中, QBS 會自動配置 Spring Data JPA 的 Base Repository, 預設的實作為 QueryBySpecExecutorImpl

    由於 Java 只能單一繼承, 為了應用程式可以保留原有的 Parent Base Repository, QBS 還多提供了 QueryBySpecExecutorAdapter 擴展點

    你的應用程式可以視情況選擇繼承 QueryBySpecExecutorImpl 或實作 QueryBySpecExecutorAdapter 去客製化 Base Repository, 如:

    class MyRepositoryImpl<T, ID> extends SimpleJpaRepository<T, ID>
      implements QueryBySpecExecutorAdapter<T> {
    
      @Setter
      @Getter
      private SpecMapper specMapper;
    
      private final EntityManager entityManager;
    
      MyRepositoryImpl(JpaEntityInformation entityInformation,
                              EntityManager entityManager) {
        super(entityInformation, entityManager);
    
        // Keep the EntityManager around to used from the newly introduced methods.
        this.entityManager = entityManager;
      }
    
      @Override
      public Class<T> getDomainClass() {
        return super.getDomainClass();
      }
      
      @Transactional
      public <S extends T> S mySave(S entity) {
        // implementation goes here
      }
    }
    

    並且透過 properties 中的 spec.mapper.repository-base-class 設定成自定義的 base repository 的 fulll package name, 如:

    spec:
      mapper:
        repository-base-class: com.acme.example.MyRepositoryImpl
    

    Default SpecMapper

    此 Starter 會在 App 啟動的過程中自動的配置一個 Default SpecMapper 並註冊到 Spring @Bean 中, 你可以透過 Autowired 的方式跟 Spring 取得.

    例如, 我想要在轉換成 Specification 後, 先做一些加強再去查詢, 則範例如下:

    class PersonService {
    
      @Autowired SpecMapper specMapper;
      @Autowired PersonRepository personRepository;
    
      List<Person> getPersonByCriteria(PersonCriteria criteria) {
        var spec = specMapper.toSpec(criteria);
        
        // Perform additional operations on the spec, ex:
        // spec = spec.and((root, query, criteriaBuilder) ->  {
        //     ...
        // });
        
        return personRepository.findAll(spec);
      }
    }
    

    Customize SpecificationResolver

    只要將你自定義的 SpecificationResolver 註冊成 Spring @Bean, 在 App 啟動的過程中就會自動的偵測並加入到 Default SpecMapper 中!

    例如, 我想要增加自定義的 Spec Annotation, 配置範例如下:

    @Configuration
    class MyConfig {
    
      @Bean
      SpecificationResolver myResolver() {
        return ...
      }
    }
    

    如果你的 SpecificationResolver 需要用到 SpecMapper 本身, 則你可以包裝成 SpecificationResolverCodecBuilder, 在建構 resolver 時就會把 SpecCodec, 即 SpecMapper 的 interface, 傳進去, 例如:

    @Configuration
    class MyConfig {
    
      @Bean
      SpecificationResolverCodecBuilder myResolver() {
        return MySpecificationResolver::new;
      }
    }
    
    class MySpecificationResolver implements SpecificationResolver {
      
      private final SpecCodec codec;
      
      MySpecificationResolver(SpecCodec codec) {
        // Keep the SpecCodec around to used.
        this.codec = codec;
      }
      
      // implementation goes here
    }
    

    Customize SkippingStrategy

    只要將你自定義的 SkippingStrategy 註冊成 Spring @Bean, 在 App 啟動的過程中就會自動的偵測並加入到 Default SpecMapper 中!

    配置範例如下:

    @Configuration
    class MyConfig {
    
      @Bean
      SkippingStrategy mySkippingStrategy() {
        return ...
      }
    }
    

    Customize ASTWriterFactory

    透過 properties 中的 spec.mapper.impersonate-logger, 可以設定 Logging 過程中, 是否要偽裝成實際處理的 object logger, 預設是關閉的, 若要開啟範例如下:

    spec:
      mapper:
        # 是否要偽裝成實際處理的 object logger, 預設關閉
        impersonate-logger: true
    

    若你需要完整的客製化, 只要將你自定義的 ASTWriterFactory 註冊成 Spring @Bean, 在 App 啟動的過程中就會自動的偵測並加入到 Default SpecMapper 中!

    配置範例如下:

    @Configuration
    class MyConfig {
    
      @Bean
      ASTWriterFactory myASTWriterFactory() {
        return ...
      }
    }
    

    Customize Default SpecMapper

    當然, 你也可以完全的客製化 SpecMapper, 只要將你的 SpecMapper 註冊成 Spring @Bean, App 啟動的過程中就會略過 Default SpecMapper 的配置而優先採用的你所註冊的那個!

    配置範例如下:

    @Configuration
    class MyConfig {
    
      @Bean
      SpecMapper mySpecMapper() {
        return SpecMapper.builder()
          . ...
          .build();
      }
    }