Aspiring Dev 23000
Aspiring Dev 23000

Reputation: 333

How to expose a custom endpoint in SpringDataRest using a default method in Repository?

        @Repository
        @RepositoryRestResource(collectionResourceRel = "myEntity", path = "myEntity")
        public interface MyEntity Repository extends JpaRepository<MyEntity, Long>, JpaSpecificationExecutor<MyEntity> {
    
        default Page<MyEntity > findByFilters(Map<String, String> filters, Pageable pageable){
            // this is a helper class that handles filtering
            Specification<MyEntity > spec = new GenericSpecification<>(filters, MyEntity .class);
            return findAll(spec, pageable);
        }
    
        MyEntity findByName(String name);
    
    }

I'm using SpringDataRest to generate boilerplate CRUD REST endpoints, but for filtering, i have my own logic based on request params, the only solution i found when reading the SpringDataRest docs when it comes to filtering is to declare NamedQueries like findByName and this generates something like this : {{entityPath}}/search/{{propertyNameUsingReflexion}} which takes name as a param.

that's not enough for my use case.

is there a way to expose my findByFilters method as an endpoint using SpringDataRest ?

PS : I do not want to create an endpoint in my controllers for each of my entites to do the filtering.

Upvotes: 0

Views: 61

Answers (1)

Polly Shaw
Polly Shaw

Reputation: 3237

I think I have found a way using the usual method of customising JPA and JDBC repositories.

You need to define another interface which specifies the new method you want to put on the repository:

public interface MyEntitySpecificationExtension {
    Page<ChatUser> findByFilters(Map<String, String> filters, Pageable pageable);
}

I have found it to be necessary to create another repository for implementing the specification:

@Repository
public interface MyEntitySpecificationRepository extends JpaRepository<MyEntity, Long>, JpaSpecificationExecutor<MyEntity> {
}

Now implementMyEntitySpecificationExtension, wrapping this repository implementing the specification:

@Component
public class MyEntitySpecificationExtensionImpl implements MyEntitySpecificationExtension { 
// note from the docs that the 'Impl' suffix is necessary for JPA to be able to find the class to mixin with the repository
    @Autowired
    private MyEntitySpecificationRepository myEntitySpecificationRepository;
    @Override
    public Page<MyEntity> findByFilters(Map<String, String> filters, Pageable pageable) {
        // this is a helper class that handles filtering
        Specification<MyEntity> spec = new GenericSpecification<>(filters, MyEntity .class);
        return myEntitySpecificationRepository.findAll(spec, pageable);
    }
}

You can now remove findByFilters and the JpaSpecificationExecutor from MyEntityRepository, and you need it to extend MyEntitySpecificationExtension:

 @Repository
 @RepositoryRestResource(collectionResourceRel = "myEntity", path = "myEntity")
 public interface MyEntityRepository extends JpaRepository<MyEntity, Long>, MyEntitySpecificationExtension {
        MyEntity findByName(String name);
    
    }

findByFilters now appears in the hypermedia list. How to use the filters query parameter is another question: as I haven't got an implementation of GenericSpecification I haven't been able to test whether it works.

Upvotes: 0

Related Questions