My mysql-query in Java always stops (i.e. freezes and does not continue) at a certain position, which namely is 543,858; even though the table contains approx. 2,000,000 entries. I've checked this by logging the current result-fetching.
It is reproducible and happens every time at the very same position.
"SELECT abc from bcd WHERE DATEDIFF(CURDATE(), timestamp) <= '"+days+"'");
Addition: It definitely is a Java error, I've just tried out this statement in Navicat (50s running time).
The query seems to freeze after the log tells me that it's now adding the result of position 543,858.
try {
...
ResultSet res = new ResultSet();
PreparedStatement stmt = new PreparedStatement(); // prepare statmenet etc.
stmt.setFetchSize(Integer.MIN_VALUE);
res = stmt.executeQuery();
...
System.out.println(res.getStatement());
...
while (res.next())
treeSet.add(res.getString("userid"));
} catch (Exception e) {
e.printStackTrace();
}
Edit: We were able to figure out the problem. This method is fine and the returned result (500,000 instead of 2,000,000) is right as well (looked up in the wrong db to verify the amount); the problem was, that the next method-call that used the result of the one posted above takes literally forever, but had no logging-implemented. So I've been fooled by missing console-logs.
Thanks anyways!
I think you might be running out of memory after processing half a million records. Try assigning more memory using command line options -Xmx etc. See here for more info about command line options.
In mysql to use streaming ResultSets you have to specify more parameters, not only fetchSize.
Try:
stmt = conn.createStatement('select ...', java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY);
stmt.setFetchSize(Integer.MIN_VALUE);
and see if that works.
It's documented in the ResultSet section.
Strange that it doesn't throw exception, but this is the only suspect I have. Maybe it starts garbage collection/flushes memory to disk and it takes so much time it doesn't get to throw it.
I would try to add to your query " LIMIT 543857" and then " LIMIT 543857" and see what happens.
If the above does not help, use the limit directive combined with order by.
I suspect that there is invalid entry in your table and the way to find it is binary search.
Related
So I'm trying to understand how to use the RowSet API, specifically CachedRowSet, and I feel like I've been bashing my head against a wall for the last hour or so and could use some help.
I've got some very simple tables set up in a MySQL database that I'm using to test this. I should also add that everything I'm attempting to do with RowSet I've been able to do successfully with ResultSet, which leads me to believe that the issue is with my usage of the ResultSet API, rather than the operation I'm attempting to do itself.
Anyway, I'm trying to insert a new row using ResultSet. I'll paste my code here, then add some notes about it below:
CachedRowSet rowSet = null;
try {
RowSetFactory rsFactory = RowSetProvider.newFactory();
rowSet = rsFactory.createCachedRowSet();
rowSet.setUrl("jdbc:mysql://localhost:3306/van1");
rowSet.setUsername("####");
rowSet.setPassword("####");
rowSet.setKeyColumns(new int[]{1});
} catch (SQLException e) {
e.printStackTrace();
}
String query = "select * from phone";
try {
rowSet.setCommand(query);
rowSet.execute();
printTable(rowSet);
rowSet.moveToInsertRow();
rowSet.setInt(1, 4);
rowSet.setString(2, "Mobile");
rowSet.setString(3, "1");
rowSet.setString(4, "732");
rowSet.setString(5, "555");
rowSet.setString(6, "1234");
rowSet.setString(7, "");
rowSet.insertRow();
rowSet.moveToCurrentRow();
rowSet.acceptChanges();
printTable(rowSet);
} catch (SQLException e) {
e.printStackTrace();
}
So, as you can see, I'm trying to update a table of phone numbers with a new phone number. Here are the details:
1) All the phone number fields are datatype char, so that leading zeroes are not lost.
2) I'm using the default CachedRowSet implementation provided by the JDBC API, as opposed to anything specific from the MySQL driver. Not sure if that matters or not, but I'm putting it here just in case. Also, I didn't see an option to import CachedRowSet from the driver library anyway.
3) I'm setting a value for every column in the table, because the RowSet API doesn't allow for rows to be inserted without a value for every column.
4) I've tried the operation using both the setter methods and the update methods. Same result either way.
5) As far as I can tell, I'm on the insert row when executing the insertRow() method. I also return to the current row before invoking acceptChanges(), but since my code never gets that far I can't really comment on that part.
6) The exception is a SQLException (no chained exception within it) thrown on the invocation of the insertRow() method. Here is the stack trace:
java.sql.SQLException: Failed on insert row
at com.sun.rowset.CachedRowSetImpl.insertRow(Unknown Source)
at firsttry.RowSetPractice.rowSetTest(RowSetPractice.java:87)
at firsttry.RowSetPractice.main(RowSetPractice.java:20)
So, I'm out of ideas. Any help would be appreciated. I've searched every thread on this site I could find, all I see is stuff about it failing on the acceptChanges() method rather than insertRow().
I have problem with java and MySQL. My code:
Connection connection;
// ...
for (String query : updateAndInsertQuery) {
Statement stm = connection.createStatement();
stm.execute(query);
stm.close();
}
Statement stm2 = connection.createStatement();
System.out.println("Before query");
System.out.flush();
ResultSet Result = stm2.executeQuery(selectQuery);
System.out.println("After query");
System.out.flush();
int vfrom, vto;
while (Result.next()) {
// ...
}
When I run program i see in MySQL queries and run
show processlist;
selectQuery is visible on list wth status Sending data or Writing to net. On console print: Before query. Next
show processlist;
returns empty list, but application don't print After query. Do you have similar problem?
-- edit
I resolve my problem.
I think:
wen MySQL returns data and query isn't visible on processlist in MySQL
I should immediately get on console message: After query
but console was empty, java process works (cpu usage was 90-100%) so I think it was my mistake, but after 1h application throws Exception
Increase memory limit resolve my problem.
So I have next question why application throw exception after hour? Garbage collection try dealocate unised objects?
executing queries manually usually leads into many different problems - all of which are platform-specific and DB-specific. I think your best answer will be : "switch to ORM".
This framework has proven to be exceptionally good, wrappig all your SQL-data into Entities and transactions (if required) will resolve most of your problems at the same time - you will only need to annotate your entities and relationships correctly. Database-queries can be executed via JPA-"criteria"s which are platform-independent AND allow you to avoid a lot of problems as well as making your code READABLE.
Tutorial : http://www.vogella.com/tutorials/JavaPersistenceAPI/article.html
SO-question : https://stackoverflow.com/questions/743065/what-is-the-fastest-way-to-learn-jpa
With JPA, you wont need to care about statements or queries anymore (well, at least most of the time) and your mentioned problem will disappear - PLUS : it only takes 30-60min to implement.
Additional tip : use Maven & Eclipselink (JPA2 implementation) - thats a very powerful, portable combination
I have checked around, and I have checked up the issue all over Google, tried all kind of solutions, but I haven't found any solution.
The issue is that the DELETE-statement isn't really working.
java.sql.Connection conn = new Connection().getConnection();
try {
conn = new Connection().getConnection();
PreparedStatement statement = (PreparedStatement) conn.prepareStatement("DELETE FROM _users WHERE _id = ?");
statement.setInt(1, 4);
statement.executeUpdate();
} catch (Exception e) {
System.out.println(e);
} finally {
try {
conn.close();
} catch (Exception e) {
System.out.println(e);
}
}
This is the code I am using, and for all I have understood statement.executeUpdate() is the method to use. I have also tried statement.execute() and all kinds of tries.
There is no exception, so no known error. The new Connection().getConnection() gives away an valid connection ( working with SELECT,INSERT,UPDATE ) and the user has permissions for SELECT,INSERT,UPDATE and DELETE.
I am currently using technology such as:
Java 6 (1.6)
mysql-connector-java-5.1.28.jar
and I also had the same experience with
mysql-connector-java-5.1.19.jar
So I would highly appreciate if anyone had the same problem as I seem to have, what it might be because of and/or how to fix it.
Thank you everyone at Stackoverflow. :) Hope this been relevant to anyone else.
To say that something isn't really working does not give us much to go on.
But I suggest that you read up on Transactions. See http://docs.oracle.com/javase/tutorial/jdbc/basics/transactions.html
Unless autoCommit is turned on, you will need to commit your transaction.
Also in your example, you use the ID with the value of 4 - does this really exist?
You always use the same id to delete from. So whether id 4 exists or not the statement will always execute.
Does the id 4 exist? You can issue this command over and over in mysql: DELETE FROM _users WHERE _id = 4;
So long as the table _users exists, you won't get any error. It'll just keep running. Verify that id 4 exists.
The problem has been resolved! So this answer is for future needs
My connection is set automaticely to conn.setAutoCommit(false) and this works for INSERT, UPDATE and SELECT, and for the DELETE-statement to work is to set connection to conn.setAutoCommit(true), and then add an conn.commit() to make the statement to go through.
Want to thank user2310289 for s/he's answer which helped alot.
having major issues with my query processing time :(
i think it is because the query is getting recompiled evrytime. but i dont see any way around it.
the following is the query/snippet of code:
private void readPerformance(String startTime, String endTime,
String performanceTable, String interfaceInput) throws SQLException, IOException {
String interfaceId, iDescp, iStatus = null;
String dtime, ingress, egress, newLine, append, routerId= null;
StringTokenizer st = null;
stmtD = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,
java.sql.ResultSet.CONCUR_READ_ONLY);
stmtD.setFetchSize(Integer.MIN_VALUE);
BufferedReader interfaceRead = new BufferedReader(new FileReader(interfaceInput));
BufferedWriter pWrite = new BufferedWriter(new FileWriter("performanceInput.txt"));
while((newLine = interfaceRead.readLine())!= null){
st = new StringTokenizer(newLine,",");
while(st.hasMoreTokens()){
append = st.nextToken()+CSV+st.nextToken()+st.nextToken()+CSV+st.nextToken();
System.out.println(append +" ");
iStatus = st.nextToken().trim();
interfaceId = st.nextToken().trim();
append = append + CSV+iStatus+CSV+interfaceId;
System.out.println(append +" ");
pquery = " Select d.dtime,d.ifInOctets, d.ifOutOctets from "+performanceTable+"_1_60" +" AS d Where d.id = " +interfaceId
+ " AND dtime BETWEEN " +startTime+ " AND "+ endTime;
rsD = stmtD.executeQuery(pquery);
/* interface query*/
while(rsD.next()){
dtime = rsD.getString(1);
ingress= rsD.getString(2);
egress = rsD.getString(3);
pWrite.write(append + CSV + dtime+CSV+ingress+CSV+egress+NL);
}//end while
}//end while
}// end while
pWrite.close();
interfaceRead.close();
rsD.close() ;
stmtD.close();
}
my interfaceId value keeps changing. so i have put the query inside the loop resulting in recompilation of query multiple times.
is there any betetr way? can i sue stored procedure in java? if so how? do not have much knowledge of it.
current processing time is almost 60 mins (:(()!!! Text file getting generated is over 300 MB
Please help!!!
Thank you.
You can use a PreparedStatement and paramters, which may avoid recompiling the query. Since performanceTable is constant, this can be put into the prepared query. The remaining variables, used in the WHERE condition, are set as parameters.
Outside the loop, create a prepared statement, rather than a regular statement:
PreparedStatement stmtD = conn.prepareStatement(
"Select d.dtime,d.ifInOctets, d.ifOutOctets from "+performanceTable+"_1_60 AS d"+
" Where d.id = ? AND dtime BETWEEN ? AND ?");
Then later, in your loop, set the parameters:
stmtD.setInteger(1, interfaceID);
stmtD.setInteger(2, startTime);
stmtD.setInteger(3, endTime);
ResultSet rsD = stmtD.executeQuery(); // note no SQL passed in here
It may be a good idea to also check the query plan from MySQL with EXPLAIN to see if that is part of the bottleneck also. Also, there is quite a bit of diagnostic string concatenation going on in the function. Once the query is working, removing that may also improve performance.
Finally, note that even if the query is fast, network latency may slow things down. JDBC provides batch execution of multiple queries to help reduce overall latency per statement. See addBatch/executeBatch on Connection.
More information required but I can offer some general questions/suggestions. It may have nothing to do with the compilation of the query plan (that would be unusual)
Are the id and dtime columns indexed?
How many times does a query get executed in the 60mins?
How much time does each query take?
If the time per query is large then the problem is the query execution itself, not the compilation. Check the indexes as described above.
If there are many many many queries then it might be the sheer volume of queries that is causing the problem. Using PreparedStatement (see mdma's answer) may help. Or you can try and batch the interfaceIDs you want by using an "in" statement and running a query for every 100 interfaceIDs rather than one for each.
EDIT: As a matter of good practice you should ALWAYS use PreparedStatement as it will correctly handle datatypes such as dates so you don't have to worry about formatting them into correct SQL syntax. Also prevents SQL injection.
From the looks of things you are kicking off multiple select queries (even 100's based on your file size)
Instead of doing that, from your input file create a comma delimited list of all the interfaceId values and then make 1 SQL call using the "IN" keyword. You know the performanceTable, startTime and endTime arent changing so the query would look something like this
SELECT d.dtime,d.ifInOctets, d.ifOutOctets
FROM MyTable_1_60 as d
WHERE dtime BETWEEN '08/14/2010' AND '08/15/2010'
AND d.id IN ( 10, 18, 25, 13, 75 )
Then you are free to open your file, dump the result set in one swoop.
In my email management system, I am retrieving data from MySQL database into Java netbeans. But it is only showing the data for one person. It should move on to next row on each rs.next(), but it is not seemingly. Following code is written in the "Next" Button, which moves on to the next selection in a JList.
try {
DefaultListModel model = new DefaultListModel();
Class.forName("com.mysql.jdbc.Driver");
Connection con = DriverManager.getConnection( "jdbc:mysql://localhost:3306/email management system","root", "ok" );
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("select * from inbox,curlogin where inbox.rmail like curlogin.mail;");
String fname="";
list.setSelectedIndex((list.getSelectedIndex()+1));
rs.next();
fname = rs.getString("name");
String sender = rs.getString("First");
String reciever = rs.getString("recipent");
String sub = rs.getString("subject");
String msg = rs.getString("message");
String mail = rs.getString("Email");
Date d = rs.getDate("Date");
Timestamp ls = rs.getTimestamp("Seen");
Time t = rs.getTime("Time");
subject.setText(sub);
message.setText(msg);
date.setText(""+d);
time.setText(""+t);
lseen.setText(""+ls);
email.setText(mail);
// TODO add your handling code here:
} catch (ClassNotFoundException | SQLException ex) {
Logger.getLogger(inbox.class.getName()).log(Level.SEVERE, null, ex);
}
user2642282's answer is technically correct but it is lacking explanation and user2642282 misunderstood what your attempting to do. Your half way to the solution with your code but it requires some changes. Lets start with this:
rs.next()
What your doing here is accessing the result set and normally you would write your code like this to loop through all the returned results:
ResultSet rs = stmt.executeQuery("your SQL statement here");
while (rs.next()) {
fname = rs.getString("name");
//...all your original code here
email.setText(mail);
}
Notice how the code is basically exactly the same as your original code except we added the while loop. Normally this would allow you to write out, display, or store each record (email in this case) returned by the query however you want/ need to do something different. So I'll explain what options I think you have next but first here is a link to the Java Docs that will teach everything you need to know about the above code.
Option 1 - Traditional Way
The way most email desktop app's handle this is simply to load all the emails and store them on the file system. This would mean that you need to change your code to load all emails at certain times. Maybe at startup and then every 60 seconds after that check for new messages (usually you should limit checking to no lower than 5 minutes). Then when a user opens a message you load it from its file. When they press next or previous you simply load in the next or previous email from the file system. So your email app is more of a file reader that parses email files and loads their content into your window. You will need to devise a filing system as well. For example naming and saving the emails as their timestamp making it easy to find the next and previous emails. You will also have to deal with deleted emails.
Option 2 - Easy but Bad Practice
Without having to change your code to much you could track which row from the database you read last and run the SQL query every time the user presses the next or previous buttons. You would use the same code your originally posted but add the following:
Check for when next() returns false meaning there is no next or previous email
Add in a variable that keeps track of the last row you read from in the database
Change your SQL query to only return the next or previous row
Option 3 - Hybrid
You could attempt to change your code to the one I showed you and do a hybrid of option 2. What you would do is track the next and previous 10 rows from the database for example. So lets say you last read from row 50. You would run the code I gave you and save all the emails from row 40 to row 60. You have two options for saving the emails:
File system which creates a bit more work but saves memory.
As objects that could take a lot of memory but is easy and fast.
EDIT - Answer to question from comments
Here is how I imagine your code working using option 2 or 3. Notice that ... means I left out code you already have but would just get in the way of this example:
int lastRowRead = 0; // starts as 0 when the program starts
//...all your original code here
ResultSet rs = stmt.executeQuery("...LIMIT "+ lastRowRead +",20;");
if(rs.next()!=false) {
fname = rs.getString("name");
//...all your original code here
email.setText(mail);
}
This code is for option 3. The int lastRowRead should be a global variable that you can keep track of and change each time you run a query to get more messages. You will need to figure out how to catch errors, for example trying to query a row number that doesn't exist. I won't help you code this because it is a good learning opportunity. NOTE: In case you are not familiar with SQL LIMIT here is another example:
....LIMIT 40, 20;
What this means is go to row 40 in the table and read the next 20 rows. So it will return emails from 40 to 60. So if you want to go the super easy but not at all efficient route your SQL query could have:
...LIMIT 40, 1;
This will only return the row number requested. So all you have to do is track which row you last read from and add or subtract 1 from it and run the query again to get the next or previous email.
You are missing a loop that iterates over the resultset. The call
rs.next()
gives you the first row of your SQL result. Try to loop over the results using until rs.next() returns false
two points:
one: if JList current selected value has not changed, when you click next button , you should change your sql , then you can see one's email one by one. at the end of your sql, you should add
select * from inbox,curlogin where inbox.rmail like curlogin.mail limit #{offset} 1
to fix your sql . the offset is a veriable from 0,1,2,3.... it represents one's email from 1 to N
two: you haven't show some event code about JList after selected value was changed. if it was changed , your should get the selected item, and pass it to your sql to filter person's email. so your sql should accept person's name .