Create a query string with or condition - java

I create a jpql query with a string
StringBuilder stringQuery = new StringBuilder();
stringQuery.append("select school from School school where ");
if(schooSearch.name()!=null && !schooSearch.name()){
stringQuery.append(" contains( ");
stringQuery.append("school.name, ");
stringQuery.append(":name");
stringQuery.append(" ) > 0 ");
}
if(schooSearch.adr()!=null && !schooSearch.adr().isEmpty()){
stringQuery.append(" or ");
stringQuery.append(" school.adr like ");
stringQuery.append(":adr");
}
All conditions use or, i have many other fields.
What is the way to avoid to check for every condition if it's the first field and we need a where

I would suggest using a flexible prepared statement:
select school
from School school
where (school.name LIKE :name OR :name IS NULL) AND
(school.adr LIKE :adr OR :adr IS NULL)
Each condition in the WHERE clause will evaluate to true and no-op should the bound parameter be null. Note that JPA does not have any contains() function, so instead you should bind %some name% to the :name parameter.

Related

JPA Criteria Query API and order by null last

My problem is null values must be last order by statement. My code snipshot below. I use javax persistance criteria builder. My query complicated.
import javax.persistence.criteria.CriteriaBuilder;
public Predicate getSomePredicate() {
Predicate predicate = cb.conjunction();....
...predicate.getExpressions().add(cb.and(cb.or(cb.and(v1, v2), cb.and(s1, s2))));
EOrderByType orderType = EOrderByType.values()[orderBy]
;
switch (orderType) {
case PRICE: cq.where(predicate).orderBy(cb.asc(root.get("price")));
break;
case PRICE_HIGH_TO_LOW: cq.where(predicate).orderBy(cb.desc(root.get("price")));
break;
case CONSUPTION: cq.where(predicate).orderBy(cb.desc(root.get("consume")));
break;
default:
break;
}
return cq.getRestriction();
}
How to achieve order by price null last with criteria builder ?
Hi I almost search all internet pages and then find a solution, you can write switch case order by part. like below: to order by desc if price is null, price value is 1000000, and to order by asc if price is null, price value is 0. if you want these, you can write expression like below.
EOrderByType orderType = EOrderByType.values()[orderBy];
Expression<Object> queryCase = cb.selectCase().when(cb.isNull(root.get("price")), 100000000).otherwise(root.get("price"));
Direction dir = Direction.ASC;
switch (orderType) {
case UCUZDAN_PAHALIYA:
queryCase = cb.selectCase().when(cb.isNull(root.get("price")), 100000000).otherwise(root.get("price"));
break;
case PAHALIDAN_UCUZA:
queryCase = cb.selectCase().when(cb.isNull(root.get("price")), 0).otherwise(root.get("price"));
dir = Direction.DESC;
break;
}
cq.where(predicate).orderBy(direction( cb, queryCase, dir));
This is a bit of an extension to katsu's answer to his own question. I was trying to find a solution to being able to sort most of the columns of a table where some columns are allowed to have null values. I wanted to sort the null values in front of the lowest non-null values when sorting in ascending order and after the lowest non-null values when sorting in descending order. In other words, pretty much the opposite of the (Oracle's) default behavior.
I found other methods that might do this, but this one didn't require me to go outside of Hibernate and JPA 2 persistence, but still get the results I wanted. This is a snippet of code taken from my actual code, but consolidated in one spot and with some names changed. Any syntax, compilation-type errors you see are probably due to that.
// sortByColumn is a String containing the Hibernate version of the column name, which had
// been assigned as the ID of the table header column of the column by which we are sorting.
// sortAscending is a Boolean object containing Boolean.TRUE if we are to sort in ascending
// order or Boolean.FALSE or null if we are to sort in descending order. This may seem a
// bit odd, but in the case we need this for, the default sort column is a release date and
// reverse chronological order is the most useful in that case.
// Also defined are: CriteriaQuery<SoftwareVersion> criteriaQuery and
// CriteriaBuilder criteriaBuilder by the typical means.
final Root<SoftwareVersion> softwareVersionRoot =
criteriaQuery.from(SoftwareVersion.class);
private static final String EMPTY_STRING = "";
if (sortByColumn != null && sortByColumn.trim().length() > 0) {
Order sortOrder;
Expression<String> sortColumnExpression;
if (sortByColumn.equalsIgnoreCase(SoftwareVersion_.installationFileLength.getName()) ||
sortByColumn.equalsIgnoreCase(SoftwareVersion_.releaseTimestamp.getName())) {
// The two non-String fields (exposed to the user) that we don't need to have the
// lower() function operate upon.
sortColumnExpression = oemSoftwareVersionRoot.get(sortByColumn);
} else {
// We use the lower() function to enforce case insensitive sorting on the columns we
// show to the user, which are all Strings except as noted above.
Expression<String> rootExpression = oemSoftwareVersionRoot.get(sortByColumn);
sortColumnExpression = criteriaBuilder.lower(rootExpression);
}
// The columns for installation file name, installation file length and release timestamp
// are just three of the columns that we allow the user to sort by. However, these three
// may have null values in the database, and require some special handling.
if (sortByColumn.equalsIgnoreCase(SoftwareVersion_.installationFileLength.getName()) ||
sortByColumn.equalsIgnoreCase(SoftwareVersion_.installationFileName.getName()) ||
sortByColumn.equalsIgnoreCase(SoftwareVersion_.releaseTimestamp.getName())
) {
Expression<Object> queryCase;
if (sortByColumn.equalsIgnoreCase(SoftwareVersion_.installationFileName.getName())) {
// Installation file name is a (case insensitive) String
queryCase = criteriaBuilder.selectCase().when(
criteriaBuilder.isNull(sortColumnExpression),
StringUtil.EMPTY_STRING).otherwise(sortColumnExpression);
} else if (sortByColumn.equalsIgnoreCase(SoftwareVersion_.releaseTimestamp.getName())) {
// Release timestamp is a database timestamp
LocalDateTime dateTime = LocalDateTime.of(1970,1,1,0,0);
// Equivalent to Unix epoch time. Note month is 1-12, not 0-11
queryCase = criteriaBuilder.selectCase().when(
criteriaBuilder.isNull(sortColumnExpression),
Timestamp.valueOf(dateTime)).otherwise(sortColumnExpression);
} else {
// Installation file length is a Long (or BigDecimal) computed when the file is uploaded.
// The user can't set or change it, but can sort by it.
queryCase = criteriaBuilder.selectCase().when(
criteriaBuilder.isNull(sortColumnExpression),
Long.valueOf(0)).otherwise(sortColumnExpression);
}
if (asc != null && asc.booleanValue()) {
sortOrder = criteriaBuilder.asc(queryCase);
} else {
sortOrder = criteriaBuilder.desc(queryCase);
}
} else {
if (asc != null && asc.booleanValue()) {
sortOrder = criteriaBuilder.asc(sortColumnExpression);
} else {
sortOrder = criteriaBuilder.desc(sortColumnExpression);
}
}
criteriaQuery.orderBy(sortOrder);
}

Lucene: Boolean OR in MultiFieldQueryParser

I have a database with 10 fields, and I need to construct a query that looks something like the following pseudo code:
theQuery = ((field1 == A) &&
(field2 == B) &&
(field3 == C) &&
(field4 == D) &&
(field5 == E) &&
(field6 == F) &&
(field7 == G) &&
((field8 == H) || (field9 == H) || (field10 == H)))
That is to say that I need fields 1-7 to definitely contain the corresponding supplied variable, and I need the variable H to definitely appear in at least one of fields 8-10.
I have been trying to use the MultiFieldQueryParser, but the problem that I have is that the BooleanClauses supplied are MUST, MUST_NOT and SHOULD, and we can set the default operator of the MultiFieldQueryParser to be either AND or OR.
When I try using AND and setting fields 1-7 with MUST and fields 8-10 with SHOULD, the query parser basically ignores fields 8-10 and gives me back anything that contains the specified data in fields 1-7.
I haven't yet tried setting the default operator to OR, because I'm guessing that the query will return results that contain one or more of the supplied variables in fields 1-10.
For those that wish to see code, my code is as follows:
ArrayList queries = new ArrayList();
ArrayList fields = new ArrayList();
ArrayList flags = new ArrayList();
if(varA != null && !varA.equals(""))
{
queries.Add(varA);
fields.Add("field1");
flags.Add(BooleanClause.Occur.Must);
}
//... The same for 2-7
if(varH != null && !varH.equals(""))
{
queries.Add(varA);
queries.Add(varA);
queries.Add(varA);
fields.Add("field8");
fields.Add("field9");
fields.Add("field10");
flags.Add(BooleanClause.Occur.Should);
flags.Add(BooleanClause.Occur.Should);
flags.Add(BooleanClause.Occur.Should);
}
Query q = MultiFieldQueryParser.parse(VERSION.LUCENE_34,
queries.toArray(),
fields.toArray(),
flags.toArray(),
theAnalyzer);
Obviously this is somewhat simplified as the ArrayLists don't neatly return me arrays of Strings and BooleanClause.Occurs, but you get the idea.
Does anyone know of a way of forming a multifield query, including both boolean ANDs and boolean ORs?
Thanks,
Rik
I don't really understand your notation, so it's hard to figure out what the problem is. But just use standard queries:
BooleanQuery topQuery = new BooleanQuery();
topQuery.add(new TermQuery(...), BooleanClause.Occur.Must);
etc.
Or just do it in text and let the parser parse it for you: +field1:A +field2:B ...

Creating a user friendly interface using an if statement in Java

I don't wish to leave a user stranded by returning nothing. Normally I can rectify this problem using a simple if else statement but since it's nested inside a for loop I don't get very good result. Below is my code for returning students attached to a module:
System.out.print("Search for a student: ");
scan = new Scanner(System.in);
String searchStudent = scan.nextLine().trim();
for (Student student : students) {
if (searchStudent.equalsIgnoreCase(student.getName())) {
Iterator it = modules.iterator();
Boolean found = false;
while (it.hasNext() && !found) {
Module module = (Module) it.next();
if (module.getStudents().contains(student)) {
System.out.printf("%s ", module.getName());
found = true;
}
}
} else {
System.out.println("Sorry. " + searchStudent + " does not exist in the database");
}
}
The output:
Search for a student: jane
UFCE3 UFCE1 Sorry. jane does not exist in the database
Sorry. jane does not exist in the database
Sorry. jane does not exist in the database
Sorry. jane does not exist in the database
Clearly in this example, Jane does exist in the database and she is enrolled on UFCE3 and UFCE1.
Since the if statement is nested inside the for loop I wouldn't expect anything less than getting an inaccurate output as the for loop will continue to loop until all elements in the student array have been passed. Any advice?
You can add a simple sentinel value (boolean flag) to your while statement. You start the value off as false, and then change it to true when a record is found.
Boolean found = false;
while (it.hasNext() && !found) {
Module module = (Module) it.next();
if (module.getStudents().contains(student)) {
System.out.printf("%s ", module.getName());
found = true;
}
Or you could use a "break" statement to terminate the loop.
while (it.hasNext() ) {
Module module = (Module) it.next();
if (module.getStudents().contains(student)) {
System.out.printf("%s ", module.getName());
break;
}
extract your for loop into a method, returning the modules you are interested in.
then call that method. check if you get any useful result and print it or print your excuse otherwise.
This is known as seperation of concerns. An entity should do exactly one thig. your for loop does at least three:
searching for students
searching for modules
printing results

Using a for each loop to find two elements in an array list

I need help writing a for each loop which searches through an array list called peoplelist of type people. The loop needs to search for the values String postcode and String name in the array. It then needs to return their ID if it is found, and null if it is not. Any sort of help would be great!
If the class People is written like a Java bean (i.e. with standard getter methods), something like this would do the job:
for (People person : peopleList) {
if (person.getName().equals(name) && person.getPostcode().equals(postCode))
return person.getId();
}
return null;
If a person's name or postcode can be null, you may want to flip the equals calls to avoid null pointer exceptions (e.g. name.equals(person.getName()) instead of person.getName().equals(name)).
Btw Person would be a better name.
Need to make a lot of assumptions about your classes, but something like this should suffice:
for (People person : peoplelist) {
if (person.getPostCode().equals(postcode) && person.getName().equals(name)) {
return person.getId();
}
}
// deal with not being found here - throw exception perhaps?
With “two elements”, do you mean “two attributes of some class”? If so, something along these lines would do:
String id = null;
for(People p : peoplelist) {
if(somePostcode.equals(p.postcode) && someName.equals(p.name)) {
id = p.id;
break; // no need to continue iterating, since result has been found
}
}
// result “id” is still null if the person was not found
//In case multiple persons match :)
List<String> result = new LinkedList<String>();
for (People person : peopleList) {
if (person.getName().equals(name) && person.getPostcode().equals(postCode))
result.add(person.getId());
}
if(result.isEmpty()){
return null;
}else{
return result;
}
People foundPerson;
for (People eachPeople : peoplelist )
{
if (Integer.valueOf(eachPeople.getID()) == 10054
&& "Jimmy".equals(eachPeople.getName()))
{
foundPerson= eachPeople;
break;
}
}
Assuming you have a Person bean, then if you want to retrieve all instances of Person whose postcode and name match some values, you may do something like this:
public List<Person> searchFirst(List<Person> persons, String postcode, String name) {
List<Person> matchingPersons = new ArrayList<Person>();
for (Person person : persons) {
if (person.getPostcode().equals(postcode) && person.getName().equals(name))
matchingPersons.add(person);
}
return matchingPersons;
}
Next time, you may want to show us your code, so we can help you in understanding what you're doing wrong :)

Casting a Object to HashMap

I'm having trouble working out how to count instances of Values in a HashMap.
I have seen that there is methods attached to the Object class that look as if they are able to help me, so I've tried to cast those in to work but I must be doing something wrong somewhere.
If there's an easier way, I haven't found it yet. NB: Library is my HashMap.
public void borrowBooks(String id, String name, String sid, String sname) {
if((getKeyFromValue(Books, name).equals(id))&&(getKeyFromValue(Students, sname).equals(sid))){
if((Object)Library.countValues(sid)!=5){
Library.put(id, sid);
}
else{
System.out.println("You have exceeded your quota. Return a book before you take one out." );
}
}
}
Which doc are you looking at ? The Javadoc for Hashmap doesn't specify a countValues() method.
I think you want a HashMap<String, List<String>> so you store a list of books per student (if I'm reading your code correctly).
You'll have to create a list per student and put that into the HashMap, but then you can simply count the entries in the List using List.size().
e.g.
if (Library.get(id) == null) {
Library.put(id, new ArrayList<String>());
}
List<String> books = Library.get(id);
int number = books.size() // gives you the size
Ignoring threading etc.
First: There is (almost) no point in ever casting anything to Object. Since everything extends Object, you can always access the methods without casting.
Second: The way you're casting actually casts the return value, not the Library. If you were doing a cast that was really necessary, you would need an extra set of parentheses:
if(((Object)Library).countValues(sid) != 5)
Third: There is no countValues method in either HashMap or Object. You'll have to make your own.
This is the general algorithm to use (I'm hesitant to post code because this looks like homework):
initialize count to 0
for each entry in Library:
if the value is what you want:
increment the count
int count = 0;
for(String str : Library.values())
{
if(str == sid)
count++;
if(count == 5)
break;
}
if(count < 5)
Library.put(id, sid);
else
System.out.println("You have exceeded your quota. Return a book before you take one out." );

Categories