2

I have a following problem. I made an application which uses spring-data and exposes it as a REST service using spring-data-rest. Everything went smooth till I wanted to have a custom implementation. I've created a CustomSomethingRepository and SomethingRepositoryImpl with one additional method. Spring data repository interface extended CustomSomethingRepository and everything was fine, I was able to execute my method from test directly, custom implementation was executed as well. Then I tried to get it through REST api and here I was surprised that this method is not available through /somethings/search . I'm almost hundred percent sure that it worked fine in spring boot 1.3.x and JpaRepositories. Now I'm using boot 1.5.x and MongoRepository. Please take a look at my example code:

@RepositoryRestResource
public interface SomethingRepository extends CrudRepository<Something>, CustomSomethingRepository {

    //this one is available in /search 
    @RestResource(exported = true)
    List<Something> findByEmail(String email);
}

and custom interface

public interface CustomSomethingRepository {
    //this one will not be available in /search which is my problem :(
    List<Something> findBySomethingWhichIsNotAnAttribute();
}

and implementation

@RepositoryRestResource
public class SomethingRepositoryImpl implements CustomSomethingRepository {

    @Override
    public List<Something> findBySomethingWhichIsNotAnAttribute() {
        return new ArrayList<>(); //dummy code
    }
}

Could you please give me a hint how can I expose CustomSomethingImpl as a part of Rest endpoint without creating another regular spring mvc bean which will be just handling this single request?

I've read questions like this: Implementing custom methods of Spring Data repository and exposing them through REST which state that this is not possible to achieve, but believe me or not, I had a project with spring-boot 1.3.x and those implementations were exposed as well :).

Thank you!

Michal Plewka
  • 291
  • 1
  • 2
  • 9
  • Maybe my [howto](https://stackoverflow.com/q/45401734) will be helpful.. – Cepr0 Sep 03 '17 at 08:00
  • Hi, thank you for your answer but it does not expose Custom implementation, you are creating another Controller only which is a workaround, I would say. The problem is how to expose custom implementation automatically which is available from spring data level. – Michal Plewka Sep 03 '17 at 11:44
  • My inattention.. )) – Cepr0 Sep 03 '17 at 16:25
  • Since your custom method is returning a List you should put it in Something Repository which spring data rest will put it on the /search path. Add List findByNotAttribute() – patelb Sep 08 '17 at 22:22

2 Answers2

0

Since your custom method is returning a List you should put it in SomethingRepository which spring data rest will put it on the /search path. Add List findByNotAttribute()

@RepositoryRestResource public interface SomethingRepository extends CrudRepository<Something> { 
@RestResource(exported = true)
List<Something> findByEmail(String email);

List<Something> findByNotAttribute(@Param String attribute);
}
patelb
  • 2,491
  • 19
  • 18
  • Yes, but it will not use a custom implementation from SomethingRepositoryImpl which is my goal. – Michal Plewka Sep 25 '17 at 11:57
  • @MichalPlewka you should not provide method body when method is annotated by `@Query` annotation IMHO. Try in your interface just method name, return type and annotation, like you have `findByEmail` for example. See https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.at-query for example. – Lubo Mar 06 '20 at 13:56
0

So, I have the exact same question as you. I have a not fully tested solution because Im still working on it. I don't like it because it seems to be a bit hacky...Also I haven't tested it out fully. This is how far I have gotten. In your CustomSomethingRepository add the @Query annotation to the method you want to expose. Inside the annotation add a valid query.

public interface CustomSomethingRepository {
     @Query("select smt from Something smt")
     List<Something> findBySomethingWhichIsNotAnAttribute();

Now in the class that implements your CustomSomethingRepository

@Repositorypublic
@Transactional(readOnly = true)
class SomethingRepositoryImpl implements CustomSomethingRepository {

     @PersistenceContext
     private EntityManager entityManager;

     @Override
     public List<Something> findBySomethingWhichIsNotAnAttribute() {
          System.out.println("HELLO");
     }
 }

Now when you go to http://localhost/something/search you should see something like

{
  "_links" : {
    "findByEmail" : {
      "href" : "http://localhost/something/search/findByEmail{?email}"
    },
    "findBySomethingWhichIsNotAnAttribute" : {
      "href" : "http://localhost/something/search/findBySomethingWhichIsNotAnAttribute"
    },
    "self" : {
      "href" : "http://localhost/something/search/"
    }
  }
}

When you point your browser to http://localhost/something/search/findBySomethingWhichIsNotAnAttribute you will see HELLO printed and the query inside the @Query annotation will NOT run.

I'm facing another problem. In the SomethingRepositoryImpl I want to be able to call the findAll() method(s) in the SomethingRepository but if I autowire the SomethingRepository to SomethingRepositoryImpl the application errors out because it detects a cycle.

The dependencies of some of the beans in the application context form a cycle:

Francisco
  • 145
  • 1
  • 3
  • 13
  • Query from `@Query` annotation is not being executed, because you have provided your own body for annotated method (body, which prints HELLO). Try to have a look at https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.at-query or just remove method body (through keep annotation there). – Lubo Mar 06 '20 at 13:58