MongoRepository same methods with diferent #Query - java

Using spring data mongo repository class, how can I do this? I need the same method twice but in one I need to exclude a field.
public interface Person extends MongoRepository<Person, String>{
Optional<Person> findById(String id);
#Query(fields="{ 'company': 0 }")
Optional<Person> findById(String id);
}
This doesn't work because I can't have the same method twice, is there a way to do it?

The problem arises when you want to call the method. It would be ambiguous which method is being called.
If you want to use method overloading to have a same name for both of your methods, it is only possible if they have different parameters.
here is an example:
public interface Person extends MongoRepository<Person, String>{
Optional<Person> findById(String id);
#Query(fields="{ 'company': 0 }")
Optional<Person> findById(String id, Boolean exclude);
}

Related

How can I create a personalized method using JPA?

I've been trying to create a personalized get request to find an user by email using Jpa Repository and a Controller class but it doesn't seem to work. Everytime I make a request using an email that exists (!!!!) on the database my method can't find it!
this is my interface:
#Repository
#Transactional
public interface TheUserRepository extends JpaRepository<User, Integer> {
User findByEmail(#Param("email") String email);
}
this is the controllers method:
#GetMapping(value="emails/{email}")
public ResponseEntity<User> findByEmail(String email){
User entity=repo.findByEmail(email);
if(entity!=null) {
return ResponseEntity.ok().body(entity);
}
else {
return ResponseEntity.noContent().build();
}
}
I have tried many other combinations but none of them are working, if anyone has any idea of how to do this request I would appreciate it so much!

How to create custom Spring Data JPA find method with my own property, specification and pageable?

I'd like to create my own custom Spring Data JPA find method that takes Integer, Specification and Pageable.
I've tried creating method like
Page<Student> findAllByGroupId(Integer id, Specification<Student> specification, Pageable pageable);
But it doesn't work.
User Repository
#Repository
public interface UserRepository<T extends User> extends PagingAndSortingRepository<T, Integer>,
JpaSpecificationExecutor<T> {
}
Student Repository
#Repository
public interface StudentRepository extends UserRepository<Student>{
Page<Student> findAllByGroupId(Integer id, Specification<Student> specification, Pageable pageable);
}
Service
#Override
public Page<Student> getGroupStudents(Integer id, StudentQuery studentQuery, Pageable pageable) {
Specification<Student> specification =
studentSpecification.getSpecification(studentQuery);
return studentRepository.findAllByGroupId(id, specification, pageable);
}
When I try this I get
java.lang.IllegalArgumentException: At least 2 parameter(s) provided but only 1 parameter(s) present in query.
Is there any way to create custom find method that takes Integer, Specification and Pageable?

Is there a way to validate method parameters on spring-data-rest repository methods?

For instance, I have a repository class like this.
#RepositoryRestResource
public interface FooRepository extends JpaRepository<Foo, Integer> {
Optional<Foo> findByBarId(#Param("barId") Integer barId);
}
This generates a search endpoint with path http://hostname/foo/search/findByBarId{?fooId}
When I access this URL without any parameters, I get a 404 which I i think is okay.
However, I would rather send a 400 for this type or errors as my business would definitely need a parameter for this API.
I tried using #javax.validation.constraints.NotNull
Optional<Foo> findByBarId(#Param("barId") #NotNull Integer barId);
as well as #org.springframework.lang.NonNull
Optional<Foo> findByBarId(#Param("barId") #NonNull Integer barId);
Both annotations did not work. Of Course it doesn't work because these annotations by itself is just meta information which is not being taken into account by spring-framework.
The documentation didn't have anything showcased for parameter validation behaviour. (They only speak about entity lifecycle validation)
Is there any straightforward way I can achieve such behaviour?
I use spring-boot 2.0.4 if that helps.
You can write a RepositoryRestController to run specific business to customize your endpoints.
Here is an example for your case, you can customize it as well :
#RepositoryRestController
public class FooController {
FooRepository fooRepository;
#Autowired
public FooController(FooRepository fooRepository) {
this.fooRepository = fooRepository;
}
#GetMapping(path = "/foo")
public ResponseEntity getfoo(#RequestParam("barId") Optional<Integer> barId) {
if (!barId.isPresent()) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
}
Optional<Foo> foo = fooRepository.findByBarId(barId.get());
return ResponseEntity.ok(foo);
}
}

Spring Data REST - Override repository findAll without creating /search/findAll URL

Is there any way to prevent Spring Data REST from creating a /search URLs for overridden repository methods?
For example the following code results in a /search/findAll URL being generated which duplicates the functionality of the collection resource:
public interface EmployeeRepository extends CrudRepository<Employee, Long>
{
#Override
#Query("SELECT e FROM Empolyee e")
Iterable<Employee> findAll();
}
This is only a cosmetic issue when overriding a single method but if you attempt to override multiple methods with the same function name and different parameters, for example both findAll methods in PagingAndSortingRepository then spring throws an exception because it's attempting to map 2 functions to the same path.
public interface EmployeeRepository extends PagingAndSortingRepository<Employee, Long>
{
#Override
#Query("SELECT e FROM Employee e")
Iterable<Employee> findAll();
#Override
#Query("SELECT e FROM Employee e")
Iterable<Employee> findAll(Sort sort);
#Override
#Query("SELECT e FROM Employee e")
Page<Employee> findAll(Pageable pageable);
}
Results in:
java.lang.IllegalStateException: Ambiguous search mapping detected. Both public abstract java.lang.Iterable uk.co.essl.roster.entity.employee.EmployeeRepository.findAll(org.springframework.data.domain.Sort) and public abstract java.lang.Iterable uk.co.essl.roster.entity.employee.EmployeeRepository.findAll() are mapped to /findAll! Tweak configuration to get to unambiguous paths!
at org.springframework.data.rest.core.mapping.SearchResourceMappings.<init>(SearchResourceMappings.java:60)
at org.springframework.data.rest.core.mapping.RepositoryResourceMappings.getSearchResourceMappings(RepositoryResourceMappings.java:128)
at springfox.documentation.spring.data.rest.EntityContext.searchMappings(EntityContext.java:107)
...
Is there any way to prevent Spring Data REST from creating a /search URLs for overridden repository methods?
I found following trick to solve this issue:
#Override
default Page<Employee> findAll(Pageable pageable) {
return findBy(pageable);
}
#RestResource(exported = false)
Page<Employee> findBy(Pageable pageable);
More other this trick allows you to set default sort order for 'get all records' request:
#Override
default Page<Employee> findAll(Pageable p) {
if (p.getSort() == null) {
// The default sort order
return findBy(new PageRequest(p.getPageNumber(), p.getPageSize(), Sort.Direction.DESC, "myField"));
}
return findBy(pageable);
}
Enjoy! ))
#RestResource(exported=false) just for overridden method will not help 'cause this blocks GET 'all records' request (
#RestResource(exported = false)

Spring service class contains too many finder methods

I am using Spring framework and Spring Data JPA to develop an application. Below are one of the repository interface and service class.
public interface UserRepository extends JpaRepository<User, Long>
User findByName(String name);
User findByEmail(String email);
}
public class DefaultUserService implements UserService {
#Inject protected UserRepository userRepo;
#Override
public User getUserById(Long id) {
return userRepo.findOne(id);
}
#Override
public User getUserByName(String name) {
return userRepo.findByName(name);
}
#Override
public User getUserByEmail(String email) {
return userRepo.findByEmail(email);
}
}
As stated by many experts, service layer design should be coarse grained and focused on application operations. Looking at the service class above, I believe that is not a good design as it directly expose all finder methods from the repository. Since all 3 service methods above are returning the same object type (User), I would want to expose only one finder method instead of three that able to encapsulate all finder logic.
public class DefaultUserService implements UserService {
#Inject protected UserRepository userRepo;
// what would be the arguments and logic for this method.
#Override
public User getUser() {
}
}
I appreciate if anyone can point me the solution on how to solve this design issue?
I think the design is not so bad, I mean I was seeing that kind of approach several times, in fact you have several finder methods but each one use different property to obtain the User, if you want to make a service method which encapsulate the logic to retrieve the user I would suggest something like this.
public class DefaultUserService implements UserService {
#Inject protected UserRepository userRepo;
enum UserFindEnum{
ID, EMAIL, NAME;
}
public User getUser(UserFindEnum e, Object obj){
switch(e.ordinal()){
case 0:
return userRepo.findOne(obj);
case 1:
return userRepo.findByName(obj);
case 2:
return userRepo.findByEmail(obj);
default:
break;
}
}
}
I mean you need to know which property you will use to find the User so at least one parameter need to be sent to the service layer so getUser() it is not enough. Probably using some kind of logic as above you will have only one service method and needed logic within it.

Categories