Query by Spec

Query by Spec (QBS) provides the QueryBySpecExecutor<T> interface, which includes several query methods:

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.
}

To use these methods, you simply need to extend QueryBySpecExecutor<T> in your existing repository interface:

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

By inheriting QueryBySpecExecutor<T>, you can directly use the query methods in your repository interface, making it easy to perform queries using specifications.

Customize Base Repository

During the configuration process, QBS automatically configures the Spring Data JPA Base Repository. The default implementation is DefaultQueryBySpecExecutor.

However, since Java only supports single inheritance, and to allow your application to retain its original parent Base Repository, QBS provides an extension point called QueryBySpecExecutorAdapter.

Depending on your application’s needs, you can choose to either extend DefaultQueryBySpecExecutor or implement QueryBySpecExecutorAdapter to customize the Base Repository. For example:

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 be 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
  }
}

You can configure your custom Base Repository by setting the spec.mapper.repository-base-class property in your application’s properties file, specifying the full package name of your custom base repository, like this:

spec:
  mapper:
    repository-base-class: com.example.MyRepositoryImpl
Last modified March 10, 2025: docs: fix link (d9ad177)