I am trying to improve performance of an api. I need to know whether the second line that's marked will hit database too ? as I want to minimise that.
StringBuffer queryBuf = new StringBuffer("some query in SQL");
--------->StringBuffer queryBuf2 = new StringBuffer(" SELECT DISTINCT PP.ID FROM ( " + queryBuf + ") PP ");
Query query1 = getSession().createSQLQuery(queryBuf2.toString());
query1.setReadOnly(true);
ScrollableResults results = query1.scroll();
if (results.isLast() == false)
results.last();
int total = results.getRowNumber() + 1;
results.close();
logger.debug(">>>>>>TOTAL COUNT<<<<<< = {}", total);
No. Only the line ScrollableResults results = query1.scroll(); executes sql.
Also, you may want to use SQL COUNT
Its plain and simple it wont hit , you are just creating StringBuffer objects.
you are just create a string buffer. its not hitting the db. If i right this code may give compile error.
I need to know whether the second line that's marked will hit database too ?
You are constructing StringBuffer objects in first three lines , why should it hit the DB ! You can use StringBuilder if synchronization is not required !
No it will not hit the database. It will query from the buffer created by first query.
Related
This question already has answers here:
How to insert values in a table with dynamic columns Jdbc/Mysql
(2 answers)
Closed 5 years ago.
What is a good design pattern to achieve this without endless code?
Given the scenario whereby the user may input 1...100 columns, maybe 23 one time, 32 on another insert, and 99 fields on another insert etc. All of which may be different fields each time too.
The PreparedStatement in Java needs to know what column names to enter first, how many ?'s to put into the values part of the INSERT query, the data types of the database field names to ensure the correct setInt and setString etc are entered.
For less than around 10 columns, you can kind of get around this challenge with the following logic;
1) If variableEnteredForFieldName is not null, then append to the relevant parts of the query in the form of a String builder type setup;
fieldName_1
?
2) Do the same for all entered field names
3) Strip out the final trailing , that will naturally be present in both the field names and the ?s
4) Create the PreparedStatement
5) Run through the same input parameters again to determine of the variableEnteredForFieldName is not null, if not null, then run a setInt or setString based on the known data type that the database requires and set this to the correct index number for the ?s.
As long as the query builder logic and the query filler logic have the names/values in the correct order in part 1 and part 2, then all works well. It does however mean duplicating the entire code that relates to this logic, one for generating the SQL to use when creating the PreparedStatement and another for filling the PreparedStatement.
This is manageable for a small number of input parameters, but this soon gets unmanageable for larger number of input parameters.
Is there a better design pattern to achieve the same logic?
The code below is an outline of all of the above for reference;
String fieldName1 = request.getParameter("fieldName1");
String fieldName2 = request.getParameter("fieldName2");
//Build Query
String fieldNames = "";
String fieldQuestionMarks = "";
if (fieldName1 != null) {
fieldNames = fieldNames + " FIELD_NAME_1 ,";
fieldQuestionMarks = fieldQuestionMarks + " ? ,";
}
if (fieldName2 != null) {
fieldNames = fieldNames + " FIELD_NAME_2 ,";
fieldQuestionMarks = fieldQuestionMarks + " ? ,";
}
//Trim the trailing ,
fieldNames = fieldNames.substring(1, fieldNames.length() - 1);
fieldQuestionMarks = fieldQuestionMarks.substring(1, fieldQuestionMarks.length() - 1);
try {
String completeCreateQuery = "INSERT INTO TABLE_NAME ( " + fieldNames + " ) VALUES ( " + fieldQuestionMarks + " );";
Connection con = DriverManager.getConnection(connectionURL, user, password);
PreparedStatement preparedStatement = con.prepareStatement(completeCreateQuery);
int parameterIndex = 1;
//Fill Query
if (fieldName1 != null) {
preparedStatement.setString(parameterIndex, fieldName1);
parameterIndex++;
}
if (fieldName2 != null) {
preparedStatement.setInt(parameterIndex, Integer.parseInt(fieldName2));
parameterIndex++;
}
}
As you can see, it's do-able. But even with just 2 optional fields, this code is huge.
The way I see it, if user is able to omit any of the columns from the list, then all columns are optional, and can be safely set to NULL during an insert. Therefore, all you need is one prepared statement with the "monster" INSERT, with all columns listed; then during the actual insert operation, you loop though the user-provided data, setting values for the columns provided, and calling setNull() for omitted columns. You'll need to maintain a structure somewhere (your DAO class most likely) mapping column names to their order in the SQL statement.
I have a use case where I want to know the columns which have been selected in an SQL string.For instance, if the SQL is like this:
SELECT name, age*5 as intelligence FROM bla WHERE bla=bla
Then, after parsing the above String, I just want the output to be: name, intelligence.
Firstly, is it possible through Calcite?
Any other option is also welcome.
PS: I want to know this before actually running the query on database.
This is definitely doable with Calcite. You'll want to start by creating an instance of SqlParser and parsing the query:
SqlParser parser = SqlParser.create(query)
SqlNode parsed = parser.parseQuery()
From there, you'll probably have the most success implementing the SqlVisitor interface. You'll want to first find a SqlSelect instance and then visit each expression being selected by calling visit on each element of getSelectList.
From there, how you proceed will depend on the complexity of expressions you want to support. However, it's probably sufficient to recursively visit all SqlCall nodes and their operands and then collect any SqlIdentifier values that you see.
It can be as simple as:
SqlParser parser = SqlParser.create(yourQuery);
SqlSelect selectNode = (SqlSelect) parser.parseQuery();
SqlNodeList list = selectNode.getList();
for (int i = 0; i < list.size(); i++) {
System.out.println("Column " + (i + 1) + ": " + list.get(i).toString());
}
I'm working in an application that saves process data (processName, processID, sessionStart, sessionEnd, computerName, currentUser) into the database every time a specific device is being in use by a process.
My problem is that the data process is being added over and over again (every 4 seconds as per specified in the runnable task), so I end up with many similar rows, I would like to find a solution for the process just being added once, and then one more time adding a sessionEnd datetime when the device stop being used. My code is as follows:
if(found){
while(found == true){
System.out.println("\nALERT! Device in use!\nThe process currently using the device is: \n" + strLineProcess+"\n");
final String regex = "^(\\S+) pid: (\\d+) ([^\\\\s]+)\\\\(.+)";
final String processDetails = strLineProcess;
String processName = null;
String processID = null;
String computerName = null;
String currentUser = null;
final Pattern pattern = Pattern.compile(regex);
final Matcher matcher = pattern.matcher(processDetails);
if (matcher.find()) {
processName = matcher.group(1);
processID = matcher.group(2);
computerName = matcher.group(3);
currentUser = matcher.group(4);
}
//Array containing all the process data
String[] processData = new String[6];
processData[0]=""+processName;
processData[1]=""+processID;
processData[2]=getDateTime();
processData[3]="";
processData[4]=""+computerName;
processData[5]=""+currentUser;
String[] processRepeated = new String[1];
insert.insertRow(processData);
break;
}
}
I though about do a select from the application, check if the processID is the same than the one I'm trying to insert, and if so, don't do anything, but I found this a bit clumsy as all this needs to happen every 4 seconds. Just can't get my head around it!
Just to clarify, strLineProcess contain a string similar to this: Skype.exe pid: 3068 WATCHOUT\tofetopo , which I divide with regex to add each value to its corresponding variable.
It is not clear from the information that you have provided, but could you solve your problem simply by adding a unique index on all columns except the datetime column?
The next easiest thing to try would be searching the database for an already existing row. There is nothing wrong with it happening every 4 seconds, or every 4 milliseconds, and it is advisable to start with the easiest possible solution, and only provide a more exotic fix if the easy solution really causes problems.
The exotic fix would be to maintain a list in memory, caching all the rows that you have seen in the last X seconds, and to search through that list for duplicates before inserting a new row. Every time you iterate through the list you drop any entries that are too old and not of interest anymore.
First of all I will explain my use case:
I will get a String Array of names from user(Can of size 2,5,1)
e.g Suppose user input is like this:
String[] names={"Micheal", "Joe","Jim"}
Now after taking input from user, I have to hit SQL table called "USERS" and check whether all of these names are present in USERS table or not. If any single name is not present then return false. If all names are present in USERS table then return true.
My Idea:
My idea is to hit USERS table. Get all names of USERS table in a String array (named as all_names) and then compare my input string(i.e names) with this all_names String. So if names is subset of all_names then return true else return false.
Problem:
But I think this is not an efficient solution. When this table will expand then I will have thousands of records so this technique will be very exhaustive. Any other better and efficient solution for this please.
Updated Solution:
Suppose names in USERS table are unique.
Thanks for your replies. Now I have adopted this approach after getting help from your answers. I want to know that this solution is a better approach or not:
String[] names={"Micheal","Jim","Joe"};
String list2string = StringUtils.join(names, ", ");
//connection was established previosuly
stmt = conn.createStatement();
System.out.println(list2string);
rs = stmt.executeQuery("SELECT COUNT(*) AS rowcount FROM USERS WHERE name IN (" +
list2string +
")");
rs.next();
int count = rs.getInt("rowcount");
rs.close();
if(names.length==count){
System.out.println("All names are in users table");
}else{
System.out.println("All names are not present in users table");
}
Want your comments on this updated solution please.
Regards
You are right, this is not really efficient.
It is the database job to do such things.
You can either make a select statement for each name, eg.
SELECT name FROM users WHERE name = 'Micheal'
or
SELECT name FROM users WHERE name IN ('Micheal', 'Joe', 'Jim')
and check the returned rows.
It might be quiet different depending on which framework you use to query the database.
you can form a string out of string array using loop
for example if you have string array like this:
String[] names={"Micheal", "Joe","Jim"}
get a string lets say s -> "Micheal", "Joe","Jim"
now query like this:
String sql = SELECT name FROM users WHERE name IN (" + s + ")". (you can check the format).
get the output collection and compare with the given collection.
One way to do it, could be
SELECT
COUNT(DISTINCT name)
FROM
users
WHERE
name IN ('Micheal', 'Joe', 'Jim')
Then check if the count is equal to your parameter count, in our case, we should get 3.
I will get a String Array of names from user(Can of size 2,5,1)
You get the input from user, you hit the database with query:
SELECT (WHATEVER_YOU_NEED) FROM SCHEMA_NAME.TABLE_NAME WHERE COLUMN IN
(USER_PROVIDED_INPUT);
You store this result in List.
Get all names of USERS table in a String array (named as all_names)
and then compare my input string(i.e names) with this all_names
String. So if names is subset of all_names then return true else
return false.
Yes, you are right, so you will use
Use Collection.containsAll():
boolean isSubset = listA.containsAll(listB);
And, if your database has unique names (which I guess can be duplicate), you can simply get the count from SQL Query and match it with the user input.
I hope this will help.
SELECT IF(
( SELECT COUNT(DISTINCT name) FROM users WHERE name IN ({toSearch}) ) = {Count},
, 1 , 0
) as Result
replace {toSearch} with e.g. 'Micheal', 'Joe', 'Jim'
{count} is the number of searche, in this example 3. so if all exist the column "Result" has the value 1 else 0
How can I fix this OutOfBoundsException?
Here is the code I am using:
ResultSet rsTagCheck = stmt.executeQuery(
"SELECT PARKING.XKRPRMT.XKRPRMT_PIDM, PARKING.XKRPRMT.XKRPRMT_STATUS, PARKING.XKRPRMT.XKRPRMT_EXPIRE_YR, PARKING.XKRPRMT.XKRPRMT_TAG FROM PARKING.XKRPRMT WHERE XKRPRMT_PIDM ='" + BannerID + "'");
while (rsTagCheck.next()){
String TagNum = rsTagCheck.getString("XKRPRMT_TAG");
ArrayList<String> myTag = new ArrayList<String>();
for (int i = 0; i < TagNum.length(); i++){
myTag.add(TagNum);
myTag.get(i + i);
I kinda know why I am getting the error, but I am not sure how to remedy the problem.
The problem is the i+i part in myTag.get(i+i). It'll work for i=0, but as soon as i=1, you'll get an exception thrown, since you've added two elements to myTag, but are accessing the third element (myTag.get(2)).
What is it that you expect myTag.get(i + i) to do?
The first time through the loop, "i" is zero and you add one element. There won't be an element 1, so the call will throw an exception. Now that I actually see what you wrote, it'll fail on the second iteration, not the first, as poor #Giu noted in his now-deleted answer. Still, it's weird and I don't know what you're trying to accomplish by calling .get() and not even looking at the return value.
You really will have to explain what it is you're trying to do, because that doesn't really make any sense as written. Did the exception in the question title really come from that code, or did you edit part of it out when posting?
edit — whoops totally saw "i+i" as "i+1". Still makes no sense to me however.
You are using the for loop by iterating on the String TagNum. You should only need to say: myTag.add(TagNum).
Imagine that the String TagNum has 4 characters. You add the String to the list 4 times, but when you reach i = 3, you are trying to retrieve the element at position 3 + 1, but the list has elements from 0 to 3.
Also, try replacing the BannerID with a ? and set the parameter to the statement accordingly.
This myTag.get(i + i); is causing the exception.
First time in the loop i is 0, you add an item into the ArrayList and then call get(0+0) which is fine.
In the next iteration, you add another element(total of 2 element in the list now) and call get(1+1), this causes exception as you have only 2 elements and valid index are 0 and 1.
Even without the problem with the get, your program as written will read through the results of the query, and then for each CHARACTER in tagNum, it will add an instance of tagNum to your array. So if tagNum is, say, "ABC", the array will end up containing "ABC" three times. If tagNum is "ABCD", it will contain "ABCD" four times. This doesn't make a lot of sense.
I think what you want is to just add tagNum to an array, defining the array OUTSIDE of the ResultSet.next loop. Something like this maybe:
ArrayList<String> myTag = new ArrayList<String>();
ResultSet rsTagCheck = stmt.executeQuery(
"SELECT PARKING.XKRPRMT.XKRPRMT_PIDM, PARKING.XKRPRMT.XKRPRMT_STATUS, PARKING.XKRPRMT.XKRPRMT_EXPIRE_YR, PARKING.XKRPRMT.XKRPRMT_TAG FROM PARKING.XKRPRMT WHERE XKRPRMT_PIDM ='" + BannerID + "'");
while (rsTagCheck.next()){
String TagNum = rsTagCheck.getString("XKRPRMT_TAG");
myTag.add(TagNum);
}
(Of course this doesn't use any of the other data in your query and I don't know what all else you're up to, but I believe that's what you're trying to do for this part.)
Update
Suppose you have ten records in your database table. After the above loop is complete, the array should be populated.
Try something like this:
ArrayList<String> myTag = new ArrayList<String>();
ResultSet rsTagCheck = stmt.executeQuery(
"SELECT PARKING.XKRPRMT.XKRPRMT_PIDM, PARKING.XKRPRMT.XKRPRMT_STATUS, PARKING.XKRPRMT.XKRPRMT_EXPIRE_YR, PARKING.XKRPRMT.XKRPRMT_TAG FROM PARKING.XKRPRMT WHERE XKRPRMT_PIDM ='" + BannerID + "'");
while (rsTagCheck.next()){
String TagNum = rsTagCheck.getString("XKRPRMT_TAG");
myTag.add(TagNum);
}
for (String tag : myTag)
{
System.out.println(tag);
}
That should give you the list of all the tags. Note you have to examine the List AFTER the while(ResultSet) loop ends. Inside the loop you will only have the elements read so far.
If you're still getting only one value, make sure that you have more than one record coming back from the result set. Like, run the query outside of a Java program and see how many records you get.
List<WebElement> div1=driver.findElements(By.xpath(".//*[#class='art_title']"));
for(int i=0;i<=div1.size();i++)
{
System.out.println(div1.get(i).getText());
Thread.sleep(1000);
}
Instead of the above format I changed it into this format :
List<WebElement> div1=driver.findElements(By.xpath(".//*[#class='art_title']"));
String[] abc = new String[div1.size()];
int i= 0;
for (WebElement e : div1)
{
abc[i] = e.getText();
i++;
System.out.println(e.getText());
}