Querying mongodb collection SpringWebFlux with reactivemongodb - spring-boot

I am developing simple spring webflux demo application with reactive mongodb and i want to read all data of Employee by name except containing name field "joe","Sara","JOE","SARA" and i have following code like:
//repository interface
public interface EmployeeRepository extends ReactiveMongoRepository<Employee, String>{
Flux<Employee> findAllByName(String name);
}
//Service class
public class EmplyeeService
{
private EmployeeRepository employeeRepository;
public Flux<Employee> findAllByOrganizationName(String name)
{
return employeeRepository.findAllByName(name);
}
public Flux<String> getAllNameExceptSome(String name)
{
Employee emp1=new Employee();
List<Flux<Employee>> emp=Arrays.asList(employeeRepository.findAllByName(name));
Flux<Flux<Employee>> emp2=Flux.fromIterable(emp)
.filter(name->name.equalsIgnoreCase("joe"));
return emp2;
}
}

First of all, unless some particular situations, you should avoid these data structures:
List<Flux<Employee>>
Flux<Flux<Employee>>
However you are not leveraging Spring Data. You can achieve you result simply changing your repository to:
public interface EmployeeRepository extends ReactiveMongoRepository<Employee, String> {
// this find all Employee except those matching names provided as param
Flux<Employee> findAllByNameNotIn(List<String> nameList);
// this find all Employee matching names provided as param
Flux<Employee> findAllByNameIn(List<String> nameList);
}
Invoking this method you will obtain the list of Employee already filtered by name.

Related

Interesting task with service layer in Controller Spring Boot

So, i have controller, that should return List of Courses (2 course in MYSQL), where i can find Course by name.
When i run application, i don't see anything course, but i can find necessary course in the search column. Example: write "java" and application print me.
#GetMapping ("/index")
public String findCourse (Model model, #RequestParam (name="keyword", defaultValue = "") String keyword ) {
List <Course> course = courseService.findCourseBycourseName(keyword);
model.addAttribute("listCourses", course);
model.addAttribute("keyword", keyword);
return "views/courses";
}
When i change main method in Controller, i see List Course, but i can not search this course. Button search don't work.
List <Course> course = courseService.findCourseBycourseName(keyword);
to --> List <Course> course = courseService.fatchAll();
Who know where my problem, and how and where i can see what findCourseBycourseName returned me?
Service layer.
public interface CourseService {
List <Course> findCourseBycourseName(String keyword);
List <Course> fetchAll();
ServiceImpl
#Override
public List <Course> findCourseBycourseName(String keyword) {
return courseDao.findCourseBycourseName(keyword);
}
#Override
public List<Course> fetchAll() {
return courseDao.findAll();
}
And Dao
public interface CourseDao extends JpaRepository <Course, Long> {
List <Course> findCourseBycourseName (String keyword);

#RepositoryRestResource changes url every time the application is restarted

I have a repository interface that extends JpaRepository and a NameRepositoryCustom.
My repository is annotated with #RepositoryRestRessource(collectionResourceRel="pathname", path="pathname").
The problem I have is that every second restart of my application the URL of the repository gets changed so I can't find the exposed data of the repository under the URL I defined and some features like the search of the repository aren't exposed in the API anymore either.
The "NameRepositroyCustom" is used for a search function which uses another Repository to implement Specification with JPA Criteria Api for a searchbar in my frontend.
Does anybody have a solution for this? The only repository annotated as #RepositoryRestRessource is the main repository that implements all the others. The NameRepositorySpec is annotated with #Repository, could this maybe be the cause?
Edit: I implemented the code as an example to clarify the relations between the mentioned classes and interfaces.
This is the basic repository related to the entity persisted in the database:
#RepositoryRestResource(collectionRessourceRel = "enitynames", path = "entitynames")
public interface EntitynameRepository extends JpaRepository<Entityname, Long>, EntitynameRepositoryCustom{
//custom methods in here
}
This is the custom repository:
public interface EntitynameRepositoryCustom {
Page<Entityname> search(String exampleParam1, String exampleParam2, Pageable pageable);
}
This is the implementation of the custom repository:
public class EntitynameRepositoryCustomImpl implements EntitynameRepositoryCustom{
#Autowired
EntityManager em;
#Autowired
EntitynameRepositorySpec entitynameRepositorySpec;
Specification<Entityname> querySpecification = null;
#Override
public Page<Entityname> search(String exampleParam1, String exampleParam2, Pageable pageable) {
//Code here uses the criteria builder and Specification to generate a custom query with optional parameters
CriteriaBuilder cb= em.getCriteriaBuilder();
CriteriaQuery<Entityname> cq = cb.createQuery(Entityname.class);
//Code below is done for every passed in parameter
if(exampleParam1 != null){
Specification<Entityname> param1Specification = EntitynameSpecification.likeParam1(exampleParam1);
querySpecification = Specification.where(param1Specification);
} else {
return null;
}
return entitynameRepositorySpec.findAll(specification, pageable);
}
}
This is the specification repository:
public interface EntitynameRepositorySpec extends JpaRepository<Entityname, Long>, JpaSpecificationExecutor<Entityname>{
}
And this is the implementation of the specification:
public class EntitynameSpecification {
public static Specification<Entityname> likeExampleParam1(String exampleParam1){
if(exampleParam1 == null){
return null;
}
return(root, query, cb) -> {
reutrn cb.like(root.get("fieldname"), "%"+ exampleParam1 + "%");
};
}
}
The URL of the repository gets changed to a part of the entity name compared to my example it would be something like: entityname has URL: /entityname
if the bug occurs the URL changes to /name.

Spring - Access a Service interface programmatically

i have several interfaces which extend a single interface.
I need to add, during a #PostCostruct method, these interfaces to a Map.
The problem is that i need to retrieve the #Service class name from the DB and i don't know ho to put the interface in the map...
I'll try to explain it better
I have a general service interface
public interface IVehicleServiceGeneral{
//methods...
}
then i have several interfaces which extend the general one.
public interface IService1 extends IVehicleServiceGeneral{
}
public interface IService2 extends IVehicleServiceGeneral{
}
the concrete implementations of these classes are annotated with #Service("service1Name"), #Service("service2Name") and so on...
Then from the DB i retrieve my Suppliers
public class Supplier {
private long id;
private String serviceName;
//getters and setters
}
Finally i need to create the map, because i need to access the implementations at runtime based on the Supplier, i created a ContextAware class to get my beans by name, but the interfaces are not beans... I also tried to put the #Qualifier on the interface, but obviously it does not work... How can I put the interface in the map?
#PostConstruct
private void createServiceMap(){
serviceMap = new HashMap<OBUSupplier, IVehicleServiceGeneral>();
List<Supplier> suppliers = supplierService.findAll();
for(Supplier s : suppliers) {
serviceMap.put(s, contextAware.getBean(s.getServiceName()));
}
}
You can create IVehicleServiceGeneral instance map like this:
class SomeClass {
Map vehicleServiceGeneralInstanceMap = new HashMap();
SomeClass(Set<IVehicleServiceGeneral> instances) {
instances.forEach(i -> vehicleServiceGeneralInstanceMap.put(i.getServiceName(), i));
}
private void createServiceMap() {
Map serviceMap = new HashMap<OBUSupplier, IVehicleServiceGeneral>();
List<Supplier> suppliers = supplierService.findAll();
for(Supplier s : suppliers) {
serviceMap.put(s, vehicleServiceGeneralInstanceMap.get(s.getServiceName()));
}
}
The only thing you require is IVehicleServiceGeneral#getServiceName which your Service1, 2 need to override with proper names that present in DB.

Spring REST projection does not kick in

I have a domain with uml diagram
here.
I have declared JpaRepositoryes for classes: Invoice, Contract, Consultant, User, Region
I don't want to expose the repositories as they are because I will need to wrap some business rules around them.
I have defined a few projections and they seem to work with my repositories yet they do not kick in with my #RestController
Here is a draft of my wrapper:
#RestController
#RequestMapping("/contractService")
public class ContractServiceImpl extends ServiceImpl<Contract, Long> implements IService<Contract, Long>
{
#RequestMapping("/all")
public List<Contract> all()
{
return findAll();
}
#RequestMapping(value = "/one", method = GET)
public Contract one(#RequestParam(value = "id", defaultValue = "1") Long id)
{
log.info(format("id=%d", id));
return repository.getOne(id);
}
#Autowired
RepositoryRestConfiguration rrc;
#RequestMapping("config")
public List<String> getConfig()
{
return rrc.projectionConfiguration()
.getProjectionsFor(Contract.class)
.entrySet().stream()
.map(e -> e.getKey() + "->" + e.getValue())
.collect(Collectors.toList());
}
Here the last method is to verify if projections got configured. i.e.:
http://localhost:8080/invoiceapi/contractService/config
gets me the result as:
[
"brief->interface e.invoice.entity.projection.Brief",
"contractEdit->interface e.invoice.entity.projection.ContractEdit"
]
My projection is defined as:
#Projection(name="brief", types={Contract.class, Consultant.class, User.class, Region.class})
public interface Brief
{
public Long getId();
public String getName();
}
Rest calls into repository directly returns me the projected results as desired, id and name only:
http://localhost:8080/invoiceapi/contracts?projection=brief
http://localhost:8080/invoiceapi/contracts/1?projection=brief
However those into my controller:
http://localhost:8080/invoiceapi/contractService/all?projection=brief
http://localhost:8080/invoiceapi/contractService/one?id=2&projection=brief
they recursively spider all reachable entities as far as the defined JpaRepositories go. (The Application objects below User do not show up)
My controller returns a document with Content-Type: application/json while JpaRepository based one returns a cool looking Content-Type: application/hal+json. Their output are different as well: My controller returns a more straightforward output while JpaRepository based one puts associated objects into an array called _embedded.

Using QueryDslRepositorySupport in combination with interface repositories

since I didn't get a reply on the spring forum I'll give it a try here.
Is there a way to have a common interface repository which is extended by interfaces the following way:
#NoRepositoryBean
public interface CommonRepository<T> extends JpaRepository<T, Long>, QueryDslPredicateExecutor<T> {
T getById(final long id);
}
#Repository
public interface ConcreteRepository extends CommonRepository<ConcreteEntity> {
List<ConcreteEntity> getByNameAndAddress(final String name, final String address);
}
public class ConcreteRepositoryImpl extends QueryDslRepositorySupport implements ConcreteRepository {
private BooleanExpression nameEquals(final QConcreteEntity entity, final String name) {
return entity.eq(name);
}
public List<ConcreteEntity> getByNameAndAddress(final String name, final String address) {
QConcreteEntity entity = QConcreteEntity.concreteEntity;
return from(entity).where(entity.name.eq(name).and(entity.address.eq(address))).list(entity);
}
}
The problem with the implementation is that I have to implement getById(final long id)
in each concrete class. I don't want to do that. Normally, spring data automatically knows about each entity. Also I want to have the functionality of QueryDslRepositorySupport.
In my example it normally generates something like:
select .. from concreteentity en where en.id = ...
Is there a way to solve it? I already stumbled upon
Spring Jpa adding custom functionality to all repositories and at the same time other custom funcs to a single repository
and
http://docs.spring.io/spring-data/data-jpa/docs/current/reference/html/repositories.html#repositories.custom-implementations
but I don't think these solutions are helpful and I don't entirely understand how I can use them to solve the problem.
Thanks,
Christian
One way to create a generic getById under QuerydslRepositorySupport is like this
T getById(long id) {
return getEntityManager().find(getBuilder().getType(), id)
}

Resources