I have a rather annoying issue. In the piece of code below, I am trying to insert a new row to the "RevisionDispersion" table in my database. However, whenever I call stmt.executeUpdate() the program freezes and there ends up being no transaction to the database. No matter how long I wait; the database just won't be updated. Below is the code of interest:
private static final String INSERT_DISPERSION = "insert into RevisionDispersion("
+ Assignments.ID + ", "
+ Persons.EMAIL + ", "
+ Handins.ID + ")"
+ " values(?, ?, ?)";
public static void disperse(DataSource source, Assignment assignment) throws Exception
{
List<String> handins = assignment.getHandins();
//used to decide who checks which assignment
int maxRNG = Math.max(1, handins.size() / assignment.getPeerCount());
int rng = new Random().nextInt(maxRNG);
PreparedStatement stmt = null;
Connection con = null;
try{
//Get the connection, set it to TRANSACTION_SERIALIZABLE and set autocommit to false
con = source.getConnection();
configureConnection(con);
//Prepare the statement to insert the new dispersion
stmt = con.prepareStatement(INSERT_DISPERSION);
stmt.setString(1, assignment.getID());
//Iterate over all hand-ins and decide from which peer a peer receives feedback
for(int i = 0; i < handins.size(); i++)
{
HandIn handin = new HandIn(source.getConnection(), handins.get(i));
String student = handin.getEmail();
stmt.setString(2, student);
for(int j = 1; j <= assignment.getPeerCount(); j++)
{
HandIn otherHandin = new HandIn(source.getConnection(), handins.get(j * rng));
stmt.setString(3, otherHandin.getId());
stmt.executeUpdate();
}
}
con.commit();
}catch(Exception e){
throw e;
}finally{
closeQuietly(con, stmt);
}
}
//This method is originally in the DBAO class, but I put it here for you folks.
protected static void configureConnection(Connection connection) throws SQLException
{
connection.setAutoCommit(false);
connection.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
}
This problem occurs in no other places in the application. Whenever I run the SQL statement in SQL Server Management Studio, with identical parameters, it does not get stuck and it inserts the new rows just fine. After deleting the rows and trying the same in the application, it gets stuck.
Can anyone point me in the right direction of what is going wrong? I've been trying for 3 hours straight now...
Stuff I already tried
-use stmt.addBatch() rather than executeUpdate() (did not make a difference. It would get stuck at executeBatch())
-Check if all connections are being closed properly; they are.
-Check if other statements/resultsets are still open that use RevisionDispersion table (there are none still open. Even if there were, should not make a difference I think?)
-Completely delete the database and set it back up
I solved the issue...
In a different piece of code I had the following:
private static final String GET_NOT_DISPERSED = "select * from Assignments where "
+ Assignments.CLOSE_DATE + "<=? and "
+ Assignments.PEER_START_DATE + ">=? and "
+ Assignments.ID + " not in(select " + Assignments.ID + " from RevisionDispersion)";
private void makeMailDispersion() throws Exception
{
DateTime currentDate = DateTime.getCurrentDateTime();
PreparedStatement assignmentsStmt = null;
ResultSet assignments = null;
Connection con = null;
try{
con = source.getConnection();
configureConnection(con);
assignmentsStmt = con.prepareStatement(GET_NOT_DISPERSED);
assignmentsStmt.setString(1, currentDate.toString());
assignmentsStmt.setString(2, currentDate.toString());
assignments = assignmentsStmt.executeQuery();
ArrayList<Assignment> requiresDispersion = new ArrayList<>();
assignments.close();
assignmentsStmt.close();
while(assignments.next())
{
Assignment assignment = new Assignment(source.getConnection(), assignments.getString(Assignments.ID));
AssignmentDisperser.disperse(source, assignment);
}
}catch(Exception e){
throw e;
}finally{
closeQuietly(con, assignmentsStmt, assignments);
}
}
In this piece of code, I closed the variables 'assignments' and 'assignmentsStmt'. I thought this would be sufficient to unlock the table after having used the GET_NOT_DISPERSED query. Apparently it was not: the table was still locked.
What I had to do in order to fix it: aside from calling assignments.close() and assignmentsStmt.close() I also had to call con.close(). That completely unlocked the table and allowed the code to run properly.
Related
I have a class called "Database" that is working perfectly well. It queries a database and returns the results as a string. When I call the class from my "Test" class it returns the results string and I can print it with System.out.println().
I'm trying to use this class on a JSP page using the same two lines of code to instantiate the class and get the string output. When I try to output on the JSP page I get nothing. What am I doing wrong? I'm completely stumped.
Class Code:
public class Database {
static String[][] reservations = new String[7][20];
public Database (String theDate) {
String url = "jdbc:mysql://myurl.com:3306/";
String driver = "com.mysql.cj.jdbc.Driver";
String user = "myusername";
String pass = "mypassword";
String db = "class";
String options = "?useSSL=false";
try (Connection conn = DriverManager.getConnection(url + db + options, user, pass);
Statement statement = conn.createStatement()) {
String query = "select reservation.first, reservation.last, startday, numberofdays, guides.first as guidefirst, guides.last as guidelast, locations.location from reservation left join guides on reservation.guide = idguides left join locations on reservation.location = idlocations where StartDay >= " + theDate;
ResultSet rs = statement.executeQuery(query);
int row = 0;
while (rs.next()) {
reservations[0][row] = rs.getString("first");
reservations[1][row] = rs.getString("last");
row++;
}
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
public String getStringRes () {
String returnString = "";
for(int i=0; i<20; i++) {
if (reservations[0][i] != null) {
returnString += i + " " + reservations[0][i];
returnString += " " + reservations[1][i] + "\n";
}
}
return returnString;
} }
JSP Code:
<%# page import="mypackage.Database" %>
<%
Database db = new Database("2015-07-01");
String str = db.getStringRes();
%>
<%= str %>
Your code contains 3 mistakes.
The most problematic
} catch (SQLException e) {
e.printStackTrace();
}
Don't ever do that. Go into your IDE settings and eliminate this template. It should be throw new RuntimeException("Unhandled", e); instead.
Here's what happened: Your SQL statement is erroneous (in two ways, even). This causes an exception. Your code handles this by ignoring it (it's printed, but, your code finishes normally). Hence, the string result remains blank (because it starts out that way and the code that is supposed to give it its real value never ran, due to the exception that you ignored).
Your SQL is broken.
The actual explanation is that date literals need to be in quotes, in SQL. Yours is not.
But that's not how you pass parameters into SQL.
The bigger issue is that passing any value like this into an SQL means your machine will be hacked in a matter of days. That's called 'SQL injection'. You don't want it. The solution is preparedstatements, where you let the JDBC driver and/or the database take care of escaping or otherwise passing string data without the SQL db engine trying to interpret it as SQL (Because, obviously, letting arbitrary users type stuff in that your DB engine then treats as SQL means you're just waiting for someone to construct some SQL such that your db engine ends up executing DROP TABLE reservation CASCADE; EXECUTE 'FORMAT C: /Y'; --.
Minor style nit
Doing business logic in constructors is a bad idea. The query should be done from getStringRes, most likely. That's also a bit of a crazy method name. Not very informative.
Which gets us to...
try (Connection conn = DriverManager.getConnection(url + db + options, user, pass);
PreparedStatement statement = conn.prepareStatement("SELECT reservation.first, .... FROM .... WHERE StartDay >= ?")) {
statement.setString(1, theDate);
try (ResultSet rs = statement.executeQuery()) {
...
}
} catch (SQLException e) {
throw new RuntimeException("unhandled", e);
}
I have a problem with a really slow connection between my Java code and a MySQL remote Database when i use multiple query.
This is my code
ArrayList<Server_Log> ar =Server_Log_Utilities.getBy2Dates(cmb_date.getSelectedItem() + "", cmb_date2.getSelectedItem() + "");
for (int c = 0; c < ar.size(); c++) {
Server_Log sl = ar.get(c);
String username = User_Utilities.getUserName(sl.getUser() + "");
String row[] = {sl.getDate(), sl.getTime(), username, sl.getReff(), sl.getDescription()};
}
but I user this code data will load fast
ArrayList<Server_Log> ar =Server_Log_Utilities.getBy2Dates(cmb_date.getSelectedItem() + "", cmb_date2.getSelectedItem() + "");
for (int c = 0; c < ar.size(); c++) {
Server_Log sl = ar.get(c);
String row[] = {sl.getDate(), sl.getTime(), sl.getReff(), sl.getDescription()};
}
this is User_Utilities.getUserName(sl.getUser() + ""); Method
public static String getUserName(String id) {
String UserName="";
try {
Connection con = new DBCon().getConnection();
ResultSet rst = DBHandle.getData(con, "SELECT username FROM user WHERE id='" + id + "'");
while (rst.next()) {
UserName =rst.getString(1);
}
con.close();
} catch (Exception ex) {
Logger.getLogger(User_Utilities.class.getName()).log(Level.SEVERE, null, ex);
}
return UserName;
}
Server_Log_Utilities.getBy2Dates(cmb_date.getSelectedItem() + "",
cmb_date2.getSelectedItem() + ""); Method
public static ArrayList getBy2Dates(String date1, String date2) {
try {
ar = new ArrayList<>();
Connection con = new DBCon().getConnection();
ResultSet rst = DBHandle.getData(con, "SELECT * FROM server_log WHERE date BETWEEN '" + date1 + "' AND '" + date2 + "' ORDER BY `date`");
while (rst.next()) {
Server_Log ci = new Server_Log();
ci.setId(rst.getInt(1));
ci.setDate(rst.getString(2));
ci.setTime(rst.getString(3));
ci.setReff(rst.getString(4));
ci.setDescription(rst.getString(5));
ci.setUser(rst.getInt(6));
ar.add(ci);
}
con.close();
} catch (Exception ex) {
Logger.getLogger(Student_Utilities.class.getName()).log(Level.SEVERE, null, ex);
}
return ar;
}
When accessing a remote database, especially over a slow link, the number of SQL statements executed is very important.
This is why the JDBC API support concepts like statement batching.
In your case, you're calling getUserName for every record in ar. Consider ways to reduce the number of calls.
Example 1: If user is usually the same, or only a few users are generating log entries, caching the user names would eliminate redundant lookup.
Example 2: Rather than looking up the user in the client, modify the Server_Log_Utilities.getBy2Dates to add a JOIN to the User table. This way, no extra turn-arounds to database will be needed.
Example 3: Instead of calling getUserName individually in a loop, collect the user ids, and lookup the names in a batch. Use either a JDBC batch of multiple SELECT statements, or use a single statement with UserId IN (?,?,?,?,...).
I am using an Oracle database and after sometime I receive the following Exception:
java.sql.SQLException: ORA-01000: maximum open cursors exceeded
I analysed my code and I seem to be closing all the ResultSet. This Exception only happens sometimes. Due to this error, I decided to change my code a little bit. Below is the code:
public class Audit {
private Connection connection;
private PreparedStatement insertAuditPreparedStatementSent;
private static int counter;
private static int JDBC_COUNTER;
public Audit() throws Exception {
connection = DriverManager.getConnection("url", "username", "password");
}
public int insertAudit(String message, java.util.Date sent) throws Exception {
PreparedStatment preparedStatement = prepareStatement(new String("INSERT INTO Audit (message, sent) VALUES (?, ?)");
if(JDBC_COUNTER == 0) {
// this is required to be executed so that ORA-08002 SQLException is not thrown
connection.createStatement().executeQuery(new String("SELECT AUDIT_SEQUENCE.NEXTVAL FROM DUAL"));
}
ResultSet resultSet = connection.createStatement().executeQuery(new String("SELECT AUDIT_SEQUENCE.CURRVAL FROM DUAL"));
resultSet.next();
primaryKey = resultSet.getInt(new String("CURRVAL"));
resultSet.close();
return primaryKey;
}
public void executeUpdateAudit(int id, java.util.Date sent) throws Exception {
if(updateAuditPreparedStatement == null) {
updateAuditPreparedStatement = connection.prepareStatement(new String("UPDATE AUDIT SET SENT = ? WHERE AUDIT_ID = " + id));
}
updateAuditPreparedStatement.setTimestamp(1, new java.sql.Timestamp(sent.getDate());
int i = updateAuditPreparedStatement.executeUpdate();
connection.commit();
}
public static void main(String[] args) throws Exception {
Audit audit = new Audit();
int primaryKey = audit.insertAudit("message", new java.util.Date());
audit.executeUpdateAudit(primaryKey, new java.util.Date());
int primaryKey2 = audit.insertAudit("message2", new java.util.Date());
audit.executeUpdateAudit(primaryKey2, new java.util.Date());
}
}
When inserting record 2 and updating record 2 only the updateAuditPreparedStatement.executeUpdate() returns 1, but the database updates the first record rather than the second record.
The reason to change the code is because I believe the PreparedStatement creates a new cursor each time. So, I want the insertAuditPreparedStatementSent to be there on many inserts without closing. I have tried insertAuditPreparedStatementSent.clearBatch() and insertAuditPreparedStatementSent.clearParameters().
I am not sure why it is updating record 1 on the primaryKey of record 2. The SQL is fine.
Any ideas?
You are not closing this:
if(JDBC_COUNTER == 0) {
// this is required to be executed so that ORA-08002 SQLException is not thrown
connection.createStatement().executeQuery(new String("SELECT AUDIT_SEQUENCE.NEXTVAL FROM DUAL"));
}
Also prepared statements needs to be closed as well and you are missing try/catch/finally in all these methods to prevent resource leakage. I strongly discourage using JDBC directly as it is very hard to work with.
In executeUpdateAudit you prepare the statement once using the first ID seen:
if(updateAuditPreparedStatement == null) {
updateAuditPreparedStatement = connection.prepareStatement(
new String("UPDATE AUDIT SET SENT = ? WHERE AUDIT_ID = " + id));
}
updateAuditPreparedStatement.setTimestamp(1,
new java.sql.Timestamp(sent.getDate());
In the second call the ID from the first call is still being used, since it's effectively hard-coded in the SQL.
You should be using a parameter for the ID as well:
if(updateAuditPreparedStatement == null) {
updateAuditPreparedStatement = connection.prepareStatement(
new String("UPDATE AUDIT SET SENT = ? WHERE AUDIT_ID = ?"));
}
updateAuditPreparedStatement.setTimestamp(1,
new java.sql.Timestamp(sent.getDate());
updateAuditPreparedStatement.setInt(2, id);
Not sure why you're using new String explicitly everywhere; it would be a little simpler as:
if(updateAuditPreparedStatement == null) {
updateAuditPreparedStatement = connection.prepareStatement(
"UPDATE AUDIT SET SENT = ? WHERE AUDIT_ID = ?");
}
updateAuditPreparedStatement.setTimestamp(1,
new java.sql.Timestamp(sent.getDate());
updateAuditPreparedStatement.setInt(2, id);
This is nothing to do with your ORA-01000, but this seems to be the main thrust of this question - you shouldn't really ask about two things at once, particularly if they aren't directly related...
Please check the oracle parameter
open_cursors
You will find this in the enterprise manager or by executing the following SQL:
select * from v$parameter a
where a.NAME = 'open_cursors';
If this parameter is very low (e.g. < 300) and you have lots of processes/users working at the same time, this error can happen.
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.
I am using the SQLite JDBC driver to access a database, for some reason the application terminates if there are no rows in the database with little explanation as to why it happened.
If the database is not empty it works perfectly fine;
This is where the termination occurs:
public static CustomFillTable Select(String table, String[] column, Object operand) throws SQLException
{
Connection connection = null;
PreparedStatement ps = null;
try
{
StringBuilder query = new StringBuilder();
query.append("select ");
for(int i = 0; i < column.length; i++)
{
query.append(column[i]);
if(i < column.length -1)
{
query.append(",");
}
}
query.append(" from ");
query.append(table);
//Verify usage of where clause
if(operand != null)
{
query.append(" where ");
query.append(operand);
}
//Termination occurs here
connection = DriverManager.getConnection(_connectionString);
connection.setAutoCommit(false);
ps = connection.prepareStatement(query.toString());
ResultSet rs = ps.executeQuery();
connection.commit();
CustomFillTable model = new CustomFillTable(rs);
while(rs.next())
{
System.out.println("id = " + rs.getString("id"));
System.out.println("name = " + rs.getString("name"));
}
return model;
}
The application closes at the DriverManager.getConnection line, something I don't find to be related to whether the database is populated or not.
Does anybody know how to fix this problem? I've posted the log dump information here.
Edit:
Connection String -
"jdbc:sqlite:D:\Documents\Uni\Semester 2\Languages, Platforms and Tools\Assignment\Java\SQLiteAssignment\mydatabasefile.db"
After looking at the vendors website it seems it is something that has been addressed in a later build. I ended up changing driver anyway but I thought this might be of some use to people.