Spring Boot JPA Like query - spring

I'm trying toimplement a search function for my table of users. I want to make a JpaRepository method that will give me a pageable list of my users that have their emails containing a given String, how can I do that?
This is whatIhave tried
The repository:
#Repository
public interface UserRepository extends JpaRepository<User, Long> {
Page<User> findByEmailContaining(String email, Pageable pageable);
}
The servive:
public Page<User> findPaginatedEmail(int pageNo, int pageSize, String sortField, String sortDirection, String text) {
Sort sort = sortDirection.equalsIgnoreCase(Sort.Direction.ASC.name()) ? Sort.by(sortField).ascending() :
Sort.by(sortField).descending();
Pageable pageable = PageRequest.of(pageNo - 1, pageSize, sort);
return this.userRepository.findByEmailContaining(text, pageable);
}
Sometimes it works and populates my table and sometimes it doesn't. What is the problem? How do I implement it?
It works fine but after a time it simply breaks I dont know why, then it wont find anything

There are many approaches, in case here it is been used the SimpleJpaRepository calling the findAll Method with parameters Specification and Pageable, returning the Page interface.
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.data.web.SortDefault;
#GetMapping(path = "/pageable")
public Page<?> getCountries(#RequestParam(name = "countryName", required = false) String countryName,
#PageableDefault(page = 0, size = 20) #SortDefault.SortDefaults({
#SortDefault(sort = "id", direction = Sort.Direction.ASC) }) Pageable pageable)
{
}
My Repository interface :
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface CountryRepository extends JpaRepository<Country, Long>, JpaSpecificationExecutor<Country> {
}
In case of having dynamic queries i use :
org.springframework.data.jpa.domain.Specification<T>
the spring data jpa :
org.springframework.data.jpa.repository.JpaSpecificationExecutor<T>
Page<T> findAll(#Nullable Specification<T> spec, Pageable pageable);
org.springframework.data.jpa.repository.support.SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID>
public Page<T> findAll(#Nullable Specification<T> spec, Pageable pageable) {
TypedQuery<T> query = getQuery(spec, pageable);
return isUnpaged(pageable) ? new PageImpl<T>(query.getResultList())
: readPage(query, getDomainClass(), pageable, spec);
}
the repository CountryRepository calling the method findAll(#Nullable
Specification spec, Pageable pageable) returns the Page

what you are looking for is:
public interface ProductRepository extends PagingAndSortingRepository<Product, Integer> {
List<Product> findAllByPrice(double price, Pageable pageable);
}
You just add one extra function parameter Pageable and also you need extend your Repository class with PagingAndSortingRepository instead of JpaRepository
Pagination example

Related

Make ElasticsearchRepository return specific fields in Page?

How to specify the fields to return in the book object?
interface BookRepository extends ElasticsearchRepository<Book, String> {
Page<Book> findByName(String name,Pageable pageable);
}

Spring Data - PagingAndSortingRepository with custom query (HQL)?

Trying to mix PagingAndSortingRepository with custom queries, no luck..
Custom repo:
public interface SiteRepositoryCustom
{
public List<SitesDbRecord> getActiveSites();
}
Impl repo:
#Repository
public class SiteRepositoryImpl implements SiteRepositoryCustom
{
private static final Logger logger = ...
#PersistenceContext
private EntityManager em;
#Override
public List<SitesDbRecord> getActiveSites()
{
logger.info( "getActiveSites start" );
try
{
String hql = "select s from SitesDbRecord s where s.isActive = true";
return em.createQuery( hql ).setMaxResults( Integer.MAX_VALUE ).getResultList();
}
catch ( Exception e )
{
logger.error( "getActiveSites failed.", e );
return null;
}
}
}
The repo injected to the service:
public interface SiteRepository extends PagingAndSortingRepository<SitesDbRecord, Integer>, SiteRepositoryCustom {
public List<SitesDbRecord> getActiveSites( Pageable pageable );
public List<SitesDbRecord> getActiveSites();
}
If I just extend CrudRepository (without the Pageable method) then all is OK. Trying to extend PagingAndSortingRepository (with or without the Pageable method) then Spring fails to boot with
PropertyReferenceException: No property getActiveSites found for type SitesDbRecord!
What is the correct way to use PagingAndSortingRepository with custom queries? Probably got it wrong, but I assumed it's Spring responsibility to provide the handling of paging/sorting.
If SitesDbRecord has boolean property named active it should be:
public interface SiteRepository extends PagingAndSortingRepository<SitesDbRecord, Integer> {
public List<SitesDbRecord> findByActiveIsTrue( Pageable pageable );
public List<SitesDbRecord> findByActiveIsTrue();
}
There is no need to extend your Custom repository, just implement PagingAndSortingRepository

Filtering with Spring Data Projection

I've created a class based Projection for my Spring Data Repository. That works great. Then I tried to annotate the constructor with #QueryProjection from QueryDSL, hoping to get a REST Endpoint with paging, sorting and filtering.
The code looks like this, but there are way more fields and details omitted for brevity:
Entity:
#Data
#Entity
public class Entity extends BaseEntity {
private String fieldA, fieldB;
private AnotherEntity ae;
}
DTO:
#Getter
#FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
public class EntityDto {
private final String fieldA;
private final String anotherEntityFieldC;
#QueryProjection
public EntityDto(final String fieldA, final String anotherEntityFieldC) {
this.fieldA = fieldA;
this.anotherEntityFieldC = anotherEntityFieldC;
}
}
Repository:
public EntityRepo extends JpaRepository<Entity, Long>, QuerydslBinderCustomizer<EntityPath<Entity>>, QuerydslPredicateExecutor<Entity> {
Page<EntityDto> findPageProjectedBy(Predicate predicate, Pageable pageable);
}
Endpoint:
#RestController
#RequestMapping(EntityEndpoint.ROOT)
#RequiredArgsConstructor(onConstructor = #__({#Autowired}))
public EntityEndpoint {
private final EntityRepo er;
#GetMapping
public ResponseEntity<PagedResources<Resource<EntityDto>>> getAllEntities(
Pageable pageable,
#QuerydslPredicate(root = EntityDto#.class) Predicate predicate,
PagedResourcesAssembler<EntityDto> assembler) {
Page<EntityDto> page = er.findPageProjectedBy(predicate, pageable);
return new ResponseEntity<>(assembler.toResource(page), HttpStatus.OK);
}
}
I get the exception:
java.lang.IllegalStateException: Did not find a static field of the same type in class at.dataphone.logis4.model.dto.QBuchungDto!
Stacktrace as gist
And that's the URL:
curl -X GET --header 'Accept: application/json' 'http://localhost:8080/Entity'
Well, it seems like you can only query with the actual Entity.
I think of it as the part in the 'WHERE'-clause, which can only have columns actually in the selected Entities, while the DTO projection happens in the 'SELECT'-clause.
The #QueryProjection-Annotation seems to serve only for the QueryDsl code generator to mark class which then can be used to create projections in a select-clause

Get all documents from an index using spring-data-elasticsearch

I am trying to connect to my external ElasticSearch server with Spring Boot.
If I do a curl from command line, I get expected results.
curl "http://ipAddr:9200/indexName/TYPE/_search?pretty=true"
But getting this error when I try to access it via Spring Boot.
<html><body><h1>Whitelabel Error Page</h1><p>This application has no explicit mapping for /error, so you are seeing this as a fallback.</p><div id='created'>Mon Sep 11 12:39:15 IST 2017</div><div>There was an unexpected error (type=Internal Server Error, status=500).</div><div>Could not write JSON: (was java.lang.NullPointerException); nested exception is com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: java.util.ArrayList[0]->org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl["facets"])</div></body></html>
Not sure why a NullPointerException and what is aggregartion.impl
Here is my Spring Application:
Controller:
#RestController
public class PojoController {
#Autowired
PojoService pojoService;
#RequestMapping(value = "/", method=RequestMethod.GET)
public #ResponseBody String index() {
return new String("Welcome:)");
}
#RequestMapping(value = "/all", method = RequestMethod.GET,
produces = { MediaType.APPLICATION_JSON_VALUE })
#ResponseBody List<POJO> findAll() {
try {
List<POJO> pojoObj = pojoService.findAll();
return pojoObj;
} catch (Exception exp) {
exp.printStackTrace();
return null;
}
}
}
Repository:
#Repository
public interface PojoRepository extends ElasticsearchRepository<POJO, Integer> {
List<POJO> findAll();
}
Service:
#Service
public class POJOServiceImpl implements POJOService{
private POJORepository pojoRepository;
private ElasticsearchTemplate elasticsearchTemplate;
#Autowired
public void setPojoRepository(PojoRepository pojoRepository) {
this.pojoRepository = pojoRepository;
}
public POJO findOne(String id) {
return pojoRepository.findOne(id);
}
public List<POJO> findAll() {
return (List<POJO>) pojoRepository.findAll();
}
}
POJO class:
#Document(indexName = "INDEX", type = "TYPE")
public class POJO {
#Id
private Integer id;
private String name;
public POJO(){
// empty
}
public POJO(Integerid, String name) {
super();
this.id = id;
this.name = name;
}
// getters and setters
}
I should be able to query all the documents in the index. Later on, I will try and use filters etc.
Any help is appreciated. Thanks :)
It looks like Jackson has a problem with handling your POJO (probably related to this issue: DATAES-274) - the problematic part is casting in repository from Iterable collection to List.
Update
In case of repositories, spring-data-elasticsearch behaves a bit different than you would expect. Taking your example:
#Repository
public interface PojoRepository extends ElasticsearchRepository<POJO, Integer> {
List<POJO> findAll();
}
and after calling in your rest controller:
List<POJO> pojoObj = pojoService.findAll();
in debugger you will see something like this:
You would expect that pojoObj list contains objects of POJO class.
And here comes the surprise - pojoObj ArrayList contains one object of AggregatedPageImpl type and its content field is the right list that contains your POJO objects.
This is the reason why you get:
Could not write JSON: ... java.util.ArrayList[0]->org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl[\"facets\"])
As I wrote before, Jackson cannot handle this while serializing POJO objects.
Solution 1
Let repositories return Iterable collection (by default).
#Repository
public interface PojoRepository extends ElasticsearchRepository<POJO, Integer> {
}
Move the conversion part to the service but use some utility method (here with Guava) in order to have it like this:
import com.google.common.collect.Lists;
public List<POJO> findAll() {
return Lists.newArrayList(pojoRepository.findAll());
}
Solution 2
Use Page in repository (here simplified version without parameters):
#Repository
public interface PojoRepository extends ElasticsearchRepository<POJO, Integer> {
Page<TestDto> findAll();
}
If you still want to operate on list - get content from page in service:
public List<POJO> findAll() {
return testDtoRepository.findAll().getContent();
}

Spring Data REST custom query integration

I want to create a REST link for an Employee entity that will basically be a findByAllFields query. Of course this should be combined with Page and Sort. In order to do that I have implemented the following code:
#Entity
public class Employee extends Persistable<Long> {
#Column
private String firstName;
#Column
private String lastName;
#Column
private String age;
#Column
#Temporal(TemporalType.TIMESTAMP)
private Date hiringDate;
}
So I would like to have lets say a query where I can do:
http://localhost:8080/myApp/employees/search/all?firstName=me&lastName=self&ageFrom=20&ageTo=30&hiringDateFrom=12234433235
So I have the following Repository
#RepositoryRestResource(collectionResourceRel="employees", path="employees")
public interface EmployeeRepository extends PagingAndSortingRepository<Employee, Long>,
JpaSpecificationExecutor<Employee> {
}
Ok so now I need a RestController
#RepositoryRestController
public class EmployeeSearchController {
#Autowired
private EmployeeRepository employeRepository;
#RequestMapping(value = "/employees/search/all/search/all", method = RequestMethod.GET)
public Page<Employee> getEmployees(EmployeeCriteria filterCriteria, Pageable pageable) {
//EmployeeSpecification uses CriteriaAPI to form dynamic query with the fields from filterCriteria
Specification<Employee> specification = new EmployeeSpecification(filterCriteria);
return employeeRepository.findAll(specification, pageable);
}
Ok, obviously this does its job but it is not integrated with HATEOAS.
I have attempted to assemble a resource changing the controller to this:
public PagedResources<Resource<Employee>> getEmployees(
PagedResourcesAssembler<Employee> assembler,
EmployeeCriteria filterCriteria, Pageable pageable) {
//EmployeeSpecification uses CriteriaAPI to form dynamic query with the fields from filterCriteria
Specification<Employee> specification = new EmployeeSpecification(filterCriteria);
Page<Employee> employees = employeeRepository.findAll(specification, pageable);
return assembler.toResource(employees);
}
Obviously I'm missing something from the above since it doesnt work and I'm getting the following Exception:
Could not instantiate bean class [org.springframework.data.web.PagedResourcesAssembler]: No default constructor found;
Ok so to make the question clear I am trying to integrate the above resource into the rest of the HATEOAS architecture. I'm not entirely sure if this is the correct approach so any other suggestions are welcome.
EDIT:
Here you can see a similar implementation. Please take a look at the configuration, you will see that all but one of the "Person" controllers are working.
https://github.com/cgeo7/spring-rest-example
Try autowring PagedResourcesAssembler as a class member and change method signature something like below
#RepositoryRestController
public class EmployeeSearchController {
#Autowired
private EmployeeRepository employeRepository;
#Autowired
private PagedResourcesAssembler<Employee> pagedAssembler;
#RequestMapping(value = "/employees/search/all/search/all", method = RequestMethod.GET)
public ResponseEntity<Resources<Resource<Employee>>> getEmployees(EmployeeCriteria filterCriteria, Pageable pageable) {
//EmployeeSpecification uses CriteriaAPI to form dynamic query with the fields from filterCriteria
Specification<Employee> specification = new EmployeeSpecification(filterCriteria);
Page<Employee> employees = employeeRepository.findAll(specification, pageable);
return assembler.toResource(employees);
}
}
This works perfectly with Spring Data Rest 2.1.4.RELEASE
The code by #Stackee007 works but the resource won't include self links. In order to do that, a little more is required.
#Autowired
PagedResourcesAssembler<Appointment> pagedResourcesAssembler;
#RequestMapping(value = "/findTodaysSchedule")
public HttpEntity<PagedResources<Resource<Appointment>>> getTodaysSchedule(
PersistentEntityResourceAssembler entityAssembler, Pageable pageable) {
Page<Appointment> todaysSchedule = apptRepo.findByStartTimeBetween(beginningOfDay, endOfDay, pageable);
#SuppressWarnings({ "unchecked", "rawtypes" })
PagedResources<Resource<Appointment>> resource = pagedResourcesAssembler.toResource(todaysSchedule,
(ResourceAssembler) entityAssembler);
return new ResponseEntity<>(resource, HttpStatus.OK);
}
Spring HATEOAS has changed the name of Resource, PagedResources and some other classes. See here. Below is a working version in 2020.
#RepositoryRestController
public class EmployeeSearchController {
#Autowired
private EmployeeRepository employeRepository;
#Autowired
private PagedResourcesAssembler<Employee> pagedAssembler;
#RequestMapping(value = "/employees/search/all", method = RequestMethod.GET)
public ResponseEntity<PagedModel<EntityModel<Employee>>> getEmployees(PersistentEntityResourceAssembler entityAssembler,,
EmployeeCriteria filterCriteria,
Pageable pageable) {
Specification<Employee> specification = new EmployeeSpecification(filterCriteria);
Page<Employee> employees = employeeRepository.findAll(specification, pageable);
return ResponseEntity.ok(pagedAssembler.toModel(plants, (RepresentationModelAssembler) entityAssembler));
}
}

Resources