How to expose custom implementation of Spring Data Rest endpoint - java

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!

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

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:

Related

Facing issue with Guava Cache

I'm using Google Guava Cache + Spring cache abstraction for caching purpose.
I'm trying to make use of Guava's Loading Cache interface for the same.
I know Spring provides support for Guava Cache, but I was wondering whether I can make use of spring's cacheable annotation alongwith Loading Cache?
Basically I wanted to keep the business layer separate from the Cache.
Kindly help. Thanks.
Guava Cache is deprecated. If you'd existing code, that'd be another matter, but for new code, use Caffeine.
Put a #Cacheable("myCacheName") on the method that you want to cache the return value for.
Put a #EnableCaching on your application class if using Spring Boot, otherwise on some #Configuration class.
Set the spec in application.properties if using Spring Boot, like so: spring.cache.caffeine.spec=maximumSize=10000,expireAfterWrite=5m. If not using Boot, use #PropertySources annotation on the same class as in #3 above.
Add org.springframework.boot:spring-boot-starter-cache and com.github.ben-manes.caffeine:caffeine to your build file. If not using Boot, you'll need to set up the dependencies differently.
You're done.
So you want both butter and jam. Okay. I will help you use loading cache along with keeping caching logic separate.
Consider you have a service class SampleServiceImpl which implements SampleService interface.
Service interface:
public interface SampleService {
User getUser(int id);
}
Service Implementation:
#Service
public class SampleServiceImpl implements SampleService {
public User getUser(int id) {
// fetch user from database
return user;
}
}
Create one more class SampleServiceCache
public class SampleServiceCache extends ServiceCacheImpl {
#Autowired
public SampleServiceCache(int expiryTime, int maximumSize) {
loadingCache =
CacheBuilder.newBuilder().maximumSize(maximumSize).expireAfterAccess(expiryTime, TimeUnit.HOURS).build(
new CacheLoader<Integer, User>() {
#Override
public User load(#Nonnull Integer userId) {
return SampleServiceCache.super.getUser(userId);
}
});
}
#Override
public User getUser(int userId) {
return loadingCache.getUnchecked(userId);
}
}
In you bean config:
#Bean
public SampleService sampleService() {
return new SampleServiceCache(expiry, maxSize);
}
The day you want to remove cache, you have to do two things:
1. Remove the cache class.
2. Change bean config to return actual implementation object rather than cache implementation object.
P.S. You can define multiple loading caches for different behaviors say user retrieval, article retrieval, etc.

Spring Boot program structure for access to data objects

I have an spring boot app that has a mongo repository and is using spring data to connect to it. This means there's an "entity" class and then a "repository" class.
However, to actually use the repository I need spring to #Autowire my repo to a variable inside another class (we'll call it X) that might want to use it. But if the X class is itself not a spring bean, it's just a regular pojo created by "new X" somewhere then it can't make use of Autowiring and therefore cant use the repo.
This seems like a show stopper... No one could ever make use of a repo outside of some very specific situations like calling the repo directly from your RestController or whatever without any intervening logic. Yet I am sure people are using this.
So my question is how to structure code so that it can be used? Do I need to do a bunch of processing and then return back up to the controller to interact with the databases? Is there a way to create some kind of other "intermediate helper bean" to mediate the connection? What should that look like?
disclaimer: I am fairly new to spring
Perhaps I can post some excerpts that can clarify your situation.
//nothing spring specific
public class MyPojo {
//properties
}
in a different package:
#Repository
public class MyRepositoryImpl implements MyRepository {
//CRUD implementation or whatever
}
in a different package:
#Service
public class MyServiceImpl implements MyService {
#Autowired //constructor-injection
public MyService(MyRepository myRepository) {
this.myRepository = myRepository;
}
private final MyRepository myRepository;
public void myBusinessLogic() {
MyPojo pojo = new MyPojo(); //not dependent on Spring
myRepository.doSomething();
//place calls to X here as needed
}
}
And finally:
#Controller
public class MyController {
#Autowired
public MyController(MyService myService) {
this.myService = myService;
}
private final MyService myService;
#GetMapping("/myPage")
public String doIt() {
myService.myBusinessLogic();
return "myPage";
}
}
Where MyRepository and MyService are interfaces that would contain the contract for their respective implementations.
Single Responsibility Principle
A major point to note is that your POJO isn't going to "use the repo" as you mention in your question. It represents an entity and shouldn't care about any specific repository. And this seems related to your issue - a POJO shouldn't be making calls to a repository (your "X" class in this case). Seems like the design should be revisited if that is the case.
As you say, you can only autowire fields in objects that themselves are autowired. This is inherent to bean injection. You should annotate X with for instance #Component and inject it where you need it.

Spring Data REST repository 404 from time to time

I have the Spring Boot application with Spring Data REST.
I have the following classes in it:
Data JPA repository for authentication purposes:
public interface UserRepository extends JpaRepository<User, Long> {
User findByUsername(String username);
}
Secured Data REST repository for API usages:
#RepositoryRestResource
#Secured(Role.ROLE_USER_READ)
public interface UserDataRestRepository extends PagingAndSortingRepository<User, Long> {
#Override
#Secured(Role.ROLE_USER_WRITE)
<S extends User>S save(S entity);
#Override
#Secured(Role.ROLE_USER_DELETE)
void delete(Long id);
}
Repository REST configurer adapter:
#Configuration
public class RepositoryRestConfig extends RepositoryRestConfigurerAdapter {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.setRepositoryDetectionStrategy(RepositoryDetectionStrategies.ANNOTATED);
config.setReturnBodyOnCreate(true);
config.setReturnBodyOnUpdate(true);
config.setReturnBodyForPutAndPost(true);
}
}
The problem is when I start up my application, Data REST repository for API is unavailable from time to time. I guess it's because Spring rewrites repository bean for type User with the first JPA repository.
In Actuator beans endpoint I can see both beans even if REST API says 404 for /users page.
Again, this behavior is unpredictable for me - sometimes it works, sometimes doesn't.
Do you know the way how to tell to Spring to use the exact bean for Data REST?
Thanks in advance.
After some investigation, I've found exactly the same issue posted here.
Also, see this one.
Finally, I've merged 2 repositories into one and it solved my problem. But this is definitely not the way I prefer.

SpringBoot Rest api

I want to make a simple java application and I want to use CRUDREPOSITORY and my own repository. I have this:
#RestController
#Transactional
#ExposesResourceFor(Person.class)
#RequestMapping("/prueba")
public class PersonController {
#RequestMapping(value="/prueba", method=RequestMethod.GET)
public String person(#RequestParam(value="id", defaultValue="1") int id) {
return "hola"+id;
}
}
this:
#RepositoryRestResource
public interface IClientRepository extends CrudRepository<Client, Long> {
}
The problem is that the CRUD works well, but I canĀ“t call my localhost:8080/prueba/prueba because it gives a 404 error. I have try all and I cant access it, please help me!!
By default, Spring Data REST serves up REST resources at the root URI, "/". There are multiple ways to change the base path.
With Spring Boot 1.2+, add below to application.properties:
spring.data.rest.basePath=/api
In your case:
spring.data.rest.basePath=/prueba/prueba
, assuming there is no override for server.contextPath in application.properties
Reference:
http://docs.spring.io/spring-data/rest/docs/current/reference/html/

Does using annotations to inject dependencies remove the main benefit of dependency injection(external configuration)?

I am using Spring, here is a controller:
#Controller
public class PersonController {
#Resource(name="PersonService")
private PersonService personService;
#RequestMapping(value = "/Person", method = RequestMethod.GET)
public String getPersons(Model model) {
// Retrieve all persons by delegating the call to PersonService
List<Person> persons = personService.getAll();
// Attach persons to the Model
model.addAttribute("persons", persons);
//then return to view jsp
}
and here is a service :
#Service("personService")
#Transactional
public class PersonService {
public List<Person> getAll() {
//do whatever
}
}
However, to properly make use of DI I should change the controller to make use of an interface (?) like so:
#Controller
public class PersonController {
#Resource(name="personService")
private IPersonService personService; //Now an interface
}
This would then allow me, for example, to use two services one test and one live. Which I could alter by adding/removing the annotation on the services :
#Service("personService") // this line would be added/removed
#Transactional
public class LivePersonService implements IPersonService {
public List<Person> getAll() {
//do whatever
}
}
and
#Service("personService") //this line would be added/removed
#Transactional
public class TestPersonService implements IPersonService {
public List<Person> getAll() {
//do something else
}
}
However one of the main benefits is lost due to the fact that the code has to be recompiled ? Whereas if I used xml lookup I could alter the dependency on-the-fly ?
The configuration is still external, because it is outside where you define which implementation is going to be injected. Inside the class, you just hardcode the "name" of something the class depends on (which is ok, because this dependency is inherent to the class).
This said, you can use XML to override the annotations of your code for the tests execution (you would have a specific XML application context for your tests) and specify which implementation you will inject.
Therefore, you don't need to change your code to run the tests. Take a look to this answer.
Well that's correct. Annotations are configuration inside the source code. Mainly intended when you have one class for each service. If you have more than one implementation for a particular interface, then XML will be a better option. Also you can mix XML configutation with annotations.
The conventional way, that I heard last time from DI camp, is that in unit tests, you shouldn't use the DI framework. Rather, simply instantiate mock service yourself and set it to the host object
test()
PersonController contr = new PersonController();
contr.personService = new TestPersonService();
// testing contr
This was hailed as the first and major achievement of DI, much to the puzzlement of people (like me) who don't get it. See my previous criticisms: advantage of using applicationcontext.getbean vs #configurable
If the DI supporters in this thread reflect the new trend in DI camp, they no longer do unit tests that way; instead tests depend on DI too, with a test specific DI config. Then it's really no different from service locator pattern. If the major feature of DI is moot, then what's the point?
Your controller class perfectly illustrated that. It cannot be used outside Spring DI framework as a POJO. There's nothing POJO about it. And nobody cares, rightfully. Same thing if your class depends on a service locator framework.
There are other features provided by Spring beans framework, none of them depends on DI; they can be implemented in a service locator framework just as well. Many people when defending DI the design pattern, are actually defending Spring the entire stack. You can actually use Spring as a service locator framework; Spring will not advertise this now, it's a blow to its main hype point; but it will once the hype weakens and it must appeal to the doubters.

Categories

Resources