Why my Pageable API is not working with Spring MVC? - spring

Im implementing an API that is working correctly, but when i use pagitation this is returning nothing.
I have the next code for my repository:
public interface VinetaRepository extends JpaRepository<Vineta, Long> {
List<Vineta> findAllByOrderByCreationdateDesc();
List<Vineta> findByTitulo(String titulo);}
and, when i used the next RequestMapping everthings is ok:
#JsonView(VinetaView.class)
#RequestMapping("/api/vinetas2/")
public List<Vineta> getvinetas2(){
return this.vinetarepository.findAll();
}
but, when i use the pagination, my api is returnin anything:
#JsonView(VinetaView.class)
#RequestMapping(value = "/api/vinetaspage/", method= RequestMethod.GET)
public Page<Vineta> getvinetaspage(Pageable page ){
//This System.out print all the size of my repository
System.out.println(this.vinetarepository.findAll().size());
//This for print the title for my 20 first objects Vineta
for(Vineta v:this.vinetarepository.findAll(page)){
System.out.println(v.getTitulo());
}
// This print 20
System.out.println(this.vinetarepository.findAll(page).getSize());
//This print the page number = 0
System.out.println(page.getPageNumber());
//This print the page size = 20
System.out.println(page.getPageSize());
//Finally, this is not returning nothing
return vinetarepository.findAll(page);
}
With the System out, i can see that the size of my query is correct (20), but nothing is returning.
Both function are using the same Jsonview, so this problem is discarded.

An adapter class (like PageWrapper) would be needed to wrap the Spring Data Page interface as the page interface may lack of features.
public class PageWrapper {
}

Related

MapStruct Spring Page to custom object conversion includes check

I am using MapStruct to convert a Page object to a custom object of my application. I am using this mapping in order to convert the content field of the Page object to a list of custom objects found in my data model:
#Mapping(target = "journeys", source = "content")
While this works OK and does convert the elements when content is present, this does not work correctly in case of no Page content. Taking a look at the code seems to show that the following check is added in the generated mapper class:
if ( page.hasContent() ) {
List<JourneyDateViewResponseDto> list = page.getContent();
journeyDateViewPageResponseDto.setJourneys( new ArrayList<JourneyDateViewResponseDto>( list ) );
}
When this is added the mapping action of the inner objects is omitted, meaning that I end up with a null list. I am not really sure as to why and how this check is added but I would like to find a way of disabling it and simply end up with an empty list of elements. Is there a way this can be done using MapStruct?
MapStruct has the concept of presence checkers (methods that have the pattern hasXXX). This is used to decide if a source property needs to be mapped.
In case you want to have a default value in your object I would suggest making sure that your object is instantiated with an empty collection or provide an #ObjectFactory for your object in which you are going to set the empty collection.
e.g.
Default value in class
public class JourneyDateViewPageResponseDto {
protected List<JourneyDateViewResponseDto> journeys = new ArrayList<>();
//...
}
Using #ObjectFactory
#Mapper
public interface MyMapper {
JourneyDateViewPageResponseDto map(Page< JourneyDateViewResponseDto> page);
#ObjectFactory
default JourneyDateViewPageResponseDto createDto() {
JourneyDateViewPageResponseDto dto = new JourneyDateViewPageResponseDto();
dto.setJourneys(new ArrayList<>());
return dto;
}
}
#Mapping(target = "journeys", source = "content", defaultExpression = "java(java.util.List.of())")

Spring Mongo Aggregation that doesn't return duplicates values

I'm working on Spring while using Mongo as database. I have some collections and elements inside it. I'm trying to create a function that will return a random element from collection on every call but not return same element twice.
I have this in my repository class:
#Aggregation(pipeline = {"{'$match':{'typeOfAdventureHolidays':'summerCamps'}}", "
{$sample:{size:1}}"})
AggregationResults<AdventureHolidays> randomSummerCamps();
This return me a random value but also show me same value twice, while I want to avoid that.
I tried to search for some answers but I just founded a couple of ways to prevent it but to not show duplicate elements, no to not return same element twice.
So my code is like this.
Repository
#Aggregation(pipeline = {"{'$match':{'typeOfAdventureHolidays':'summerCamps'}}", "
{$sample:{size:1}}"})
AggregationResults<AdventureHolidays> randomSummerCamps();
ServiceImpl
#Override
public List<AdventureHolidays> getRandomSummerCamps() {
return Collections.singletonList(adventureHolidaysRepository.randomSummerCamps().
getMappedResults().stream().findFirst().orElse(null));
}
Controller
#GetMapping("/getRandomSummerCamps")
public String getRandomSummerCamps(Model model) {
model.addAttribute("randomSummerCamps",
adventureHolidaysService.getRandomSummerCamps());
return "randomSummerCamps";
}
If I need to provide something else I'm here, I tried to find something that will be useful but no success

if/else doese not work in my POST Method of SpringBoot rest api

I use Spring Boot rest api with MongoDB.
In the POST Method, if there is not scoreID and there is not a player with specific date in my collection, because at the same time a player cannot play different games and bring score, then if the specific player and gamecode exist, create a score.
In fact, in the POST Method I used Nested IF-ELSE conditions.
But, in the Postman when I execute POST Request with this data:
{
"scoreid":"s11",
"score":1000,
"player":"sahari",
"gamecode":"g12",
"date":"2020-01-01"
}
always, I recieve an error, in the Postman, 400 Bad Request!, which i defined in the last line of my IF-ELSE statements.
I do not know, what is my mistake and why my program doese not execute IF conditions correct.
The POST Method:
//Create Score
#PostMapping
public ResponseEntity<?> createScore(#RequestBody #JsonView(Views.class) #Valid Score score) {
String p = srepo.findByPlayerName(score.getPlayer());
String g = srepo.findByGameCode(score.getGamecode());
String scoreid = srepo.findByScoreid(score.getScoreid());
Query query = new Query();
query.addCriteria(new Criteria().andOperator(Criteria.where("player").is(score.getPlayer()),
Criteria.where("date").is(score.getDate())));
if((scoreid != null)) {
return ResponseEntity.status(409).body("Conflict!"); }
else
if(mongoTemplate.exists(query, Score.class))
return ResponseEntity.status(409).body("There is not Possible at same time one player brings different Scores!");
else
if((p!= null)&&(g!= null))
{
history = new ArrayList<History>();
h = new History();
h.setScore(score.getScore());
h.setDate(score.getDate());
history.add(h);
hrepo.save(h);
score.setHistory(history);
srepo.insert(score);
return ResponseEntity.ok(score);
}
else
{
return ResponseEntity.status(400).body("Bad Request!");
}
}
The Score Repository:
#Repository
public interface ScoreRepository extends MongoRepository<Score, String>{
#Query("{'scoreid':?0}")
public String findByScoreid(String scoreid);
#Query("{'Player.nickname':?0}")
public String findByPlayerName(String player);
#Query("{'Games.code':?0}")
public String findByGameCode(String game);
}
The problem is not for my IF-ELSE statements.The problem is in the Score Repository
I must return a List instead of String for findByPlayerName and findByGameCode and for findByScoreid which is for checking duplicate in the POST Method I must return type of Score

How can I have two return types in one controller returning Collection of Class A or Object of Class B

I am trying to follow Rest Principles, so I have two rest controllers
with a base URL localhost:8088/trucks.
First, I have to get all trucks:
#GetMapping(value = "/trucks")
public final Collection<TruckDto> getAllTrucks() {
LOGGER.debug("test: getAllTrucks()");
Collection<Truck> trucks = truckService.getAllTrucks();
return mappingService.map(trucks, TruckDto.class);
}
Note, my issue also has to do with the fact that I have different classes I am returning for truckById and truckByTruckCode.
And I have 'get truck by ID' service:
#GetMapping(value = "/trucks/{truckId:[0-9]+}")
#ResponseStatus(HttpStatus.FOUND)
#ResponseBody
public final TruckDto getTruckId(#PathVariable(value = "truckId") final Integer truckId) {
LOGGER.debug("test: truckId({})", truckId);
Truck truck = truckService.getTruckById(truckId);
return mappingService.map(truck, TruckDto.class);
}
Now I have a get by truckCode that works but it doesnt follow the rest principle , which is something like , there should be only one base url and all others build from it , here it is
#ResponseStatus(HttpStatus.OK)
#ResponseBody
#GetMapping(value = "/trucks/{truckCode:[0-9]*[a-zA-Z][a-zA-Z0-9]*}")
public final TruckWithAvgPetrolDto getTruckByTruckCode (#PathVariable(value = "truckCode")
final String truckCode) {
LOGGER.debug("getTruckByTruckCode()");
TruckWithAvgDto truck = truckService.getTruckByTruckCode(truckCode);
return mappingService.map(truck, TruckWithAvgPetrolDto.class);
}
Now it works but I think it should be an optional param there for it should be in one method. So maybe I can have some sort of optional return type because I have two different services, methods to return in both situations.
Because get by id just gets truck detail, but get by trukCode performs a left join and gets truck average consumption of petrol from an order table so I have two different DTOs for get by id and get by truckCode.
My questions are how can I get something like one method say getBY(param) if I put a number it should get by id and return TruckDto but if I put a code like BY788 it should get by code and return a TruckWithAvgPetrolDto. Is that's possible?
This is a solution that works but i will need verification that it is ok to do such in rest or it is bad practice
#GetMapping("/trucks/{value}")
public ResponseEntity<?> getTruckByIdOrCode(#PathVariable(value = "value" )String value) {
if (value.matches("[0-9]*[a-zA-Z][a-zA-Z0-9]*")) {
TruckWithAvgDto list = truckService.getTruckByTruckCode(value);
return new ResponseEntity<TruckWithAvgDto>(list, HttpStatus.FOUND);
}else {
Truck truck = truckService.getTruckById(Integer.parseInt(value));
return new ResponseEntity<Truck>(truck,HttpStatus.FOUND);
}
}

Hibernate queries getting slower and slower

I'm working on a process that checks and updates data from Oracle database. I'm using hibernate and spring framework in my application.
The application reads a csv file, processes the content, then persiste entities :
public class Main() {
Input input = ReadCSV(path);
EntityList resultList = Process.process(input);
WriteResult.write(resultList);
...
}
// Process class that loops over input
public class Process{
public EntityList process(Input input) :
EntityList results = ...;
...
for(Line line : input.readLine()){
results.add(ProcessLine.process(line))
...
}
return results;
}
// retrieving and updating entities
Class ProcessLine {
#Autowired
DomaineRepository domaineRepository;
#Autowired
CompanyDomaineService companydomaineService
#Transactional
public MyEntity process(Line line){
// getcompanyByXX is CrudRepository method with #Query that returns an entity object
MyEntity companyToAttach = domaineRepository.getCompanyByCode(line.getCode());
MyEntity companyToDetach = domaineRepository.getCompanyBySiret(line.getSiret());
if(companyToDetach == null || companyToAttach == null){
throw new CustomException("Custom Exception");
}
// AttachCompany retrieves some entity relationEntity, then removes companyToDetach and adds CompanyToAttach. this updates relationEntity.company attribute.
companydomaineService.attachCompany(companyToAttach, companyToDetach);
return companyToAttach;
}
}
public class WriteResult{
#Autowired
DomaineRepository domaineRepository;
#Transactional
public void write(EntityList results) {
for (MyEntity result : results){
domaineRepository.save(result)
}
}
}
The application works well on files with few lines, but when i try to process large files (200 000 lines), the performance slows drastically, and i get a SQL timeout.
I suspect cache issues, but i'm wondering if saving all the entities at the end of the processing isn't a bad practice ?
The problem is your for loop which is doing individual saves on the result and thus does single inserts slowing it down. Hibernate and spring support batch inserts and should be done when ever possible.
something like domaineRepository.saveAll(results)
Since you are processing lot of data it might be better to do things in batches so instead of getting one company to attach you should get a list of companies to attach processes those then get a list of companies to detach and process those
public EntityList process(Input input) :
EntityList results;
List<Code> companiesToAdd = new ArrayList<>();
List<Siret> companiesToRemove = new ArrayList<>();
for(Line line : input.readLine()){
companiesToAdd.add(line.getCode());
companiesToRemove.add(line.getSiret());
...
}
results = process(companiesToAdd, companiesToRemove);
return results;
}
public MyEntity process(List<Code> companiesToAdd, List<Siret> companiesToRemove) {
List<MyEntity> attachList = domaineRepository.getCompanyByCodeIn(companiesToAdd);
List<MyEntity> detachList = domaineRepository.getCompanyBySiretIn(companiesToRemove);
if (attachList.isEmpty() || detachList.isEmpty()) {
throw new CustomException("Custom Exception");
}
companydomaineService.attachCompany(attachList, detachList);
return attachList;
}
The above code is just sudo code to point you in the right direction, will need to work out what works for you.
For every line you read you are doing 2 read operations here
MyEntity companyToAttach = domaineRepository.getCompanyByCode(line.getCode());
MyEntity companyToDetach = domaineRepository.getCompanyBySiret(line.getSiret());
You can read more than one line and us the in query and then process that list of companies

Resources