Cannot use MatchMode with variable of type double - spring

I am implementing server-side filtering of dataTable. In the service which implements the filtering I want to test if the search criteria is a number ; if so then I implement the filtering based on the bean attribute of type double ( salary ) , otherwise I make the filtering based on the bean attribute of type String ( username ) :
#Override
#Transactional
public List<User> list(int start, int length, String search) {
Criteria criteres = sessionFactory.getCurrentSession().createCriteria(User.class);
if (!search.equals("")) {
if (NumberUtils.isNumber(search))
criteres.add(Restrictions.like("salary", Double.parseDouble(search)));
else
criteres.add(Restrictions.like("username", search, MatchMode.ANYWHERE));
}
criteres.setFirstResult(start);
criteres.setMaxResults(length);
criteres.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
#SuppressWarnings("unchecked")
List<User> listUser = (List<User>) criteres.list();
return listUser;
}
The problem is that if I write :
if (!search.equals("")) {
if (NumberUtils.isNumber(search))
criteres.add(Restrictions.like("salary", String.valueOf(Double.parseDouble(search)), MatchMode.ANYWHERE));
else
criteres.add(Restrictions.like("username", search, MatchMode.ANYWHERE));
}
then I got alert error : DataTables warning: table id=t_list - Ajax error. For more information about this error, please see http://datatables.net/tn/7
So how to make possible to make a "like" condition on the double variable ?

Related

InvalidPathException while sorting with org.springframework.data.domain.Pageable

I am trying to sort my table's content on the backend side, so I am sending org.springframework.data.domain.Pageable object to controller. It arrives correctly, but at the repository I am getting org.hibernate.hql.internal.ast.InvalidPathException. Somehow the field name I would use for sorting gets an org. package name infront of the filed name.
The Pageable object logged in the controller:
Page request [number: 0, size 10, sort: referenzNumber: DESC]
Exception in repository:
Invalid path: 'org.referenzNumber'","logger_name":"org.hibernate.hql.internal.ast.ErrorTracker","thread_name":"http-nio-8080-exec-2","level":"ERROR","level_value":40000,"stack_trace":"org.hibernate.hql.internal.ast.InvalidPathException: Invalid path: 'org.referenzNumber'\n\tat org.hibernate.hql.internal.ast.util.LiteralProcessor.lookupConstant(LiteralProcessor.java:111)
My controller endpoint:
#GetMapping(value = "/get-orders", params = { "page", "size" }, produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<PagedModel<KryptoOrder>> getOrders(
#ApiParam(name = "searchrequest", required = true) #Validated final OrderSearchRequest orderSearchRequest,
#PageableDefault(size = 500) final Pageable pageable, final BindingResult bindingResult,
final PagedResourcesAssembler<OrderVo> pagedResourcesAssembler) {
if (bindingResult.hasErrors()) {
return ResponseEntity.badRequest().build();
}
PagedModel<Order> orderPage = PagedModel.empty();
try {
var orderVoPage = orderPort.processOrderSearch(resourceMapper.toOrderSearchRequestVo(orderSearchRequest), pageable);
orderPage = pagedResourcesAssembler.toModel(orderVoPage, orderAssembler);
} catch (MissingRequiredField m) {
log.warn(RESPONSE_MISSING_REQUIRED_FIELD, m);
return ResponseEntity.badRequest().build();
}
return ResponseEntity.ok(orderPage);
}
the repository:
#Repository
public interface OrderRepository extends JpaRepository<Order, UUID> {
static final String SEARCH_ORDER = "SELECT o" //
+ " FROM Order o " //
+ " WHERE (cast(:partnerernumber as org.hibernate.type.IntegerType) is null or o.tradeBasis.account.retailpartner.partnerbank.partnerernumber = :partnerernumber)"
+ " and (cast(:accountnumber as org.hibernate.type.BigDecimalType) is null or o.tradeBasis.account.accountnumber = :accountnumber)"
+ " and (cast(:orderReference as org.hibernate.type.LongType) is null or o.tradeBasis.referenceNumber = :orderReference)"
+ " and (cast(:orderReferenceExtern as org.hibernate.type.StringType) is null or o.tradeBasis.kundenreferenceExternesFrontend = :orderReferenceExtern)"
+ " and (cast(:dateFrom as org.hibernate.type.DateType) is null or o.tradeBasis.timestamp > :dateFrom) "
+ " and (cast(:dateTo as org.hibernate.type.DateType) is null or o.tradeBasis.timestamp < :dateTo) ";
#Query(SEARCH_ORDER)
Page<Order> searchOrder(#Param("partnerernumber") Integer partnerernumber,
#Param("accountnumber") BigDecimal accountnumber, #Param("orderReference") Long orderReference,
#Param("orderReferenceExtern") String orderReferenceExtern, #Param("dateFrom") LocalDateTime dateFrom,
#Param("dateTo") LocalDateTime dateTo, Pageable pageable);
}
Update:
I removed the parameters from the sql query, and put them back one by one to see where it goes sideways. It seems as soon as the dates are involved the wierd "org." appears too.
Update 2:
If I change cast(:dateTo as org.hibernate.type.DateType) to cast(:dateFrom as date) then it appends the filed name with date. instead of org..
Thanks in advance for the help
My guess is, Spring Data is confused by the query you are using and can't properly append the order by clause to it. I would recommend you to use a Specification instead for your various filters. That will not only improve the performance of your queries because the database can better optimize queries, but will also make use of the JPA Criteria API behind the scenes, which requires no work from Spring Data to apply an order by specification.
Since your entity Order is named as the order by clause of HQL/SQL, my guess is that Spring Data tries to do something stupid with the string to determine the alias of the root entity.

Spring Data JPA - #Query with null values based on user input

The user has a form:
checkbox with fields status and priority
submit btn
Objective: Query DB based on these values.
If one of them is null or false, the criteria should be ignored.
Ex: status is checked and priority is not, I want to query only based on status.
The code below will never execute with one of them being false. I also read about Query by Example but could not find a solution.
Null values can indeed be ignored, but need to be previously defined.
I thought about replacing null with something similar to *.
Ex: WHERE c.status = *. It didn't work.
#Query(value = "SELECT * FROM tickets c WHERE c.status = :status AND c.priority= :priority",
nativeQuery = true)
List<Ticket> findByFilter(#Param("status") String status,
#Param("priority") String priority);
Do you have any idea how can I do this?
Thank you
Thank you Simon for pointing me in the right direction.
This was the web page that helped me:
https://dimitr.im/writing-dynamic-queries-with-spring-data-jpa
My situation:
Created a specification class:
public final class DbFilterSpecification {
public static Specification<Ticket> statusContains(String expression) {
return (root, query, builder) -> builder.like(root.get("status"), contains(expression));
}
public static Specification<Ticket> priorityContains(String expression) {
return (root, query, builder) -> builder.like(root.get("priority"), contains(expression));
}
private static String contains(String expression) {
return MessageFormat.format("%{0}%", expression);
}
}
Created a method inside the service layer:
public List<Ticket> findAllWithSpecification(String status, String priority) {
Specification<Ticket> specification = Specification
.where(status == null ? null : DbFilterSpecification.statusContains(status))
.and(priority == null ? null : DbFilterSpecification.priorityContains(priority));
return ticketRepository.findAll(specification);
}

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

Spring JPA repoistory findBy IN List - allow null

Short Description
How do I make findBy<Field>In work with IN when the array list input is null. e.g. ignore it. What would your DAO for this look like?
Longer description.
Imagine you have creating a search for users page.
in the application. You have various options to filter on.
created (date range always given)
Country (when null ignore and search all countries)
AgeRange
Job Title
etc...
Now say you want to search for all users in a given date range in a list of countries.
When searching for users I will always search for a date joined however if I have not selected a country I want it to search for all countries.
I am planning on adding several more filter options other than country. So I don't really want to create lots of findBy methods for each possible field combination.
DAO
#Repository
public interface UserDao extends JpaRepository<User, Long> {
public List<BeatRate> findByCreatedBetweenAndCountryIn(Date from, Date to, ArrayList<String> countryList );
}
Test
#Test
public void test() throws ParseException {
Date from = new SimpleDateFormat( "yyyy-MM-dd" ).parse( "2015-01-01" );
Date to = new SimpleDateFormat("yyyy-MM-dd").parse("2015-05-15");
//ArrayList<String> countryList = new ArrayList<String>();
//countryList.add("UK");
//countryList.add("Australia");
//countryList.add("Japan"); // works ok when I have a list
countryList = null; // I want it to search for all countries when this is null -- this errors and doesnt work..
List<BeatRate> beatRates = beatRateDao.findByCreatedBetweenAndRentalCountryIn(from, to, countryList);
Assert.assertTrue(beatRates.size()>0);
}
You can have two methods:
beatRateDao.findByCreatedBetweenAndRentalCountryIn(from, to, countryList);
and
beatRateDao.findByCreatedBetweenAndRental(from, to);
Then simply pick one based on countryList:
List<BeatRate> beatRates = (countryList != null && !countryList.isEmpty())
? beatRateDao.findByCreatedBetweenAndRentalCountryIn(from, to, countryList)
: beatRateDao.findByCreatedBetweenAndRental(from, to);
The IN clause requires a non-nullable and non empty argument list as otherwise the query will fail.
On PostgreSQL, if you try to run a query like this:
select *
from product
where quantity in ( )
you get the following error:
ERROR: syntax error at or near ")"
LINE 3: where quantity in ( )
^
********** Error **********
ERROR: syntax error at or near ")"
SQL state: 42601
Character: 45

CriteriaBuilder - Sum using SelectCase

I am trying to perform a summation SQL query like the following:
select group_ID, sum(case when user_type = 'Exec' then 1000
when user_type = 'Office' then 10 else 0 end)
from subscription
group by group_ID;
using the following snippet from a hiberate CriteriaBuilder query:
criteriaBuilder.sum(
criteriaBuilder.selectCase()
.when(criteriaBuilder.equal(subscriptionJoin.get(Subscription_.userType), "Exec"),1000)
.when(criteriaBuilder.equal(subscriptionJoin.get(Subscription_.userType), "Office"),1)
.otherwise(101))
However the following compile error appears:
Inferred type 'java.lang.object' for type parameter 'N' is not within its bound; should extend 'java.lang.number'
Any idea how to support performing a summation using the selectCase?
Sum is defined as follows:
<N extends Number> Expression<N> sum(Expression<N> x);
So reason to the compilation error is that sum method expect such arguments which is Expression with type that extends Number. It determines type from the selectCase and ends up with java.lang.Object, which is not acceptable.
Problem can be solved by giving type parameter (<Number>):
criteriaBuilder.sum(
criteriaBuilder.<Number>selectCase()
We are using Spring Data JPA in our project and i have the same case where i need to do sum. Instead of criteria query i'm just following the "named parameters" approach because this approach seems easy.
My method which gives me sum is as follows.
public interface ITransactionEntryRepo extends PagingAndSortingRepository<TransactionEntryEntity, String> {
#Query("select SUM(CASE WHEN te.debit = 'Y' THEN (te.amount * - 1) WHEN te.debit = 'N' THEN te.amount ELSE 0 END) AS availablebalance FROM TransactionEntity t, TransactionEntryEntity te WHERE t.id = te.transactionEntity.id and te.accountEntity.id = :id and te.valid = 'T' and t.retcode = 'XX' GROUP BY te.accountEntity.id")
public double findAvailableBalance(#Param("id") String id);
}
And I call this method in the class where i need
double balance = iTransactionEntryRepo.findAvailableBalance(accountEntity.getId());
and pass it(balance) wherever I need to. Hope this helps someone.
For aggregate operation you should pass the CriteriaQuery with numeric type to be proper expression for criteria builder, however this may not affect your criteria base restriction of you entity type. Finally you can append the desired predicates to your criteria query for having criteria base aggregation.
public class Aggregate<T, S extends Number> {
public Aggregate(Class<T> tableType, Class<S> type) {
this.criteriaBuilder = entityManager.getCriteriaBuilder();
this.criteria = criteriaBuilder.createQuery(type);
this.root = criteria.from(tableType);
}
public Aggregate<T, S> aggregate(String field) {
criteria.select(criteriaBuilder.sum(root.get(field)));
return this;
}
public <I> Aggregate<T, S> restrict(String field, I i) {
criteria.where(criteriaBuilder.equal(root.get(field), i));
return this;
}
public S perform() {
return entityManager.createQuery(criteria).getSingleResult();
}
private Root<T> root;
private final CriteriaQuery<S> criteria;
private final CriteriaBuilder criteriaBuilder;
}

Resources