Is there a better way to write this? Checking scenarios - java

So I have this code that checks 4 parameters (author, title, keyword and subject) to do some dynamic sql query generation for a project I'm working on and I was just wondering if anyone knew of a better way to write this out. I get the feeling this is a really inefficient way to do this but it's late and I'm tired. Please let me know if you know of any better way to do this, it's just a bunch of if/else case checking statements at this point.
Thanks!
if(_author!=null)
{
query += authorQ;
if(_title != null)
{
if(conjunct[0] == 0)
{
query += " AND ";
query += titleQ;
}
else
{
query += " OR ";
query += titleQ;
}
if(_keyword != null)
{
if(conjunct[1] == 0)
{
query += " AND ";
query += keywordQ;
}
else
{
query += " OR ";
query += keywordQ;
}
if(_subject != null)
{
if(conjunct[2] == 0)
{
query += " AND ";
query += subjectQ;
}
else
{
query += " OR ";
query += subjectQ;
}
}
}
else
{
if(_subject != null)
{
if(conjunct[2] == 0)
{
query += " AND ";
query += subjectQ;
}
else
{
query += " OR ";
query += subjectQ;
}
}
}
}//title = null
else
{
if(_keyword != null)
{
if(conjunct[1] == 0)
{
query += " AND ";
query += keywordQ;
}
else
{
query += " OR ";
query += keywordQ;
}
if(_subject != null)
{
if(conjunct[2] == 0)
{
query += " AND ";
query += subjectQ;
}
else
{
query += " OR ";
query += subjectQ;
}
}
}
else //keyword null
{
if(_subject != null)
{
if(conjunct[2] == 0)
{
query += " AND ";
query += subjectQ;
}
else
{
query += " OR ";
query += subjectQ;
}
}
//if subject's null at this point we don't care
}
}
}
else //author null
{
if(_title != null)
{
if(conjunct[0] == 0)
{
query += " AND ";
query += titleQ;
}
else
{
query += " OR ";
query += titleQ;
}
if(_keyword != null)
{
if(conjunct[1] == 0)
{
query += " AND ";
query += keywordQ;
}
else
{
query += " OR ";
query += keywordQ;
}
if(_subject != null)
{
if(conjunct[2] == 0)
{
query += " AND ";
query += subjectQ;
}
else
{
query += " OR ";
query += subjectQ;
}
}
}
else
{
if(_subject != null)
{
if(conjunct[2] == 0)
{
query += " AND ";
query += subjectQ;
}
else
{
query += " OR ";
query += subjectQ;
}
}
}
}//title = null
else
{
if(_keyword != null)
{
if(conjunct[1] == 0)
{
query += " AND ";
query += keywordQ;
}
else
{
query += " OR ";
query += keywordQ;
}
if(_subject != null)
{
if(conjunct[2] == 0)
{
query += " AND ";
query += subjectQ;
}
else
{
query += " OR ";
query += subjectQ;
}
}
}
else //keyword null
{
if(_subject != null)
{
if(conjunct[2] == 0)
{
query += " AND ";
query += subjectQ;
}
else
{
query += " OR ";
query += subjectQ;
}
}
//if subject's null at this point we don't care
}
}
}

How about a more structured and scalable approach, see example below. By the way, you shouldn't really be putting user input straight into SQL queries - use prepared statements instead to try to guard against injection attacks.
// test values
String _author = "authorfoo";
String _title = "titlebar";
String _keyword = null;
String _subject = "subjectfoo";
String authorQ = "author=" + _author;
String subjectQ = "subject=" + _subject;
String titleQ = "title="+ _title;
String keywordQ = "keyword=" + _keyword;
int conjunct[] = new int[]{ 0, 1, 1, 0};
// query building code
String []inputs = {_author, _title, _keyword, _subject};
String []queries = {authorQ, titleQ, keywordQ, subjectQ};
StringBuilder builder = new StringBuilder();
for (int i = 0 ; i < inputs.length; i++) {
if (inputs[i] != null) {
if (builder.length() > 0) { // don't start query with connective
if (conjunct[i] == 0) {
builder.append(" AND ");
} else {
builder.append(" OR ");
}
}
builder.append(queries[i]);
}
}
String query = builder.toString();
System.out.println(query);

a = _author ? authorQ : 1;
t = _title ? titleQ : 1;
k = _keyword ? keywordQ : 1;
s = _subject ? subjectQ : 1;
c1 = conjunct[0] ? " OR " : " AND ";
c2 = conjunct[1] ? " OR " : " AND ";
c3 = conjunct[2] ? " OR " : " AND ";
query += a + c1 + t + c2 + k + c3 + s;

Related

Restrictions OR in hibernate (Java)

I want rewrite sql query to hibernate criteria and I have this problem.
It is original:
if (null != filterGroupIds && !filterGroupIds.isEmpty()) {
hqueryText += (requereAnd ? " and " : "") + "(";
requereAnd = true;
if (filterGroupIds.contains("null")) {
hqueryText += " w.crmUser.groupId = null ";
filterGroupIds.remove("null");
hqueryText += filterGroupIds.isEmpty() ? "" : "or";
}
if (!filterGroupIds.isEmpty())
hqueryText += " w.crmUser.groupId in (" + StringUtils.join(filterGroupIds.iterator(), ",") + ")";
hqueryText += ")";
}
It is my criteria:
if (null != filterGroupIds && !filterGroupIds.isEmpty()) {
if (filterGroupIds.contains(Integer.valueOf(-1))) {
criteria.add(Restrictions.eq("crmUser.groupId", null));
filterGroupIds.remove(Integer.valueOf(-1));
if (!filterGroupIds.isEmpty()) {
criteria.add(Restrictions.or(Restrictions.in("crmUser.groupId", filterGroupIds)));
}
}else {
criteria.add(Restrictions.in("crmUser.groupId", filterGroupIds));
}
}
but my OR Restrictions not work.
You should be getting compile time error for
criteria.add(Restrictions.or(Restrictions.in("crmUser.groupId", filterGroupIds)));
as Restrictions.or() takes two criterion values.Recheck your code and pass two criterion object to be compared.

code part I do not understand

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.

Handling SQL Null Parameters Elegantly

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() + "'");
}

Building a string recursively in Java

I am trying to build a string recursively but it isn't quite working
my code looks like this
public void UpdatePrintList(ArrayList<Node> closedList, ArrayList<Node> openList)
{
if(count <= iterations)
{
String line1 = "";
for(int i = 0; i < closedList.size(); i++)
{
if(i > 0)
{
line1 = line1 + "-";
}
line1 = line1 + closedList.get(i).GetMovement();
}
line1 = line1 + " " + closedList.get(closedList.size()-1).GetG() + " " + closedList.get(closedList.size()-1).GetHeuristic() + " " + closedList.get(closedList.size()-1).GetF();
printList.add(line1);
//*****************************************************************
String line2 = "OPEN ";
for(int i = 0; i < openList.size(); i++)
{
line2 = FindEarlierNode(openList.get(i), line2);
}
System.out.println(line2);
}
count++;
}
private String FindEarlierNode(Node varNode, String varString)
{
if(varNode.OpenedBy() == null)
{
varString += varNode.GetMovement() + "-";
}
else
{
FindEarlierNode(varNode.OpenedBy(), varString);
}
varString = varString + varNode.GetMovement() + "-";
return varString;
}
The strange thing is that I know that this if statement
if(varNode.OpenedBy() == null)
{
varString += varNode.GetMovement() + "-";
}
runs correctly, so the function does reach the earliest node. But it doesnt add to the string. The code runs but returns nothing. GetMovement just returns a one or two character string. The output should look like this:
OPEN S-R S-RD S-D
But instead it looks like this:
OPEN D-DL-L-
Can anyone help?
Managed to work it out. This gives me my desired output:
private String FindEarlierNode(Node varNode, String varString)
{
if(varNode.OpenedBy() != null)
{
varString = varString + varNode.GetMovement() + "-";
return FindEarlierNode(varNode.OpenedBy(), varString);
}
return varString += varNode.GetMovement() + " ";
}
thanks everyone.

Avoid comparison from conditional statements

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;

Categories

Resources