I'm trying to get data from a dynamodb table using Spring Data, filtering by two fields using In keyword, but it always return an empty result.
I have an entity like:
#DynamoDBTable(tableName = "my-table")
public class MyEntity {
#Id
private MyTableKey id;
private String saleDate;
private Long sellerId;
// Some other properties
#DynamoDBHashKey
public String getSaleDate() {
return this.saleDate;
}
#DynamoDBRangeKey
public Long getSellerId() {
return this.sellerId;
}
}
and my interface for the repository is
#EnableScan
#EnableScanCount
public interface MySalesRepository
extends PagingAndSortingRepository<MyEntity, MyTableKey> {
Page<MyEntity> findById (MyTableKey id, Pageable pageable);
Page<MyEntity> findAllBySaleDateAndSellerIdIn (List<String> saleDate,
List<Long> sellerId, Pageable pageable);
}
I want to get items that have both saleDate in the saleDate list and sellerId in the sellerId list, but I get 0 results.
How can I get the values I want using And and In keywords?
Try changing this:
Page<MyEntity> findAllBySaleDateAndSellerIdIn (List<String> saleDate,
List<Long> sellerId, Pageable pageable);
to this:
Page<MyEntity> findBySaleDateInAndSellerIdIn (List<String> saleDate,
List<Long> sellerId, Pageable pageable);
Related
I have an API controller which allows users return a list of user profiles they have saved to their account. This data is stored in MongoDB and in POJO terms the data I want to paginate on is a LinkedHashSet of strings.
Now, in some other parts of my code I have simple List<String> which I'm able to "paginate" via the code below:
public static <T> List<T> getPage(List<T> sourceList, int page, int pageSize) {
if (pageSize <= 0 || page < 0) {
throw new IllegalArgumentException("invalid page size: " + pageSize);
}
if (sourceList == null || sourceList.size() < (page * pageSize)) {
return Collections.emptyList();
}
return sourceList.subList(page, Math.min(page + pageSize, sourceList.size()));
}
Can anyone help me do apply this same logic to a LinkedHashset? I'm struggling since the collection does not have a sublist method.
Below is my controller:
#GetMapping("/users/savedlist")
public Iterable<User> getUsersListOfSavedProfiles(Authentication authentication,
#PageableDefault(page = 0, size = 10) Pageable pageable) {
return savedProfilesService.getUsersSavedProfiles(authentication.getName(), pageable.getPageNumber(), pageable.getPageSize());
And the Pojo:
#Document(collection = "UsersSavedProfiles")
public class SavedProfiles {
#Id
#JsonIgnore
private String _id;
private String userId;
private LinkedHashSet<String> savedProfileIds;
Access to the DB via DALImpl:
#Override
public SavedProfiles findByUserId(String userId) {
Query query = new Query();
query.addCriteria(Criteria.where("userId").is(userId));
return mongoTemplate.findOne(query, SavedProfiles.class);
}
**** Edit for Clarity ****
I know I can paginate a Mongo query for User Profile documents BUT what I need to do is paginate the ArrayList savedProfiles inside one of the documents.
Thanks again to the people who looked at this
I searched everywhere for an example of a Spring piece of code using simultaneously these 3 JPA concepts, very important when querying :
filtering - using Example, ExampleMatcher
paging - using Pageable (or similar)
sorting - using Sort
So far I only saw examples using only 2 of them at the same time but I need to use all of them at once. Can you show me such an example?
Thank you.
PS: This has examples for Paging and Sorting but not filtering.
Here is an example, searching for news on title attribute, with pagination and sorting :
Entity :
#Getter
#Setter
#Entity
public class News {
#Id
private Long id;
#Column
private String title;
#Column
private String content;
}
Repository :
public interface NewsRepository extends JpaRepository<News, Long> {
}
Service
#Service
public class NewsService {
#Autowired
private NewsRepository newsRepository;
public Iterable<News> getNewsFilteredPaginated(String text, int pageNumber, int pageSize, String sortBy, String sortDirection) {
final News news = new News();
news.setTitle(text);
final ExampleMatcher matcher = ExampleMatcher.matching()
.withIgnoreCase()
.withIgnorePaths("content")
.withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING);
return newsRepository.findAll(Example.of(news, matcher), PageRequest.of(pageNumber, pageSize, sortDirection.equalsIgnoreCase("asc") ? Sort.by(sortBy).ascending() : Sort.by(sortBy).descending()));
}
}
Call example :
for (News news : newsService.getNewsFilteredPaginated("hello", 0, 10, "title", "asc")) {
log.info(news.getTitle());
}
Found the answer in the end after more research:
public Page<MyEntity> findAll(MyEntity entityFilter, int pageSize, int currentPage){
ExampleMatcher matcher = ExampleMatcher.matchingAll()
.withMatcher("name", exact()); //add filters for other columns here
Example<MyEntity> filter = Example.of(entityFilter, matcher);
Sort sort = Sort.by(Sort.Direction.ASC, "id"); //add other sort columns here
Pageable pageable = PageRequest.of(currentPage, pageSize, sort);
return repository.findAll(filter, pageable);
}
I am new to Spring boot and hibernate. Here I am trying run a search based optional parameter query Where i can search by name, country etc. If I kept this field null then query should all list. But the problem is my method is returning all data ignoring my search parameter. my model class look like
#Entity(name="MLFM_ORDER_OWNER")
public class ModelOrderOwner {
#Id #GenericGenerator(name = "custom_sequence", strategy =
"com.biziitech.mlfm.IdGenerator")
#GeneratedValue(generator = "custom_sequence")
#Column(name="ORDER_OWNER_ID")
private Long orderOwnerId;
#Column(name="OWNER_NAME")
private String ownerName;
#OneToOne
#JoinColumn(name="BUSINESS_TYPE_ID")
private ModelBusinessType businessTypeId;
#Column(name="SHORT_CODE")
private String shortCode;
#ManyToOne
#JoinColumn(name="OWNER_COUNTRY")
private ModelCountry ownerCountry;
// getter setter..
My Repository interface looks like
public interface OrderOwnerRepository extends
JpaRepository<ModelOrderOwner,Long>{
#Query("select a from MLFM_ORDER_OWNER a where a.businessTypeId.typeId=coalsec(:typeId,a.businessTypeId.typeId) and a.ownerCountry.countryId=coalsec(:countryId,a.ownerCountry.countryId) and a.ownerName LIKE %:name and a.shortCode LIKE %:code")
public List <ModelOrderOwner> findOwnerDetails(#Param("typeId")Long typeId,#Param("countryId")Long countryId,#Param("name")String name,#Param("code")String code);
}
And here is my method in controller
#RequestMapping(path="/owners/search")
public String getAllOwner(Model model,#RequestParam("owner_name") String name,#RequestParam("shortCode") String code,
#RequestParam("phoneNumber") String phoneNumber,#RequestParam("countryName") Long countryId,
#RequestParam("businessType") Long typeId
) {
model.addAttribute("ownerList",ownerRepository.findOwnerDetails(typeId, countryId, name, code));
return "data_list";
}
Can Any one help me in this regard? please?
It is too late too answer, but for anyone who looks for a solution yet there is a more simple way as below:
In my case my controller was like:
#RestController
#RequestMapping("/order")
public class OrderController {
private final IOrderService service;
public OrderController(IOrderService service) {
this.service = service;
}
#RequestMapping(value = "/{username}/", method = RequestMethod.GET)
public ResponseEntity<ListResponse<UserOrdersResponse>> getUserOrders(
#RequestHeader Map<String, String> requestHeaders,
#RequestParam(required=false) Long id,
#RequestParam(required=false) Long flags,
#RequestParam(required=true) Long offset,
#RequestParam(required=true) Long length) {
// Return successful response
return new ResponseEntity<>(service.getUserOrders(requestDTO), HttpStatus.OK);
}
}
As you can see, I have Username as #PathVariable and length and offset which are my required parameters, but I accept id and flags for filtering search result, so they are my optional parameters and are not necessary for calling the REST service.
Now in my repository layer I have just created my #Query as below:
#Query("select new com.ada.bourse.wealth.services.models.response.UserOrdersResponse(FIELDS ARE DELETED TO BECOME MORE READABLE)" +
" from User u join Orders o on u.id = o.user.id where u.userName = :username" +
" and (:orderId is null or o.id = :orderId) and (:flag is null or o.flags = :flag)")
Page<UserOrdersResponse> findUsersOrders(String username, Long orderId, Long flag, Pageable page);
And that's it, you can see that I checked my optional arguments with (:orderId is null or o.id = :orderId) and (:flag is null or o.flags = :flag) and I think it needs to be emphasized that I checked my argument with is null condition not my columns data, so if client send Id and flags parameters for me I will filter the Result with them otherwise I just query with username which was my #PathVariable.
Don't know how but below code is working for me:
#Query("select a from MLFM_ORDER_OWNER a
where a.businessTypeId.typeId=COALESCE(:typeId,a.businessTypeId.typeId)
and a.ownerCountry.countryId=COALESCE(:countryId,a.ownerCountry.countryId)
and a.ownerName LIKE %:name and a.shortCode LIKE %:code")
public List <ModelOrderOwner> findOwnerDetails(
#Param("typeId")Long typeId,
#Param("countryId")Long countryId,
#Param("name")String name,
#Param("code")String code);
and in my controller class:
#RequestMapping(path="/owners/search")
public String getAllOwner(Model model,
#RequestParam("owner_name") String name,
#RequestParam("shortCode") String code,
#RequestParam("phoneNumber") String phoneNumber,
#RequestParam("countryName") Long countryId,
#RequestParam(value = "active", required = false) String active, #RequestParam("businessType") Long typeId) {
if(typeId==0)
typeId=null;
if(countryId==0)
countryId=null; model.addAttribute("ownerList",ownerRepository.findOwnerDetails(typeId, countryId, name, code, status));
return "data_list";
}
JPQL doesn't support optional parameters.
There is no easy way of doing this in JPQL. You will have to write multiple WHERE clauses with OR operator.
Refer these answers to similar questions: Answer 1 & Answer 2
PS: You might want to look into Query by Example for your use case. It supports handling of null parameters.
Use JpaSpecificationExecutor //import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
Step 1: Implement JpaSpecificationExecutor in your JPA Repository
Ex:
public interface TicketRepo extends JpaRepository<Ticket, Long>, JpaSpecificationExecutor<Ticket> {
Step 2 Now to fetch tickets based on optional parameters you can build Specification query using CriteriaBuilder
Ex:
public Specification<Ticket> getTicketQuery(Integer domainId, Calendar startDate, Calendar endDate, Integer gameId, Integer drawId) {
return (root, query, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
predicates.add(criteriaBuilder.equal(root.get("domainId"), domainId));
predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("createdAt"), startDate));
predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get("createdAt"), endDate));
if (gameId != null) {
predicates.add(criteriaBuilder.equal(root.get("gameId"), gameId));
}
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
};
}
Step 3: Pass the Specification instance to jpaRepo.findAll(specification), it will return you the list of your entity object (Tickets here in the running example)
ticketRepo.findAll(specification); // Pass output of function in step 2 to findAll
This is how my entity looks like:
#Entity
public class Registration {
#Id
#GeneratedValue
private Integer id;
#org.springframework.format.annotation.DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate date;
}
This is how my repo could look like:
#Query(value = "SELECT * FROM registration WHERE MONTH(date) = ?1 AND YEAR(date) = ?2")
List<Registration> findAll(Integer month, Integer year);
And this will be service:
public List<Registration> getCurrentRegistration() {
LocalDate today = LocalDate.now();
return registrationRepository.findAll(today.getMonth().getValue(), today.getYear());
}
public List<Registration> getRegistrations(Integer month, Integer year) {
return registrationRepository.findAll(month, year);
}
How can I change my native query to be JPA query?
Will the JPA query able to work on postgresql and hsqldb?
And why JPA queries are the best for spring apps? (or why they are not)
Make a specification class and write the below specification method in it.
import javax.persistence.criteria.Predicate;
import org.springframework.data.jpa.domain.Specification;
public class RegistrationSpecification {
public static Specification<Registration > registrationSpecForDate(
LocalDate invoiceDate ) {
return (root, cq, cb) -> {
List<Predicate> predicates = new ArrayList<Predicate>();
if (invoiceDate!=(null)) {
predicates.add(cb.greaterThanOrEqualTo(root.get("date"),
invoiceDate));
}
return cb.and(predicates.toArray(new Predicate[0]));
};
}
Then in your repository inject this specification in JPA's findAll() method.
`public List<Registration> getRegistrations(LocalDate date) {
return
registrationRepository.findAll
(RegistrationSpecification.registrationSpecForDate(date));
`
You could do this using QueryDSL JPA (https://github.com/querydsl/querydsl/tree/master/querydsl-jpa) to define a predicate:
Predicate createPredicate(Integer month, Integer year) {
return QRegistration.date.year().eq(year).and(QRegistration.date.month().eq(month));
}
Then make your repo extend QueryDslPredicateExecutor:
public interface RegistrationRepository extends JpaRepository<Registration>, QueryDslPredicateExecutor {
// Your query methods here
}
This contains a List<T> findAll(Predicate predicate) method which you can pass your predicate in to obtain the items you were after, e.g.:
registrationRepository.findAll(createPredicate(1, 1970));
See here for more info about using QueryDSL with Spring: https://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/
I have the following data model, and I want to get a specific object in the sub list objects, I know it's possible to get the entire list and go through each object and compare with what the search id, but I wonder if it is possible use MongoRepository to do this.
#Document
public class Host {
#Id
private String id;
#NotNull
private String name;
#DBRef
private List<Vouchers> listVoucher;
public Host() {
}
//Getters and Setters
}
And..
#Document
public class Vouchers {
#Id
private String id;
#NotNull
private int codeId;
public Vouchers() {
}
//Getters and Setters
}
The Repository Class:
public interface HostRepository extends MongoRepository<Host, String> {
List<Host> findAll();
Host findById(String id);
Host findByName(String name);
//How to build the correct query ??????????
List<Vouchers> findVouchersAll();
Vouchers findByVouchersById(String hostId, String voucherId);
}
The Controller Class:
#RestController
#RequestMapping(value = "api/v1/host")
public class VoucherController {
#Inject
HostRepository hostRepository;
#RequestMapping(value = "/{hostId}/voucher",method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public List<Vouchers> list() {
return hostRepository.findVouchersAll();
}
#RequestMapping(value = "/{hostId}/voucher/{voucherId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public Vouchers getOneVoucher(#PathVariable String hostId, #PathVariable String voucherId) {
Vouchers voucher = hostRepository.findByVouchersById(hostId, voucherId);
if (voucher != null) {
return voucher;
} else {
throw new VoucherNotFoundException(String.format("There is no voucher with id=%s", voucherId));
}
}
}
Thanks in Advance!
I think there is a way to do this although I have not tried this myself but maybe I can shed some light in how I would do it.
Firstly, I would rather use the more flexible way of querying mongodb by using MongoTemplate. MongoTemplate is already included in the Spring Boot Mongodb data library and it looks like you are already using the library so it is not an additional library that you will have to use. In Spring there is a way to #Autowired your MongoTemplate up so it is quick and easy to get the object for completing this task.
With mongoTemplate, you would do something like this:
Query query = new Query();
query.addCriteria(Criteria.where("listVouchers.id").is("1234"));
List<Host> host = mongoTemplate.find(query, Host.class);
Please see docs here: https://docs.mongodb.org/manual/tutorial/query-documents/