I'm generating an SQL statement by checking if each of the column fields submitted to the query are empty (== null) or not. It seems that my approach is pretty naive so I'm wondering what can be done about handling null parameters elegantly. When something isn't specified it should simply match anything.
Here is the code:
public List<Flight> findMatchingFlights(Flight flight)
{
List<Flight> foundFlights = new ArrayList<>();
StringBuilder sqlQueryBuilder = new StringBuilder();
sqlQueryBuilder.append("SELECT * FROM Flights");
boolean emptyQuery = true;
if(flight.getDeparture() != null)
{
if(emptyQuery)
{
sqlQueryBuilder.append(" WHERE ");
emptyQuery = false;
}
sqlQueryBuilder.append("Departure = '" + flight.getDeparture() + "'");
}
if(flight.getArrival() != null)
{
if(emptyQuery)
{
sqlQueryBuilder.append(" WHERE ");
emptyQuery = false;
}
else
{
sqlQueryBuilder.append(" AND ");
}
sqlQueryBuilder.append("Arrival = '" + flight.getArrival() + "'");
}
if(flight.getFlightNumber() != null)
{
if(emptyQuery)
{
sqlQueryBuilder.append(" WHERE ");
emptyQuery = false;
}
else
{
sqlQueryBuilder.append(" AND ");
}
sqlQueryBuilder.append("Number = '" + flight.getFlightNumber() + "'");
}
if(flight.getFlightMinutes() != 0)
{
if(emptyQuery)
{
sqlQueryBuilder.append(" WHERE ");
emptyQuery = false;
}
else
{
sqlQueryBuilder.append(" AND ");
}
sqlQueryBuilder.append("Duration = " + flight.getFlightMinutes());
}
/*
...
A bunch more fields
*/
if(flight.getAirplane() != null)
{
if(emptyQuery)
{
sqlQueryBuilder.append(" WHERE ");
}
else
{
sqlQueryBuilder.append(" AND ");
}
sqlQueryBuilder.append("Airplane = '" + flight.getAirplane() + "'");
}
sqlQueryBuilder.append(";");
// Execute sql and fill list with rows that match
}
You can create the common method for the below block and call the method by passing the arguments.
if(flight.getArrival() != null)
{
if(emptyQuery)
{
sqlQueryBuilder.append(" WHERE ");
emptyQuery = false;
}
else
{
sqlQueryBuilder.append(" AND ");
}
sqlQueryBuilder.append("Arrival = '" + flight.getArrival() + "'");
}
Best approach would be to do the trick in SQL than checking for null in Java. This is how you can do it.
sqlQueryBuilder.append("(Number = '" + flight.getFlightNumber() + "' OR " + flight.getFlightNumber() + " IS NULL)");
This way you wont have to check null in java, if the flight.getFlightNumber() is null then this where clause will always return true which is what you would want.
The only drawback to this method is that the clause will be included in the query, but since you are intending to use these columns to query the table incase they are not null, i would assume the table would be indexed likewise.
sqlQueryBuilder.append("SELECT * FROM Flights WHERE 1 = 1");
then you don't need emptyQuery flag and checks and many if else constractions.
List<Flight> foundFlights = new ArrayList<>();
StringBuilder sqlQueryBuilder = new StringBuilder();
sqlQueryBuilder.append("SELECT * FROM Flights WHERE 1 = 1");
if(flight.getDeparture() != null) {
sqlQueryBuilder.append("AND Departure = '" + flight.getDeparture() + "'");
}
Related
I'm using a spring framework and the code I'm using won't work or check if the query is null, though I used a .isEmpty() method it doesn't mean that the query is empty. I wanted to make sure that my query is empty because a part of my code does invoke an id in which case I didn't even though its null so please help me T.T
public List<Object> searchEmployee(EmployeeSearchDto data) {
Session session = sessionFactory.openSession();
final String CRITERIA_EMPLOYEEID = "emp.employeeID =:id";
final String CRITERIA_EMPLOYEEID2 = "emp.employeeID LIKE:id";
final String CRITERIA_POSITION= "emp.positionID =:posID";
final String CRITERIA_DEPARTMENT="emp.departmentID =:deptID";
final String CRITERIA_WORKPLACE = "emp.workplaceID =:workID";
Boolean selected_dept = false;
Boolean selected_pos = false;
Boolean selected_work = false;
Boolean input_empID = false;
Boolean input_empName = false;
firstName = "";
middleName = "";
lastName = "";
completeName = "";
firstLastName = "";
List<String> criteria = new ArrayList<>();
List<Object> employees = null;
// checking the fields if all the fields is empty
try{
//one by one check the select field
String query = "Select"
+ " emp.employeeID,"
+"emp.firstName,"
+"emp.middleName,"
+"emp.lastName,"
+"pos.positionName,"
+"dept.deptName,"
+"work.workplaceName"
+"from Employee emp "
+ "INNER JOIN Department dept "
+ "ON emp.departmentID = dept.deptID "
+ "INNER JOIN Position pos "
+ "ON emp.positionID = pos.positionID "
+ "INNER JOIN Workplace work "
+ "ON emp.workplaceID = work.workplaceID ";
if(!data.isEmpty()) {
query = query.concat("WHERE ");
if(data.getEmployeeID()!="" && data.getEmployeeID()!=null) {
criteria.add(CRITERIA_EMPLOYEEID2);
System.out.println("Employee IDs");
input_empID = true;
}
if(data.getEmployeeName()!="" && data.getEmployeeName()!=null){
criteria.add(nameCriteriaHelper(data.getEmployeeName()));
System.out.println("Employee Name AKOOO");
input_empName = true;
}
if(data.getDepartmentID()!=0) {
criteria.add(CRITERIA_DEPARTMENT);
System.out.println("Dept ID ");
selected_dept = true;
}
if(data.getPositionID()!=0) {
criteria.add(CRITERIA_POSITION);
System.out.println("POS ID ");
selected_pos = true;
}
if(data.getWorkplaceID()!=0) {
criteria.add(CRITERIA_WORKPLACE);
selected_work = true;
}
query = query.concat(String.join(" OR ", criteria));
}
query = query.concat(" ORDER BY emp.joinDate DESC");
System.out.println("QUERY: " + query);
Query q = session.createQuery(query);
if(input_empID) {
q.setParameter("id", "%" + data.getEmployeeID() + "%");
}
if(input_empName) {
if(searchbyOne)
q.setParameter("inputName", "%" + data.getEmployeeName() + "%");
if(searchbyFandL)
q.setParameter("firstLastName", "%" +firstLastName+ "%");
if(searchbyCompName)
q.setParameter("completeName", "%" +completeName+ "%");
}
if(selected_dept) {
q.setParameter("deptID", data.getDepartmentID());
}
if(selected_pos) {
q.setParameter("posID", data.getPositionID());
}
if(selected_work) {
q.setParameter("workID", data.getWorkplaceID());
}
employees = (List<Object>) q.list();
}catch(Exception e){
e.printStackTrace();
}finally{
session.close();
}
return employees;
}
public String nameCriteriaHelper(String name) {
searchbyOne = false;
searchbyFandL = false;
searchbyCompName = false;
final String noOfTokens_1 = "CONCAT(emp.lastName,' ',emp.firstName, ' ',emp.middleName) LIKE :inputName";
final String noOfTokens_2 = "(CONCAT(emp.lastName, ' ', emp.firstName) LIKE :firstLastName "
+ "OR CONCAT(emp.firstName, ' ', emp.lastName) LIKE :firstLastName)";
final String noOfTokens_3 = "CONCAT(emp.lastName,' ',emp.firstName, ' ',emp.middleName) LIKE :completeName";
StringTokenizer stringTokenizer = new StringTokenizer(name);
int no_of_tokens = stringTokenizer.countTokens();
switch(no_of_tokens) {
case 1: searchbyOne = true;
return noOfTokens_1;
case 2: firstName = stringTokenizer.nextToken();
lastName = stringTokenizer.nextToken();
firstLastName = lastName + " " + firstName;
searchbyFandL = true;
return noOfTokens_2;
default: int counter = 0;
while( counter < (no_of_tokens - 2)) {
firstName = firstName.concat(stringTokenizer.nextToken() + " ");
counter++;
}
firstName = stringTokenizer.nextToken();
middleName = stringTokenizer.nextToken();
lastName = stringTokenizer.nextToken();
completeName = lastName + " " + firstName + " " + middleName;
searchbyCompName = true;
return noOfTokens_3;
}
You're using wrong order and wrong function to compare string:
Replace:
data.getEmployeeID()!="" && data.getEmployeeID()!=null
With
data.getEmployeeID() != null && !data.getEmployeeID().equals("")
Comparing string must use equals(). And check for null should be done first, before accessing the equals method
You should correct other conditions as above too.
Actually, the logic that Mr. Nguyễn provided here is faulty. An object or variable cannot both be null and initialized to a default value (such as foo == "") at the same time.
At the time of the logic check, if the String is in fact null, the second half of the logic statement will engage, checking to see if the String is equal to "", which will throw a null pointer exception. Instead of checking for both at the same time, check for one and then check for the other like so:
//since two logic checks are being performed,
//it is advantageous to put the data from the query
//into memory so you don't have to get the
//same result twice
String foo = data.getEmployeeID();
if (foo != null)
{
if (!(foo.equals("")))
{
//the result is neither null or empty
}
else
{
//the result is not null but it is empty
}
}
else
{
//the result is null
}
I don't understand why in this code, it's do that:
query.setString("idFamilleDeProduit", String.valueOf(familleDeProduits.getFamilleDeProduitsGenerique().getId()));
When I looked my table in my database, id column is integer.
it's PostgreSql-9.4.1207
my function:
public List<ContratMdd> recherchePourDuplication(String typeAccord, FamilleDeProduitsNomenclature familleDeProduits, SocieteInterne societeInterne, SocieteExterne societeExterne, String anneeAccord) throws PersistenceException {
List<ContratMdd> listContratMdd = new ArrayList<ContratMdd>();
String requete = "";
if (!"".equals(anneeAccord)){
requete += " anneeAccord = :anneeAccord";
}
if (!"".equals(typeAccord) && ! "".equals(requete)){
requete += " AND";
}
if (!"".equals(typeAccord)){
requete += " type = :type";
}
boolean existFamille = false;
requete += (familleDeProduits != null && familleDeProduits.getFamilleDeProduitsGenerique() != null) ? " AND " : "";
if(familleDeProduits != null && familleDeProduits.getFamilleDeProduitsGenerique() != null){
existFamille = true;
requete += " estAppliqueSur.familleDeProduitsGenerique IS NOT NULL AND estAppliqueSur.familleDeProduitsGenerique.id = :idFamilleDeProduit";
}
boolean existSocieteInterne = false;
boolean existSocieteExterne = false;
requete += (societeInterne != null) ? " AND " : "";
if(societeInterne != null){
existSocieteInterne = true;
String table = societeInterne instanceof Master ? "MasterImpl" : "AdherentImpl";
requete += " contractantInterne.id = :idsocieteInterne AND contractantInterne IN (FROM "+table+") ";
}
requete += (societeExterne != null) ? " AND " : "";
if(societeExterne!=null){
existSocieteExterne = true;
String table = societeExterne instanceof GroupeIndustriel ? "GroupeIndustrielImpl" : "FournisseurImpl";
requete += " contractantExterne.id = :idsocieteExterne AND contractantExterne IN (FROM "+table+") ";
}
if (!"".equals(requete)) {
requete = "from ContratMddImpl where" + requete;
Query query = createQuery(requete);
if (!"".equals(anneeAccord)){
query.setBigInteger("anneeAccord", new BigInteger(anneeAccord));
}
if (!"".equals(typeAccord)){
query.setString("type", typeAccord);
}
if(existFamille){
query.setString("idFamilleDeProduit", String.valueOf(familleDeProduits.getFamilleDeProduitsGenerique().getId()));
}
if (existSocieteInterne){
query.setInteger("idsocieteInterne", societeInterne.getId());
}
if (existSocieteExterne){
query.setInteger("idsocieteExterne", societeExterne.getId());
}
listContratMdd.addAll((List<ContratMdd>) query.list());
}
return listContratMdd;
}
It is happening because the Postgre's DB Driver allows it. But you should be using setInt() instead of setString() for an Integer as other DB Drivers might not support it.
Here is what java.sql.PreparedStatement Documentation has to say:
Note: The setter methods (setShort, setString, and so on) for setting
IN parameter values must specify types that are compatible with the
defined SQL type of the input parameter. For instance, if the IN
parameter has SQL type INTEGER, then the method setInt should be used.
I'm trying to get a user's stored name from Parse using the following code:
ParseUser puParseUser = ParseUserProfile.getCurrentUser();
String sParseUserObjectID = puParseUser.getObjectId();
ParseQuery<ParseUserProfile> pqUserProfileQuery = ParseQuery.getQuery(ParseUserProfile.class);
pqUserProfileQuery.getInBackground(sParseUserObjectID, new GetCallback<ParseUserProfile>() {
#Override
public void done(ParseObject object, ParseException e) {
if (e == null) {
sFirstName = object.getString(getResources().getString(R.string.keyParseFirstName));
sMiddleName = object.getString(getResources().getString(R.string.keyParseMiddleName));
sSurname = object.getString(getResources().getString(R.string.keyParseSurname));
if (sFirstName == null) {
Log.e(KEAMapActivity.this.toString(), "ParseUser FirstName is NULL: " + sFirstName); // TODO: For Testing ONLY
txtDrawerHeader.setText(getResources().getString(R.string.sWelcome));
} else if (sMiddleName == null) {
Log.e(KEAMapActivity.this.toString(), "ParseUser MiddleName is NULL: " + sMiddleName); // TODO: For Testing ONLY
txtDrawerHeader.setText(getResources().getString(R.string.sWelcome));
} else if (sSurname == null) {
Log.e(KEAMapActivity.this.toString(), "ParseUser Surname is NULL: " + sSurname); // TODO: For Testing ONLY
txtDrawerHeader.setText(getResources().getString(R.string.sWelcome));
} else {
Log.e(KEAMapActivity.this.toString(), "ParseUser Name is: " + sFirstName + sMiddleName + sSurname); // TODO: For Testing ONLY
sMiddleName = sMiddleName.substring(0, 1) + ".";
sFullName = sFirstName + " " + sMiddleName + " " + sSurname;
txtDrawerHeader.setText(sFullName);
}
} else {
if (e.getCode() == ParseException.OBJECT_NOT_FOUND) {
Log.e(KEAMapActivity.this.toString(), "ParseUser OBJECT NOT FOUND"); // TODO: For Testing ONLY
txtDrawerHeader.setText(getResources().getString(R.string.sWelcome));
} else {
Log.e(KEAMapActivity.this.toString(), "Error retrieving User's Name: " + e.getMessage()); // TODO: For testing ONLY.
}
txtDrawerHeader.setText(getResources().getString(R.string.sWelcome));
}
}
});
The above code returns: OBJECT_NOT_FOUND exception. But in my own conscience, I know a parse object with the same objectId I provide in this code exists and it's a user object containing user data. I want to get the user's first, middle and last name.
Please note ParseUserProfile is a subclass of com.parse.ParseUser class.
What can I do to solve this? If you need more detail or have any question regarding my code, please ask.
Hi I'm trying to filter records on the basis of Brand,Flavor,Price,Size,Type. i am using a single form to handle this so if user filter only by brand then rest of options like price,flavor will be unchecked so i am checking if Brand or flavor or price is null.I got the solution but i have make lot of comparisons for all cases. i need a solution by which i have make less comparisons.I am using following code
public List<Products> Filter_Items(String[] Brand, String[] Flavour,Float Price,String Size,String Type)
{
ResultSet rs;
List<Products> data = null;
PreparedStatement stmt;
try {
StringBuilder param = new StringBuilder();
if (Brand != null) {
for (String str : Brand) {
param.append("'").append(str).append("', ");
}
}
StringBuilder param1 = new StringBuilder();
if (Flavour != null) {
for (String str : Flavour) {
param1.append("'").append(str).append("', ");
}
}
String prm = param.toString().length() > 2 ? param.toString()
.substring(0, param.toString().length() - 2) : null;
String prm1 = param1.toString().length() > 2 ? param1.toString()
.substring(0, param1.toString().length() - 2) : null;
String query = "select * from products where ";
if (prm != null && prm1 != null) {
query += "Brand in (" + prm + ") and Flavour in (" + prm1 + ")";
} else if (prm != null && prm1 == null) {
query += "Brand in (" + prm + ")";
} else if (prm1 != null && prm == null) {
query += "Flavour in (" + prm1 + ")";
}
stmt = DataBaseConnection.DBConn.getConnection().prepareStatement(query);
rs = stmt.executeQuery();
if (rs != null) {
data = new ArrayList<Products>();
while (rs.next()) {
Products p = new Products();
p.setTitle(rs.getString("Ttile"));
p.setCategory(rs.getString("Category"));
p.setSubCategory(rs.getString("SubCategory"));
p.setSubCategoryTwo(rs.getString("SubCategorytwo"));
p.setPrice(rs.getInt("Price"));
p.setFlavour(rs.getString("Flavour"));
p.setSize(rs.getString("Size"));
p.setImage(rs.getString("image"));
p.setBrand(rs.getString("Brand"));
p.setInstock(rs.getString("instock"));
p.setInstockQty(rs.getInt("instockqty"));
data.add(p);
}
}
} catch (Exception e) {
System.out.println(e.getStackTrace());
return null;
}
return data;
}
}
You can use as always third party libraries for validation like javax validation or any other more specialized on this.
You could create a bean of your data with all the getters and setters and apply validation on your bean.
To pass arrays you need getters and setters like this one:
public class Foo {
private int[] array;
public Foo(int[] array) {
this.array = Arrays.copyOf(array, array.length);
}
/** #param the array to use */
public setArray(int[] array) {
this.array = Arrays.copyOf(array, array.length);
}
/** #return a copy of the array */
public int[] getArray() {
return Arrays.copyOf(array, array.length);
}
}
To validate a bean see this tutorial.
What you really need is to start naming variables according to what they are logically. Names like prm and prm1 are meaningless, while brand and flavor make more sense as variable names since that's what's in them.
I don't see the number of comparisons as a problem. But giving everything generic variable names and then not even indenting the if-statements makes my eyes glaze over.
How about something like this:
if (brand != null && flavor != null)
{
query += "Brand in (" + brand + ") and Flavour in (" + flavor + ")";
}
else if (brand != null && flavor == null)
{
query += "Brand in (" + brand + ")";
}
else if (flavor != null && brand == null)
{
query += "Flavour in (" + flavor + ")";
}
Edit: One thing you can do is split your SQL into two variables, one for the main query and one for the where clause, then just add to the where clause like this:
private String addToWhereClause(String currentWhereClause, String fieldname, String value)
{
String returnValue = "";
if (value != null)
{
if(!"".equals(currentWhereClause))
{
returnValue += " AND ";
}
returnValue += " "+fieldname+" IN(" + value + ") ";
}
return returnValue;
}
String whereClause = "";
whereClause += addToWhereClause(whereClause, "Brand", brand);
whereClause += addToWhereClause(whereClause, "Flavour", flavor);
query = query + " " + whereClause;
I have written a multithreaded application that analyzes rows in a database with regex and updates them appropriately. I am writing each row to a log file for logging purposes. I have noticed that the same row is being written to the log file several times...sometimes upwards of 15 times. Here are snippets of the code.
Setting up ThreadPoolExecuter:
private static BlockingQueue<Runnable> worksQueue = new ArrayBlockingQueue<Runnable>(blockingQueueSize);
private static ThreadPoolExecutor exec = new ThreadPoolExecutor(threadPoolSize, threadPoolSize, 10, TimeUnit.SECONDS, worksQueue);
In this part, I run a query, then go through the results:
rs = ps.executeQuery();
while (rs.next()) {
exec.execute(new UpdateMember(rs, conn, fileWriter));
if (worksQueue.size() == blockingQueueSize) {
//reach the maximum, stop refill
for (;;) {
Thread.yield();
//wait until the size of queue reached the minimum
if (worksQueue.size() == 0) {
//start refill
break;
}
}
}
}
UpdateMember (with only run and writeToLog methods showing):
public class UpdateMember implements Runnable {
ResultSet rs;
Connection conn;
FileWriter fw;
public UpdateMember(ResultSet rs, Connection conn, FileWriter fw) {
this.rs = rs;
this.conn = conn;
this.fw = fw;
}
#Override
public void run() {
try {
String regex = "((?<city>[a-zA-Z\\s\\.]+)\\s)?(?<provState>AB|ALB|Alta|alberta|BC|B\\.C\\.|British Columbia|LB|Labrador|MB|Man|Manitoba|N[BLTSU]|Nfld|NF|Newfoundland|NWT|Northwest Territories|Nova Scotia|New Brunswick|Nunavut|ON|ONT|Ontario|PE|PEI|Prince Edward Island|QC|PC|QUE|QU|Quebec|SK|Sask|Saskatchewan|YT|Yukon|Yukon Territories)(\\s(?<country>CA|CAN|CANADA))?$";
Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
BigDecimal memrecno = rs.getBigDecimal(2);
String addressLineTwo = rs.getString(4);
String addressLineThree = rs.getString(5);
String addressLineFour = rs.getString(6);
BigDecimal attrrecno = rs.getBigDecimal(9);
String addressBeingParsed = "";
String city = null;
String province = null;
String country = null;
boolean usingAddressThree = false;
boolean usingAddressFour = false;
if (addressLineFour == null) {
if (addressLineThree == null) {
city = "Unknown";
}
else
{
addressBeingParsed = addressLineThree;
usingAddressThree = true;
}
}
else
{
addressBeingParsed = addressLineFour;
usingAddressFour = true;
}
if (usingAddressThree || usingAddressFour) {
Matcher matcher = pattern.matcher(addressBeingParsed);
// if matches are found
if (matcher.matches()) {
city = matcher.group("city");
province = matcher.group("provState");
country = matcher.group("country");
if (city == null || city.isEmpty()) {
// cities are alpha characters and spaces only
String cityRegex = "(?<city>^[a-zA-Z\\s\\.]+$)";
Pattern cityPattern = Pattern.compile(cityRegex, Pattern.CASE_INSENSITIVE);
if (usingAddressFour && (addressLineThree != null) && !addressLineThree.isEmpty()) {
Matcher cityMatcher = cityPattern.matcher(addressLineThree);
if (cityMatcher.matches()) {
city = cityMatcher.group("city");
}
else
{
city = "Unknown";
}
}
else if (usingAddressThree && (addressLineTwo != null) && !addressLineTwo.isEmpty()) {
Matcher cityMatcher = cityPattern.matcher(addressLineTwo);
if (cityMatcher.matches()) {
city = cityMatcher.group("city");
}
else
{
city = "Unknown";
}
}
else
{
city = "Unknown";
}
}
if (province != null && !province.isEmpty()) {
province = createProvinceCode(province);
}
}
else
{
city = "Unknown";
}
}
// update attributes in database
boolean success = updateRow(memrecno, attrrecno, city, province);
String logLine = memrecno.toString() + "|" + attrrecno.toString() + "|" + addressLineTwo + "|" + addressLineThree + "|" + addressLineFour + "|" + city + "|" + province + "|" + country + "|" + success + "|" + String.valueOf(Thread.currentThread().getId());
writeToLog(logLine);
}
catch (Exception e)
{
e.printStackTrace();
}
}
private synchronized void writeToLog(String line) {
try {
fw.write(line + "\r\n");
fw.flush();
}
catch (IOException ex)
{
System.out.println("Error writing to log file. " + ex.getMessage());
}
}
}
I don't know if the threads are also calling the updateRow method multiple times, but I'm assuming they are and that's really bad.
Any ideas as to why it would be doing this?
I don't think ResultSet is thread safe. From your code, you should get the value first and then pass the value instead of rs into the thread.