Aborting Ajax and JDBC Request - java

My question consists of two parts on interrupting requests:
I'm using a dynamic Java Web Application (Tomcat 8.0.15, Java EE 7 Web) with a SQL Server 2008 and JavaScript/jQuery (3.1.1).
Apparently some variants of that problem have been posted on Stackoverflow already a few years ago.
I'm having an input/textarea-field with an onkeyup-event that sends a request to a Servlet and gets a response with some kind of google-like suggestion-drop-down.
I had in my mind whenever I fire another jQuery-Ajax-Request the old on is aborted. Yesterday I realized that's not the case (any more?) and I get the dropdown several times instead of just once.
If you happen to know whether or not jQuery changed something in a new version, please let me know how and if I am able to revert that.
The second part is on the server-side: I'm firing a quite heavy query to the database and would like that to be interrupted as the Ajax-Request is aborted. That seems to be quite a problem.
There's a discussion here already about the same issue in PHP/MySQL (Stop mysql query when press stop button).
Back in 2009 there was "no simple way": How to cancel ajax request that has run (on server side) - I wonder whether or not something changed and meanwhile I can do something about it.
There are several approaches on how the Ajax-query can be interrupted. I chose Abort Ajax requests using jQuery and my code looks like that:
Main-File:
<html>
<head>
<script>
var xhr; //global variable for the Ajax-Object
</script>
<meta ... />
The field is also pretty straightforward:
<textarea id="field_106" onkeyup="sendInfo(/*Some parameters*/)"></textarea>
The code:
function sendInfo(/*All the Params*/) {
//That's new
if(xhr!=null){
xhr.abort();
}
//Before I haven't had that xhr-val too
xhr=$.ajax({
url: jsp,
data: {
input: document.getElementById(input).value,
/*
* Lots
* of
* other
* parameters
*/
},
type: 'GET',
success: function (result) {
document.getElementById(output).innerHTML = result;
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
document.getElementById(output).innerHTML = "err--" + XMLHttpRequest.status + " -- " + XMLHttpRequest.statusText;
}
});
}
}
Before on the Firebug-Console I had the the issue every single keyup-event is firing its own Request which results - depending on quite a few requests, depending on how much is sent:
After the changes it looks like it used to:
Apparently I'm not so wrong, Google seems to use the very same method of interrupting Requests (just their servers are faster than I can type):
The Servlet puts together two Queries. The first four entries are the "Top Results" depending on usage, the second fetches the rest:
private final String sql = "SELECT TOP 5 "
// ALL THE VALS
" FROM TopResults "
+ " WHERE Description like ? order by CNT desc";
private final String sql2 = "SELECT "
// SAME VALS
+ "FROM Articles art "
+ "LEFT OUTER JOIN "
+ "TopResults ON ArtikelNr=Artikelnumber "
+ "WHERE art.Artikelbezeichnung1 like ? "
+ ""
+ "EXCEPT "
+ ""
+ sql;
public String getInfo() {
Connection c = null;
PreparedStatement ps = null;
ResultSet rs = null;
StringBuilder ret = new StringBuilder();
try {
c = BaseConnection.getConnection();
ret.append(getTableStart());
ps = c.prepareStatement(sql);
ps.setString(1, "%" + query + "%");
rs = ps.executeQuery();
ret.append(appendX("", rs));
BaseConnection.disconnect(rs, ps);
ps = c.prepareStatement(sql2);
ps.setString(1, "%" + query + "%");
ps.setString(2, "%" + query + "%");
rs = ps.executeQuery();
ret.append(appendX("topTR", rs));
ret.append(getTableEnd());
return ret.toString();
} catch (SQLException | NamingException ex) {
Logger.getLogger(GetInfo.class.getName()).log(Level.SEVERE, null, ex);
return ex.getLocalizedMessage() + "<br/>" + ((ex instanceof SQLException) ? ((SQLException) ex).getSQLState() : "Naming Exception");
} finally {
BaseConnection.disconnect(rs, ps, c);
}
}
Here I was wondering about my style of sending both Statements, but it's just a style: Executing two Java PreparedStatements with one connection - style choice
I wonder if there are better methods than mine to shoot too many queries into the database. Thank you

Related

Trouble with ResultSet using executeUpdate

I am new to programming and have run into a problem while using executeUpdate with the resultSet next() method.
It iterates once only through the result set then the execute update closes the result set. I get error: ResultSet not open. Operation "next" not permitted. Verify that autocommit is off.
I have added the con.setAutoCommit(false) statement but problem still persists.
I need to run the update multiple times with different variable values.
Here is the code I have:
try {
String eidQuery = "SELECT EID FROM EMPLOYEE_DATA WHERE ACTIVE = TRUE ORDER BY EID";
int nextEID;
Statement st = con.createStatement();
con.setAutoCommit(false);
rs = st.executeQuery(eidQuery);
while (rs.next()){
nextEID = rs.getInt(1);
String getDailyTotals = "SELECT DATE, SUM(TOTAL), MAX(OUT_1) FROM PUNCHES WHERE EID = " + nextEID + " AND DATE >= '" + fd + "' "
+ "AND DATE <= '" + td + "' GROUP BY DATE";
ResultSet rs2 = st.executeQuery(getDailyTotals);
while (rs2.next()){
double dailyTotal = rs2.getDouble(2);
if (dailyTotal > 8){
double dailyOT = dailyTotal-8;
String dailyDate = rs2.getDate(1).toString();
Timestamp maxTime = rs2.getTimestamp(3);
String updateOT = "UPDATE PUNCHES SET OT = " + dailyOT + " WHERE EID = " + nextEID + " AND DATE = '" + dailyDate + "' AND OUT_1 = '" + maxTime + "'";
st.executeUpdate(updateOT);
}
}
}
rs = st.executeQuery("SELECT PUNCHES.EID, EMPLOYEE_DATA.FIRST_NAME, EMPLOYEE_DATA.LAST_NAME, SUM(PUNCHES.OT) FROM PUNCHES "
+ "JOIN EMPLOYEE_DATA ON PUNCHES.EID = EMPLOYEE_DATA.EID WHERE PUNCHES.DATE >= '" + fd + "' AND PUNCHES.DATE <= '" + td + "' GROUP BY EMPLOYEE_DATA.FIRST_NAME, EMPLOYEE_DATA.LAST_NAME, PUNCHES.EID");
Reports.setModel(DbUtils.resultSetToTableModel(rs));
} catch (SQLException ex) {
Logger.getLogger(GUI.class.getName()).log(Level.SEVERE, null, ex);
JOptionPane.showMessageDialog(null, ex);
}
You're new to programming and (obviously) Java. Here are a few recommendations that I can offer you:
Do yourself a favor and learn about PreparedStatement. You should not be creating SQL by concatenating Strings.
You are committing the classic newbie sin of mingling database and UI Swing code into a single, hard to debug lump. Better to decompose your app into layers. Start with a data access interface that encapsulates all the database code. Get that tested and give your UI an instance to work with.
Do not interleave an update query inside the loop over a ResultSet. Better to separate the two completely.
Read about MVC. You'll want your Swing View to be separate from the app Controller. Let the Controller interact with the data access interface, get the results, and give the results to the View for display. Keep them decoupled and separate.
Learn JUnit. It'll help you with testing.
From the java.sql.ResultSet javadoc:
A ResultSet object is automatically closed when the Statement object
that generated it is closed, re-executed, or used to retrieve the next
result from a sequence of multiple results.
After you execute the update, the prior ResultSet is closed. You need to rework your code to account for that.
https://docs.oracle.com/javase/7/docs/api/java/sql/ResultSet.html
The easiest way to rework might be to use two Statements, one for the query and one for the update, but as noted in duffymo's answer there's a fair amount more you could do to improve things.
From API's statement documentation "By default, only one ResultSet object per Statement object can be open at the same time. Therefore, if the reading of one ResultSet object is interleaved with the reading of another, each must have been generated by different Statement objects"
You need two different Statements if you want to read two different ResultSet in the same nested loops.

SQLite Crash In Java Application (Online Game)

I am writing an plugin for a online game. It is running on a MS Server 2012. The plugin works by extending #EventMethods provided by the developers API and my Plugin uses a SQLite database for storing player information in relation to the games "chunks" (3D spaces within the environment).
Everything had gone fine on my test server, but I only had myself and one other user testing it.
When I deployed the plugin to the main server and people started joining (around 12 users) the server crashed after about ten minutes with a "A fatal error has been detected by the Java Runtime Environment: EXCEPTION_ACCESS_VIOLATION"
Now it looks to me like this was caused by SQLite crashing.
Here is an example of one of my methods that access the SQLite database.
#EventMethod
public void onPlayerPlaceObject(PlayerPlaceObjectEvent event) throws SQLException {
int blockXChunk = event.getChunkPositionX();
int blockYChunk = event.getChunkPositionY();
int blockZChunk = event.getChunkPositionZ();
String blockChunkID = "" + blockXChunk + blockYChunk + blockZChunk;
try {
//get the playerUID and the blockchunkID
ResultSet rs = database.executeQuery("SELECT * FROM `Areas` WHERE `PlayerUID` = '" + event.getPlayer().getUID() + "' AND `AreaID` = '" + blockChunkID + "'");
if (rs.next() ) {
//player owns chunk do whatever you like!
rs.close();
return;
} rs.close();
//lets see if the blockChunk event is happening somewhere where someone own ths AreaID
rs = database.executeQuery("SELECT * FROM `Areas` WHERE `AreaID` = '" + blockChunkID + "'" );
if (rs.next() ) {
//chunk is owned by someone and its not the one making the event.
String string = (String)rs.getString("PlayerUID");
rs.close();
rs = database.executeQuery("SELECT * FROM `Friends` WHERE `OwnerUID` = '" + string + "' AND `FriendUID` = '" + event.getPlayer().getUID() + "'" );
if (rs.next() ) {
//player is a friend of the owner do whatever you like!
rs.close();
return;
} rs.close();
event.setCancelled(true);
return;
} else { rs.close(); }
} catch ( Exception e ){ System.out.println(); }
}
I suspect that these methods, being called so often on the SQLite database is causing it to crash. Can someone please help me find a safer way to access this data? Or would I be better off using a "proper" database like MySQL and would that have structure in place to stop the EXCEPTION_ACCESS_VIOLATION ?
Edit: I have removed the reused ResultSet objects in the preceding code (naming the objects rs, rs2, rs3, etc.) but the Java Runtime Environment is still crashing with an EXCEPTION_ACCESS_VIOLATION on an apparent random basis (I added println statements throughout the code hoping to track down where the crash was occurring - but it indeed appears random). Sometimes the server will run for 10 minutes and crash, other times it will run for 24 hours or so and then crash. I also enclosed all of the ResultSets within try-with-resources blocks. With the same outcome.
Don't reuse the ResultSet object rs for several queries, create a new instance for each query to avoid problems.

JDBC timeout with ResultSet am I doing it right?

I have a JDBC ResultSet that gives me a TimeOut after only a few thousand rows are processed. I have a few million rows to process, so I'd like to tweak my program to avoid this, just not sure what needs to be tweaked.
Database table is indexed and returns data quickly using selection criteria, so I don't believe it is on the database side. I'm returned 14 columns mixed between address columns and ints. Not a lot of data.
I'm doing a connection.createStatement() and then building the SQL from there. The answer might be I should use a prepared statement.
Statement stmt = null;
ResultSet rs = null;
try {
stmt = conn.createStatement();
String jobNameFilter = (Cli.getJobName() != null) ? " AND [JobName] = '" + Cli.getJobName() + "'" : "";
String sortOrder = (Cli.isAscending()) ? "ASC" : "DESC";
String orderByClause = Cli.isRandom() ? " ORDER BY [Randomizer] " + sortOrder + ",[RecordID] " + sortOrder : " ORDER BY [RecordID] " + sortOrder;
String startingIdFilter = (Cli.getStartingId() != null) ? " AND [RecordId] > " + Cli.getStartingId() : "";
String driverQuery = "SELECT [RecordID], [Column1] AS [TrackingID], [Address]" + ", [Suite] AS [AptSuiteOther], [City], [Building2Key]"
+ ", [ST] AS [State], [ZIPCode]" + ", [BusinessName], [ContactLastName], [Suite]" + ", [Phone], [EmailAddress]"
+ " FROM [Project].[TestSet] WITH (READUNCOMMITTED)"
+ " INNER JOIN [Project].[State] sttable ON sttable.[ST] = UPPER([Project].[TestSet].[ST]) AND [TerritoryFlag] = 0" + " WHERE [BuildingKey] = 0 " + jobNameFilter
+ startingIdFilter + " AND (([FirstResponse] IS NULL AND ([Building2Key] IS NULL OR [Building2Key] = 0)) OR ([Building2Key] > 0 AND [SecondResponse] IS NULL)) " + orderByClause;
rs = stmt.executeQuery(driverQuery);
} catch (SQLException e1) {
logger.error("SQLException", e1);
}
try {
while (rs.next()) {
int recordId = rs.getInt("RecordID");
// Process data
numberProcessed++;
}
} catch (SQLException sqle) {
logger.error("SQLException", sqle);
}
I'm closing all the ResultSet, Connection and Statement in a finally statement at a different level also.
I'm not sure if I need to set the timeout to something higher, setFetchSize to something greater? Trap timeout and create ResultSet again?
Change logic to only pull one row at a time?
You'd have to profile your app to find out for sure, but I'm guessing that the "// Process data" part is the culprit. You're holding the connection open while process all of the rows.
I'd suggest that you read a batch of rows at a time, close the statement, and then process the batch. Then do a select for the next batch, rinse and repeat.
Selecting one row at a time would introduce a lot of overhead, so I wouldn't suggest doing that.
Also, make sure that you're using a connection pool, so that you don't actually have to build a new Connection each time. The pool will keep it open for you, and recycle it if it goes dead / times out.

MySQL before start of exception [duplicate]

This question already has answers here:
ResultSet exception - before start of result set
(6 answers)
Closed 5 years ago.
I get an error stating that I got an exception before start of a result set. I'm trying to get a value (score from the MySQL database) and add one to the Java rank based on the player score. This is to create a scoreboard.
So if the player's score is lower than the current score, it gets posted with rank 1. If it's higher, the program checks the score against the next entry in the MySQL database. I haven't yet implemented a feature to change all the current entries rank's to increment by 1.
Bottom Line: I'm creating a scoreboard using MySQL and Java. The Java program creates a score entry based on input, and then sends it off to the MySQL database.
System.out.println("Your score is: "+score*2+" (A lower score is better.)");
try {
// create a java mysql database connection
String myDriver = "com.mysql.jdbc.Driver";
String myUrl = "jdbc:mysql://4.30.110.246:3306/apesbridge2013";
String dbName = "apesbridge2013";
String tbName = period + "period";
Class.forName(myDriver);
Connection conn = DriverManager.getConnection(myUrl, "user", CENSORED);
next = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);
ResultSet resultSet = next.executeQuery("SELECT * FROM " + tbName);
int cscore = resultSet.getInt("score");
for(int sscore = score; sscore > cscore;){
resultSet.next();
cscore = resultSet.getInt("score");
rank++;
}
stmt = conn.createStatement();
stmt.executeUpdate("insert into " + dbName + "." + tbName + " " + "values(" + rank + ", '" + name + "', " + score + ")");
stmt.close();
conn.close();
}
catch (Exception e)
{
System.err.println("Got an exception! ");
System.err.println(e.getMessage());
}
}
Put resultSet.next(); right below your executeQuery line.
As stated by #hd1, you need to call ResultSet.next() after the call to executeQuery:
while (resultSet.next()) {
...
Also, better to use PreparedStatement instead of java.sql.Statement and use parameter placeholders to protect against SQL Injection attacks:
There's a problem in your for loop; the exit condition should be when there are no more rows to fetch. Your query doesn't guarantee that the exit condition will ever be met, and you may attempt to fetch past the end of the resultset. (And even when your for loop does happen to be entered, and when if the for loop does happen to be exited, the rank value derived by that loop is non-deterministic, it's dependent on the order that rows are returned by the database.
I also don't see any call to resultSet.close() or next.close().
There's so many problems here, it's hard to know where to begin.
But firstly, it would be much more efficient to have the database return the rank to you, with a query:
"SELECT COUNT(1) AS rank FROM " + tbName + " WHERE score < " + score
rather than pulling back all the rows back, and comparing each score. That's just painful, and a whole lot of code that is just noise. That would allow you to focus on the code that DOES need to be there.
Once you get that working, you need to ensure that your statement is not vulnerable to SQL injection, and prepared statements with bind variables is really the way to go there.
And you really do need to ensure that calls are made to the close() methods on the resultset, prepared statements, and the connection. We typically want these in a finally block. Either use nested try/catch blocks, where the variables are immediately initialized, like this:
try {
Connection conn = DriverManager.getConnection(...
try {
stmt = conn.CreateStatement();
String query = "SELECT COUNT(1) AS `rank` FROM " + tbName + " WHERE `score` < " + score ;
try {
ResultSet rs = stmt.executeQuery(query);
while (rs.next()) {
rank = rs.getInt("rank");
}
} finally {
if (rs!=null) { rs.close() };
}
} finally {
if (stmt!=null) { stmt.close() };
}
} finally {
if (conn!=null) { conn.close() };
}
Or one big try/catch block can also be workable:
} finally {
if (resultSet!=null) { resultSet.close() };
if (next!=null) { next.close() };
if (conn!=null) { conn.close() };
)
The point is, the close methods really do need to be called.

Avoiding database locks on SQL Server 2012 when updating using Java

I have a web application which is based on SQL Server 2012, and I use Java to update data in the database. (Windows Server 2008, JSP, Tomcat7, Java7)
The relevant code is as follows:
public static synchronized int execute(String dsName, String packageAndFunction, List fields) {
// prepare insertStr
String executeStr = buildStatement(dsName, packageAndFunction, null, fields);
dbConn = DBConnection.getInstance();
Connection conn = dbConn.getConnection();
CallableStatement stmt = null;
int result = RESULT_FAILED;
try {
stmt = conn.prepareCall(executeStr);
// fill statement parameters (each ?)
fillStatement(stmt, fields);
stmt.execute();
result = stmt.getInt(fields.size());
} catch(SQLException e) {
Log.getInstance().write("Exception on executeGeneral (" + packageAndFunction + ") " + e.toString());
} finally {
try {
stmt.close();
dbConn.returnConnection(conn);
} catch(SQLException e) {
Log.getInstance().write("Exception on executeGeneral (" + packageAndFunction + ") " + e.toString());
}
}
return result;
}
About 90% of the time, the code works great. The rest of the time there is some kind of lock on the table which will disappear by itself in perhaps half an hour or so. The lock prevents even simple SELECT queries on the table from executing (in SQL Server Management Studio). In severe cases it has prevented the entire application from working.
I had an idea to use stmt.executeUpdate() instead of stmt.execute(), but I have tried to research this and I do not see any evidence that using stmt.execute() for updating causes locks.
Can anyone help?
Thanks!
It's difficult to diagnose with that code. The next time that it happens, pull up your activity monitor on the SQL server and see what sql command is holding the lock.

Categories