Returning Page<Object> in Query DSL - spring

I am using Query DSL and want my result set to return a Page Object. Is there a way to do that in Query DSL? If so Whats my query going to be looking like?
I am using JPAQuery and I have my QClasses
The Method structure is this
public Page<Object> searchPerson(String name,String phone){
Page<Object> results=null;
JPQLQuery query = new JPAQuery(entityManager);
QPerson person = QPerson.person;
//I am assuming my query would go here
results = query.from(person). ?????
return results;}
Help!

Here is my implementation for Paging with QueryDSL. A PageRequest defines the parameters for our query (limit and page):
public class PageRequest {
protected Long page = 1l;// 1 is the first page
protected Integer limit = 10;
public PageRequest(Long page, Integer limit) {
this.limit = limit;
this.page = page;
}
public Long getPage() {
return page;
}
public Integer getLimit() {
return limit;
}
public Long getOffset() {
return (page - 1l) * limit;
}
}
The Page Class contains the result (here the attribute objects) of the query and could implement methods to create nice paging links.
public class Page<T> extends PageRequest {
protected Collection<T> objects;
private Long totalCount;
private Long pageCount;
private Boolean hasPageLinkPrev;
private Boolean hasPageLinkNext;
private Collection<Long> pageLinks;
public Page(Long page, Integer limit, Long totalCount, Collection<T> objects) {
this.page = page;
this.limit = limit;
this.totalCount = totalCount;
this.objects = objects;
this.pageCount = totalCount / limit;
if (totalCount % limit > 0) {
this.pageCount = this.pageCount + 1;
}
this.hasPageLinkPrev = page > 1;
this.hasPageLinkNext = page < this.pageCount;
this.pageLinks = new ArrayList<>();
if (this.pageCount != 1) {
this.pageLinks.add(1l);
if (page > 3l) {
this.pageLinks.add(-1l);
}
if (page > 2l) {
if (page.equals(this.pageCount) && this.pageCount > 3l) {
this.pageLinks.add(page - 2l);
}
this.pageLinks.add(page - 1l);
}
if (page != 1l && !page.equals(this.pageCount)) {
this.pageLinks.add(page);
}
if (page < this.pageCount - 1l) {
this.pageLinks.add(page + 1l);
if (page == 1l && this.pageCount > 3l) {
this.pageLinks.add(page + 2l);
}
}
if (page < this.pageCount - 2l) {
this.pageLinks.add(-1l);
}
this.pageLinks.add(this.pageCount);
}
}
public Page(PageRequest pageRequest, Long totalCount, Collection<T> objects) {
this(pageRequest.getPage(), pageRequest.getLimit(), totalCount, objects);
}
public Long getTotalCount() {
return this.totalCount;
}
public Long getPageCount() {
return this.pageCount;
}
public Long getPage() {
return this.page;
}
public Integer getLimit() {
return this.limit;
}
public Boolean getHasPageLinkPrev() {
return this.hasPageLinkPrev;
}
public Boolean getHasPageLinkNext() {
return hasPageLinkNext;
}
public Collection<Long> getPageLinks() {
return pageLinks;
}
public Collection<T> getObjects() {
return objects;
}
}
With that stuf it is not very hard to create the query and put the results in our page object. One possibility is to write a generic method in the base class of the repository classes:
protected <T> Page<T> getPage(JPQLQuery<T> query, PageRequest pageRequest) {
List<T> resultList = query
.offset(pageRequest.getOffset())
.limit(pageRequest.getLimit())
.fetch();
Long totalCount = query.fetchCount();
return new Page<T>(pageRequest, totalCount, resultList);
}
In your repository class you create your query for the particular use case. Then you can use the method getPage to get the results in a Page.
public Page<Person> searchPerson(String name,
String phone,
PageRequest request){
Page<Person> results=null;
JPQLQuery<Person> query = new JPAQuery<>(entityManager);
QPerson person = QPerson.person;
query = query.from(person)
.where(person.name.eq(name)
.and(person.phone.eq(phone)));
return getPage(query, request);
}

The solution for the above was using BooleanBuilder implemented on the method above and changed the method name to return Person Object.
Please check BooleanBuilder
QPerson person= QPerson.person;
BooleanBuilder builder = this.getBuilder(name, phone,page, pageSize, sortFlag, sortItem);
PageRequest pg = getPRequest(page, pageSize);
Page<Person> pages personRepo.findAll(builder,pg);
return pages;
and then Implemented getBuilder Method for it which is the below one
public BooleanBuilder getBuilder(String name, String phone, Integer page, Integer pageSize, String sortFlag, String sortItem) {
QPerson person = QPerson.person;
BooleanBuilder builder = new BooleanBuilder();
builder.and(person.name.startsWith(name));
return builder;
}
and finally implemented the getPRequest Method as the following
public PageRequest getPRequest(Integer page, Integer pageSize) {
return new PageRequest(page, pageSize);
}
Oooooh Happy Days!

Related

How to group map values after Collectors.groupingBy() without using forEach

I have a list of ProductDto objects and I want to group them the similar ones using java 8 streams Collectors.groupingBy(). After grouping the records I want to combine the similar records as single productDto. To achieve this I have used map.forEach and got the expected result, but I want to avoid the forEach loop and want to know any better solution in java 8.
Below is the my Main Class code snippet.
public class GroupTest {
public static void main(String[] args) {
GroupTest t=new GroupTest();
List<ProductDto> inputDtos=t.createInputData();
List<ProductDto> resultDtos=t.getGroupedResult(inputDtos);
//writing to Excel
}
private List<ProductDto> getGroupedResult(List<ProductDto> inputDtos) {
Map<Object, List<ProductDto>> groupedMap = inputDtos.stream()
.collect(Collectors.groupingBy(ProductDto::groupSimilarProductIdentifier));
List<ProductDto> resultProductDtos=new ArrayList<>();
groupedMap.forEach((key, dtos) -> {
if (dtos.size() > 1) {
ProductDto productDto = dtos.get(0);
dtos.forEach(dto -> {
if (dto.getLap1() != null) {
productDto.setLap1(dto.getLap1());
} else if (dto.getLap2() != null) {
productDto.setLap2(dto.getLap2());
} else if (dto.getLap3() != null) {
productDto.setLap3(dto.getLap3());
}
});
resultProductDtos.add(productDto);
} else {
resultProductDtos.addAll(dtos);
}
});
return resultProductDtos;
}
private List<ProductDto> createInputData(){
List<ProductDto> dtos=new ArrayList<>();
dtos.add(new ProductDto(1L,"DELL",8,"DELL_s001",null,null));
dtos.add(new ProductDto(1L,"DELL",8,null,"DELL_s002",null));
dtos.add(new ProductDto(1L,"DELL",8,null,null,"DELL_s003"));
dtos.add(new ProductDto(1L,"HP",8,"HP_s001",null,null));
dtos.add(new ProductDto(2L,"APPLE",16,"MAC_s001",null,null));
return dtos;
}
}
This is the ProductDto class code
public class ProductDto {
private Long userId;
private String manufacter;
private int ram;
private String lap1;
private String lap2;
private String lap3;
public ProductDto(Long userId, String manufacter, int ram, String lap1, String lap2, String lap3) {
super();
this.userId = userId;
this.manufacter = manufacter;
this.ram = ram;
this.lap1 = lap1;
this.lap2 = lap2;
this.lap3 = lap3;
}
//getters and Setters
public List<Object> groupSimilarProductIdentifier() {
return Arrays.asList(userId, manufacter, ram);
}
}
Below is the screenshot image shows the input and output records. Output records is the results exactly I want it. Any alternate or better solution in java 8 which is efficient is most welcome.
After Rono comment I found the answer so posting the answer what I did in getGroupedResult method and added a new function mergetwoProduct. So this may help somebody.
Below is the code for my getGroupedResult and mergetwoProduct methods after changes.
private List<ProductDto> getGroupedResult(List<ProductDto> inputDtos) {
List<ProductDto> productdtos= new ArrayList<>(inputDtos.stream().collect(
Collectors.toMap(ProductDto::groupSimilarProductIdentifier, e -> e, (a, b) -> mergetwoProduct(a, b)))
.values());
return productdtos;
}
private ProductDto mergetwoProduct(ProductDto p1,ProductDto p2) {
if (p2.getLap1() != null) {
p1.setLap1(p2.getLap1());
} else if (p2.getLap2() != null) {
p1.setLap2(p2.getLap2());
} else if (p2.getLap3() != null) {
p1.setLap3(p2.getLap3());
}
return p1;
}

Implementing Pagination using entity manager in spring

How can I implement pagination in Spring + hibernate project ?
Following is the code. I will get PageRequest object and I want to return Page of item
#Repository
public class ItemRepository {
#PersistenceContext
EntityManager entityManager;
public Page<Item> findItems(PageRequest pageRequest) {
// TODO: Implement me
return new Page<>(new ArrayList<>(), 0, 0);
}
}
public class PageRequest {
private final int count;
private final int pageNumber;
public PageRequest(int pageNumber, int count) {
assert pageNumber >= 0;
assert count > 0;
this.pageNumber = pageNumber;
this.count = count;
}
public int getCount() {
return count;
}
public int getPageNumber() {
return pageNumber;
}
}
I found the solution
public Page<Item> findItems(PageRequest pageRequest) {
Query query = entityManager.createQuery("From Item");
int pageNumber =pageRequest.getPageNumber();
int pageSize = pageRequest.getCount();
query.setFirstResult((pageNumber) * pageSize);
query.setMaxResults(pageSize);
List <Item> fooList = query.getResultList();
Query queryTotal = entityManager.createQuery
("Select count(f.id) From Item f");
long countResult = (long)queryTotal.getSingleResult();
int i=(int)countResult;
return new Page<>(fooList, pageRequest.getPageNumber(),i);
}
I had to query two times to DB once to get records and then to get all the count
One way to do it is to add logic in your PageRequest class to "slice" an incoming list depending on its Pageable method parameter and return it as org.springframework.data.domain.PageImpl.
Here is a static method that you can use in your PageRequest class:
public static <E> Page<E> returnPagedList(Pageable pageable, List<E> listOfEntities) {
List<E> listToReturn = listOfEntities;
if (pageable.isPaged()) {
int pageSize = pageable.getPageSize();
int currentPage = pageable.getPageNumber();
int startItem = currentPage * pageSize;
if (listOfEntities.size() < startItem) {
listToReturn = Collections.emptyList();
} else {
int toIndex = Math.min(startItem + pageSize, listOfEntities.size());
listToReturn = listOfEntities.subList(startItem, toIndex);
}
}
return new PageImpl<>(listToReturn, pageable, listOfEntities.size());
}
Then in your repository you can do this (notice you receive a Pageable object):
public Page<Item> findItems(Pageable pageable) {
EntityManager em = getEntityManager();
List<Item> list = ... // get list of all Items
return PageRequest.pagedList(pageable, list);
}
}

JUnit/Spring/MongoDB: Unit test fails due to null value

I am fairly new to unit test and started writing a simple test to make sure the returned query is not null using assertJ. I am using Fongo for my unit test and although I am having no error, the returned value is always null.
This is the class to be tested:
#Repository
public class DataVersionDaoMongo extends MongoBaseDao<DataVersion> implements DataVersionDao {
#Autowired
MongoOperations mongoOperations;
public DataVersionDaoMongo() {
initType();
}
#Override
public DataVersion findByDBAndCollection(String dbName, String collectionName) {
//return mongoOperations.findOne(Query.query(Criteria.where("dbName").is(dbName).and("collectionName").is(collectionName)), DataVersion.class);
Criteria criteria = Criteria.where("dbName").is(dbName).and("collectionName").is(collectionName);
Query query = Query.query(criteria);
return mongoOperations.findOne(query, DataVersion.class);
}
}
This is my unit test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("classpath:/testApplicationContext.xml")
public class DataVersionDaoMongoTest {
#Autowired
private DataVersionDaoMongo dataVersionDaoMongo;
//private MongoOperations mongoOperations;
private DataVersion dataVersion;
#Rule
public FongoRule fongoRule = new FongoRule();
#Test
public void findByDBAndCollection() {
String dbname = "mydb";
String collectionName = "mycollection";
DB db = fongoRule.getDB(dbname);
DBCollection collection = db.getCollection(collectionName);
Mongo mongo = fongoRule.getMongo();
collection.insert(new BasicDBObject("name", "randomName"));
assertThat(dataVersionDaoMongo.findByDBAndCollection(dbname, collectionName)).isNotNull();
}
}
I am sure that
dataVersionDaoMongo.findByDBAndCollection(dbname, collectionName) is returning null (It is returning DataVersion object which is null), so the test fails. How would I actually go about and make it return DataVersion that is not null?
Here is the DataVersion class:
#Document(collection = "DataVersion")
public class DataVersion {
#Id
private String id;
private String dbName;
private String collectionName;
private String version;
private boolean isCompleted;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getDbName() {
return dbName;
}
public void setDbName(String dbName) {
this.dbName = dbName;
}
public String getCollectionName() {
return collectionName;
}
public void setCollectionName(String collectionName) {
this.collectionName = collectionName;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public boolean isCompleted() {
return isCompleted;
}
public void setCompleted(boolean isCompleted) {
this.isCompleted = isCompleted;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((collectionName == null) ? 0 : collectionName.hashCode());
result = prime * result + ((dbName == null) ? 0 : dbName.hashCode());
result = prime * result + (isCompleted ? 1231 : 1237);
result = prime * result + ((version == null) ? 0 : version.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DataVersion other = (DataVersion) obj;
if (collectionName == null) {
if (other.collectionName != null)
return false;
} else if (!collectionName.equals(other.collectionName))
return false;
if (dbName == null) {
if (other.dbName != null)
return false;
} else if (!dbName.equals(other.dbName))
return false;
if (isCompleted != other.isCompleted)
return false;
if (version == null) {
if (other.version != null)
return false;
} else if (!version.equals(other.version))
return false;
return true;
}
}
Any help would be greatly appreciated!
P.S.
This is what I am adding in my unit test class:
#Autowired
private MongoOperations mongoOperations;
Then
DataVersion dataVersion = new DataVersion();
dataVersion.setDbName("DBDataVersion");
dataVersion.setVersion("version1");
dataVersion.setCollectionName("DataVersion");
mongoOperations.insert(dataVersion);
assertThat(dataVersionDaoMongo.findByDBAndCollection(dataVersionDaoMongo.getDbName(), dataVersion.getCollectionName())).isNotNull();
The unit test passes because it is no longer returning null, but then I am not making use of Fongo anymore. I am not sure if what I am doing is right or not.
You insert the document into mycollection collection in the test but the dao queries DataVersion collection.
Also you don't define dbName and collectionName in the stored object, hence, it won't be picked by a query which targets that two fields.

spring data jpa dynamic query which has IN clause

I want to create dynamic query in spring data jpa. Doing many search I can implement it, but I came across a problem when I add IN operator in where clause. I need to check id IN (longlist)
Here is my entity class
#Entity
#Table(name = "view_detail")
public class ViewDetailDom {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
private String name;
#Column(name = "user_id")
private Long userId;
private String description;
Here is specification builder class and specification class
public class ViewDetailSpecificationsBuilder {
private final List<SearchCriteria> params;
public ViewDetailSpecificationsBuilder() {
params = new ArrayList<SearchCriteria>();
}
public ViewDetailSpecificationsBuilder with(String key, Operation operation, Object value) {
params.add(new SearchCriteria(key, operation, value));
return this;
}
public Specification<ViewDetailDom> build() {
if (params.size() == 0) {
return null;
}
List<Specification<ViewDetailDom>> specs = new ArrayList<Specification<ViewDetailDom>>();
for (SearchCriteria param : params) {
specs.add(new ViewDetailSpecification(param));
}
Specification<ViewDetailDom> result = specs.get(0);
for (int i = 1; i < specs.size(); i++) {
result = Specifications.where(result).and(specs.get(i));
}
return result;
}
}
public class ViewDetailSpecification implements Specification<ViewDetailDom> {
private SearchCriteria criteria = new SearchCriteria();
public ViewDetailSpecification(SearchCriteria searchCriteria) {
this.criteria.setKey(searchCriteria.getKey());
this.criteria.setOperation(searchCriteria.getOperation());
this.criteria.setValue(searchCriteria.getValue());
}
#Override
public Predicate toPredicate(Root<ViewDetailDom> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
String value = criteria.getValue().toString().replaceAll(" ", "%");
if (criteria.getOperation() != null && criteria.getOperation() != Operation.DEFAULT) {
if (criteria.getOperation() == Operation.GREATHERTHANEQUALTO) {
return builder.greaterThanOrEqualTo(root.<String>get(criteria.getKey()), value);
} else if (criteria.getOperation() == Operation.LESSTHANEQUALTO) {
return builder.lessThanOrEqualTo(root.<String>get(criteria.getKey()), value);
} else if (criteria.getOperation() == Operation.EQUAL) {
return builder.equal(root.<String>get(criteria.getKey()), value);
} else if (criteria.getOperation() == Operation.IN) {
Path<Long> view = root.<Long>get(criteria.getKey());
return view.in(criteria.getValue());
}
} else {
if (root.get(criteria.getKey()).getJavaType() == String.class) {
return builder.like(builder.lower(root.<String>get(criteria.getKey())),
"%" + value.toLowerCase() + "%");
} else {
return builder.equal(root.get(criteria.getKey()), value);
}
}
return null;
}
}
This method creates specification builder:
public ViewDetailSpecificationsBuilder createSearchSpecifications(ViewSearch view) {
ViewDetailSpecificationsBuilder builder = new ViewDetailSpecificationsBuilder();
if (StringUtils.isNotBlank(view.getName())) {
builder.with("name", Operation.DEFAULT, view.getName());
}
if (StringUtils.isNotBlank(view.getDescription())) {
builder.with("description", Operation.DEFAULT, view.getDescription());
}
return builder;
}
And finally I do this:
ViewDetailSpecificationsBuilder builder = createSearchSpecifications(view);
builder.with("userId", Operation.DEFAULT, userSessionHelper.getUserId());
builder.with("id", Operation.IN, viewids);
Specification<ViewDetailDom> spec = builder.build();
viewDetailDao.findAll(spec);
But I am getting following error:
"Unaware how to convert value [[5, 7, 8] : java.util.ArrayList] to requested type [java.lang.Long]; nested exception is java.lang.IllegalArgumentException: Unaware how to convert value [[5, 7, 8] : java.util.ArrayList] to requested type [java.lang.Long]"
I have resolved this problem in this way:
ViewDetailSpecification class:
if (criteria.getOperation() == Operation.IN) {
final List<Predicate> orPredicates = new ArrayList<Predicate>();
List<Long> viewIds = (List<Long>) criteria.getValue();
for (Long viewid : viewIds) {
orPredicates.add(builder.or(builder.equal(root.<String>get(criteria.getKey()), viewid)));
}
return builder.or(orPredicates.toArray(new Predicate[orPredicates.size()]));
}
In kotlin I have the same error, I change the ArrayList to Array, with this code:
fun values(): Array<String> {
val elems = arrayListOf<String>()
return elems.toTypedArray()
}
Try you convert ArrayList to array, for java see: make arrayList.toArray() return more specific types

Spring - NoSuchMethodException when calling a RestService

I have this simple Mapping that should return me a List objects
#RestController
#RequestMapping(value="/api")
public class ServerRESTController {
#Autowired ServerService serverService;
#RequestMapping(value="/server/{idServer}", method = RequestMethod.GET)
public ResponseEntity<Server> getFloorLatUpdate(#PathVariable int idServer){
Server server = serverService.findById(idServer);
return new ResponseEntity<Server>(server, HttpStatus.OK);
}
#RequestMapping(value="/server/list", method = RequestMethod.GET)
public ResponseEntity<List<Server>> listAllServers(){
List<Server> servers = serverService.findAllServers(-1);
return new ResponseEntity<List<Server>>(servers, HttpStatus.OK);
}
}
Server.class is a model
#Entity
#Table(name = "server")
public class Server implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
private int serverId;
private Piano piano;
private String nomeServer;
private String serverIp;
private String descrizione;
private boolean online;
private Set<Interruttore> interruttori;
private String firmwareVersion;
public Server(){
}
public Server(int serverId, Piano piano, String nomeServer, String serverIp, String descrizione, boolean online,
Set<Interruttore> interruttori, String firmwareVersion){
this.serverId = serverId;
this.piano = piano;
this.nomeServer = nomeServer;
this.descrizione = descrizione;
this.serverIp = serverIp;
this.online = online;
this.interruttori = interruttori;
this.setFirmwareVersion(firmwareVersion);
}
#Id
#Column(name = "id_server", unique = true, nullable = false)
#GeneratedValue(strategy = IDENTITY)
public int getServerId() {
return serverId;
}
public void setServerId(int serverId) {
this.serverId = serverId;
}
#ManyToOne
#JoinColumn(name="id_piano")
public Piano getPiano() {
return piano;
}
public void setPiano(Piano piano) {
this.piano = piano;
}
#Column(name="nome_server")
public String getNomeServer() {
return nomeServer;
}
public void setNomeServer(String nomeServer) {
this.nomeServer = nomeServer;
}
#Column(name="server_ip")
public String getServerIp() {
return serverIp;
}
public void setServerIp(String serverIp) {
this.serverIp = serverIp;
}
#Column(name="descrizione")
public String getDescrizione() {
return descrizione;
}
public void setDescrizione(String descrizione) {
this.descrizione = descrizione;
}
#Column(name="online")
public boolean isOnline() {
return online;
}
public void setOnline(boolean online) {
this.online = online;
}
#JsonIgnore
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "server")
public Set<Interruttore> getInterruttori() {
return interruttori;
}
public void setInterruttori(Set<Interruttore> interruttori) {
this.interruttori = interruttori;
}
#Column(name = "firmware_version")
public String getFirmwareVersion() {
return firmwareVersion;
}
public void setFirmwareVersion(String firmwareVersion) {
this.firmwareVersion = firmwareVersion;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((descrizione == null) ? 0 : descrizione.hashCode());
result = prime * result + ((nomeServer == null) ? 0 : nomeServer.hashCode());
result = prime * result + (online ? 1231 : 1237);
result = prime * result + ((piano == null) ? 0 : piano.hashCode());
result = prime * result + serverId;
result = prime * result + ((serverIp == null) ? 0 : serverIp.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Server other = (Server) obj;
if (descrizione == null) {
if (other.descrizione != null)
return false;
} else if (!descrizione.equals(other.descrizione))
return false;
if (nomeServer == null) {
if (other.nomeServer != null)
return false;
} else if (!nomeServer.equals(other.nomeServer))
return false;
if (online != other.online)
return false;
if (piano == null) {
if (other.piano != null)
return false;
} else if (!piano.equals(other.piano))
return false;
if (serverId != other.serverId)
return false;
if (serverIp == null) {
if (other.serverIp != null)
return false;
} else if (!serverIp.equals(other.serverIp))
return false;
return true;
}
}
When trying to call for the service i'm getting:
HTTP Status 500 - Request processing failed; nested exception is java.lang.IllegalStateException: Method [listAllServers] was discovered in the .class file but cannot be resolved in the class object
cause by
java.lang.NoSuchMethodException: it.besmart.restcontroller.ServerRESTController.listAllServers()
I cannot understand why this happens, I have always used ResponseEntity in this way... maybe it's for the List?
Please post the whole code so we can find out.
This usually comes when the method is invoked at the wrong place or when there is a mismatch in build and runtime environments or when you miss arguments in a constructor and so on.
Also, you may want to check the files in the classpath. There may be a mismatch between compile time and actual runtime environments for some reason. For example, you say that you build it using command line. So, there may be some discrepancy there, no harm in checking.
Finally, you can check for spelling mistakes - I know that sounds strange, but case sensitivity is important.

Resources