Result Set closes automatically - java

I have a problem with my music bot for Discord.
I want to send an embed message when a track ist started, but the ResultSet always closes.
So it can't pass the if-query.
Here is my code (class "TrackScheduler"):
try {
file = new URL("https://img.youtube.com/vi/" + videoID + "/hqdefault.jpg").openStream();
builder.setImage("attachment://thumbnail.png");
System.out.println("4");
ResultSet set = LiteSQL.onQuery("SELECT * FROM musicchannel WHERE guildid = " + guildid);
try {
System.out.println("3");
if(set.next()) {
long channelid = set.getLong("channelid");
TextChannel channel;
System.out.println("2");
if((channel = guild.getTextChannelById(channelid)) != null) {
System.out.println("1");
channel.sendTyping().queue();
channel.sendFile(file, "thumbnail.png").embed(builder.build()).queue();
}
}
}
catch (SQLException e) {
e.printStackTrace();
}
}
catch (IOException e) {
e.printStackTrace();
}
}
My LiteSQL.onQuery (class "LiteSQL"):
private static Connection c;
private static Statement s;
public static ResultSet onQuery(String sql) {
try {
return s.executeQuery(sql);
}
catch(SQLException e) {
e.printStackTrace();
}
return null;
}
Here is the error:
ava.sql.SQLException: ResultSet closed
at org.sqlite.core.CoreResultSet.checkOpen(CoreResultSet.java:76)
at org.sqlite.jdbc3.JDBC3ResultSet.findColumn(JDBC3ResultSet.java:39)
at org.sqlite.jdbc3.JDBC3ResultSet.getLong(JDBC3ResultSet.java:423)
at de.nameddaniel.bot.musik.TrackScheduler.onTrackStart(TrackScheduler.java:79)
at com.sedmelluq.discord.lavaplayer.player.event.AudioEventAdapter.onEvent(AudioEventAdapter.java:72)
at com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayer.dispatchEvent(DefaultAudioPlayer.java:368)
at com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayer.startTrack(DefaultAudioPlayer.java:117)
at com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayer.playTrack(DefaultAudioPlayer.java:80)
at de.nameddaniel.bot.musik.AudioLoadResult.trackLoaded(AudioLoadResult.java:20)
at com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager.checkSourcesForItemOnce(DefaultAudioPlayerManager.java:443)
at com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager.checkSourcesForItem(DefaultAudioPlayerManager.java:419)
at com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager.lambda$createItemLoader$0(DefaultAudioPlayerManager.java:218)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
I'm new here, so if there is any missing information, please tell me.
As well, I'm sorry for the bad formatting.
Greetings, Daniel :)

tl;dr
Do not use static on your Statement and Connection fields.
Details
This code has a security leak. Look up SQL injection. The basic gist is: Statement is almost entirely useless. You want PreparedStatement, and you want your SQL queries to be solely string literals. Don't, ever, 'make the query string' by concatenating user input in. The Query string should be, say, SELECT * FROM musicchannel WHERE guildid = ? (yes, with a literal question mark in the string), then use the setInt method of PreparedStatement to set the guild id. Or better yet, as the JDBC API is not really designed for consumption like this, use something like JDBI.
This is bad exception handling. If you don't know what to do, the right 'I dont know' catch block is throw new RuntimeException("Uncaught", e); and not e.printStackTrace();. Better yet, have these methods just throw SQLException; methods that obviously do DB things should be throwing that. Note that your main method can (and should) be declared to throw Exception.
Connection, PreparedStatement, and ResultSets are all resources and need to be opened via try-with-resources. Not doing so means your app has a leak and will break something if it runs long enough. For DB code, the DB will eventually run out of connections and become entirely inaccessible until you close the java app. That's why you need try-with-resources.
You have a single Statement and Connection (the fields are static). Presumably your discord bot can receive more than one message, so if you try to send more than one, the system goes down in flames. Don't use 'static' here. The code you pasted does not itself contain anything that would close your ResultSet, but by redesigning away from static the problem is likely to go away by itself.

(Apart from the other answer, which is actually all very good suggestions and you should follow) I presume the following is the problem:
return s.executeQuery(sql);
I don't think that will work if it's a static and is being used multiple times by other objects. It will be cleaned up eventually. Rather than doing that there, you should be returning just an object with the data that you need. Look up the DAO class pattern.

Related

Apache Jena SPARQL query will not abort

I'm having a problem in my Java application where Apache Jena will never stop a SPARQL query until it's finished, even if I explicitly tell it to stop. Here's the code that gets called to run a query:
try {
Model union = null;
union = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM_RULE_INF);
if (ontologies != null)
for (OntModel om : ontologies)
union = ModelFactory.createUnion(union, om);
Reasoner reasoner = ReasonerRegistry.getOWLReasoner();
reasoner = reasoner.bindSchema(union);
InfModel infmodel = ModelFactory.createInfModel(reasoner, triples);
query_running = true;
Query query = QueryFactory.create(query_string);
query_execution = QueryExecutionFactory.create(query, infmodel);
ResultSet rs = query_execution.execSelect();
for ( ; rs.hasNext(); ) {
// do stuff with results
}
} catch (Exception e) {}
finally {
stopQuery();
}
stopQuery() gets called at the end, but the method is also called when the user hits a "cancel" button. Here's what that method looks like:
public void stopQuery() {
try {
if (query_execution != null) {
//query_execution.close();
query_execution.abort();
query_execution = null;
System.out.println("STOPPED");
}
} catch (Exception e) { e.printStackTrace(); }
query_running = false;
}
When the cancel button is hit while a query is running (10+ minutes on a relatively small dataset...?), the method gets called, but the query continues to run in the background. I know it's still running because I can see the application in task manager taking up 30%+ CPU until the query presumably completes. I've tried .abort(), .close(), and both at the same time, but I cannot figure out how to stop the query mid-execution. I've even tried wrapping the query code in a separate thread, but that doesn't work either. It makes sense that threading wouldn't solve the problem because the thread needs to see the interrupt request, but the code is freezing on a particular line. The code seems to freeze on rs.hasNext(), but not the first check. It will run quickly with the first x results of the ResultSet (which are likely explicit statements it finds easily), but then it will seem to freeze for a long while after that, likely searching for implicit results with the reasoner. How can I force the query to stop? I don't want to use a timeout -- I want the user to have the option to stop the query or let it play out. This problem is not specific to any one query or dataset.
Thanks.

Add new record to oracle database from Java Gui

I'm trying to add a new record to my oracle 11g database from javaNetbeans but it's not working. Here is my code.
private void InsertbtnActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
String PositionType=jTextField1.getText().trim();
String PositionTypeDesc=jTextField2.getText().trim();
try{
Class.forName("oracle.jdbc.oracleDriver");
Connection c= DriverManager.getConnection("jdbc:oracle:thin:#localhost:1521:HRM","System","jayden");
java.sql.Statement st=c.createStatement();
st.executeUpdate("Insert into PositionType values('"+jTextField1.getText()+"','"+jTextField2.getText()+"')");
System.out.println();
JOptionPane.showMessageDialog(Null,"DATA SACVE!");
}catch (ClassNotFoundException | SQLException e){
}
}
The problem lies here:
catch (ClassNotFoundException | SQLException e){
}
Now you have a problem and
Don't know what went wrong
Don't know where it happend
Don't even known if something went wrong
When getting an exception, you should at least log it somehow, and deal with the situation properly (either rethrow it, or do error handling by showing an error dialog or something). The least you should do is:
catch (ClassNotFoundException | SQLException e){
e.printStackTrace();
}
which prints the exception (type and message) as well as the stacktrace (invocation chain, with details on which methods / code lines the exception occurred.
That will give you insights on what went wrong and what to fix.
Most probably the class name is wrong.
"oracle.jdbc.oracleDriver" is not plausible, try "oracle.jdbc.OracleDriver" where the class name begins with an upper-case O. By convention, Java class names should always begin with an upper-case letter.
But make sure to install a proper error handling as well, as suggested by the other answers / comments.

java.sql.SQLException Closed Statement

I have a problem, the below code runs fine if I run it without the autoCommit property, however I would prefer to run it as a transaction, the code basically inserts an article's header information and then the list each articles associated with it (so it's like a one-to-many relationship), so I could like to commit everything in one go rather than first the article information and then its items. The issue is that when I reach to the cn.commit() line, I get an exception that says "Closed Statement"
database insertion method
public static void addArticle(Article article) throws SQLException {
Connection cn = null;
PreparedStatement ps = null;
StringBuffer insert = new StringBuffer();
StringBuffer itemsSQL = new StringBuffer();
try {
article.setArticleSortNum(getNextArticleNum(article.getShopId()));
article.setArticleId(DAOHelper.getNextId("article_id_sequence"));
cn = DBHelper.makeConnection();
cn.setAutoCommit(false);
insert.append("insert query for article goes here");
ps = cn.prepareStatement(insert.toString());
int i = 1;
ps.setLong(i, article.getArticleId()); i++;
ps.setLong(i, article.getShopId()); i++;
ps.setInt(i, article.getArticleNum()); i++;
// etcetera...
ps.executeUpdate();
itemsSQL.append("insert query for each line goes here");
itemStatement = cn.prepareStatement(itemsSQL.toString());
for(Article item : article.getArticlesList()) {
item.setArticleId(article.getArticleId());
i= 1;
itemStatement.setLong(i, item.getArticleId()); i++;
itemStatement.setInt(i, item.getItemsOnStock()); i++;
itemStatement.setInt(i, item.getQuantity()); i++;
// etcetera...
itemStatement.executeUpdate();
}
cn.commit();
} catch (SQLException e) {
cn.rollback();
log.error(e.getMessage());
throw e;
}
finally {
DBHelper.releasePreparedStatement(ps);
DBHelper.releasePreparedStatement(itemStatement);
DBHelper.releaseConnection(cn);
}
}
I also had the items insertion where the For is running with addBatch() then executeBatch but also the same Closed Statement error upon reaching cn.commit()... I dont understand why its closing, all connections and everything is released in the finally clause, so I get the feeling I'm making some fundamental error I'm not aware of... Any ideas? Thanks in advance!
EDIT: Below is the stack trace:
java.sql.SQLException: Closed Statement at
oracle.jdbc.dbaccess.DBError.throwSqlException(DBError.java:189) at
oracle.jdbc.dbaccess.DBError.throwSqlException(DBError.java:231) at
oracle.jdbc.dbaccess.DBError.throwSqlException(DBError.java:294) at
oracle.jdbc.driver.OracleStatement.ensureOpen(OracleStatement.java:6226)
at
oracle.jdbc.driver.OraclePreparedStatement.sendBatch(OraclePreparedStatement.java:592)
at
oracle.jdbc.driver.OracleConnection.commit(OracleConnection.java:1376)
at com.evermind.sql.FilterConnection.commit(FilterConnection.java:201)
at
com.evermind.sql.OrionCMTConnection.commit(OrionCMTConnection.java:461)
at com.evermind.sql.FilterConnection.commit(FilterConnection.java:201)
at com.dao.ArticlesDAO.addArticle(ArticlesDAO.java:571) at
com.action.registry.CustomBaseAction.execute(CustomBaseAction.java:57)
at
org.apache.struts.action.RequestProcessor.processActionPerform(RequestProcessor.java:431)
at
org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:236)
at
org.apache.struts.action.ActionServlet.process(ActionServlet.java:1196)
at
org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:432)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:760) at
javax.servlet.http.HttpServlet.service(HttpServlet.java:853) at
com.evermind.server.http.ServletRequestDispatcher.invoke(ServletRequestDispatcher.java:765)
at
com.evermind.server.http.ServletRequestDispatcher.forwardInternal(ServletRequestDispatcher.java:317)
at
com.evermind.server.http.HttpRequestHandler.processRequest(HttpRequestHandler.java:790)
at
com.evermind.server.http.HttpRequestHandler.run(HttpRequestHandler.java:270)
at
com.evermind.server.http.HttpRequestHandler.run(HttpRequestHandler.java:112)
at
com.evermind.util.ReleasableResourcePooledExecutor$MyWorker.run(ReleasableResourcePooledExecutor.java:192)
at java.lang.Thread.run(Unknown Source)
EDIT 2:
These are the parameters in the driver's datasource config, I thought the debugging process might be making it time out, but even finishing in less than a second throws the closed statement exception
min-connections="20"
max-connections="200"
inactivity-timeout="20"
stmt-cache-size="40"/>
It's usually best to create a statement, use it and close it as soon as possible, and it does no harm to do so before the transaction gets committed. From reading the Oracle tuturial about the batch model it's sounding like it could be a problem to have multiple statements open at one time. I would try closing the ps object before working with the itemStatement, then moving the initialization
itemStatement = cn.prepareStatement(itemsSQL.toString());
to directly above the for loop, and also move where you close the itemStatement to immediately after the for loop:
PreparedStatement itemStatement = cn.prepareStatement(itemsSQL.toString());
try {
for(Article item : article.getArticlesList()) {
item.setArticleId(article.getArticleId());
i= 1;
itemStatement.setLong(i, item.getArticleId()); i++;
itemStatement.setInt(i, item.getItemsOnStock()); i++;
itemStatement.setInt(i, item.getQuantity()); i++;
// etcetera...
itemStatement.executeUpdate();
}
} finally {
DBHelper.releasePreparedStatement(itemStatement);
}
It looks like what is going on is you have some batching parameter set on the connection that is causing the connection to try to look for unfinished business in the statement to finish up; it's finding the statement is already closed and the connection is complaining about it. This is weird because at the point the commit blows up on you the code hasn't reached the finally where the statement gets closed.
Reading up on Oracle batching models may be helpful. Also check the JDBC driver version and make sure it's right for the version of Oracle you're using, and see if there are any updates available for it.

How to provide correct arguments to setAsciiStream method?

This is my FULL test code with the main method:
public class TestSetAscii {
public static void main(String[] args) throws SQLException, FileNotFoundException {
String dataFile = "FastLoad1.csv";
String insertTable = "INSERT INTO " + "myTableName" + " VALUES(?,?,?)";
Connection conStd = DriverManager.getConnection("jdbc:xxxxx", "xxxxxx", "xxxxx");
InputStream dataStream = new FileInputStream(new File(dataFile));
PreparedStatement pstmtFld = conStd.prepareStatement(insertTable);
// Until this line everything is awesome
pstmtFld.setAsciiStream(1, dataStream, -1); // This line fails
System.out.println("works");
}
}
I get the "cbColDef value out of range" error
Exception in thread "main" java.sql.SQLException: [Teradata][ODBC Teradata Driver] Invalid precision: cbColDef value out of range
at sun.jdbc.odbc.JdbcOdbc.createSQLException(Unknown Source)
at sun.jdbc.odbc.JdbcOdbc.standardError(Unknown Source)
at sun.jdbc.odbc.JdbcOdbc.SQLBindInParameterAtExec(Unknown Source)
at sun.jdbc.odbc.JdbcOdbcPreparedStatement.setStream(Unknown Source)
at sun.jdbc.odbc.JdbcOdbcPreparedStatement.setAsciiStream(Unknown Source)
at file.TestSetAscii.main(TestSetAscii.java:21)
Here is the link to my FastLoad1.csv file. I guess that setAsciiStream fails because of the FastLoad1.csv file , but I am not sure
(In my previous question I was not able to narrow down the problem that I had. Now I have shortened the code.)
It would depend on the table schema, but the third parameter of setAsciiStream is length.
So
pstmtFld.setAsciiStream(1, dataStream, 4);
would work for a field of length 4 bytes.
But I dont think it would work as you expect it in the code. For each bind you should have separate stream.
This function setAsciiStream() is designed to be used for large data values BLOBS or long VARCHARS. It is not designed to read csv file line by line and split them into separate values.
Basicly it just binds one of the question marks with the inputStream.
After looking into the provided example it looks like teradata could handle csv but you have to explicitly tell that with:
String urlFld = "jdbc:teradata://whomooz/TMODE=ANSI,CHARSET=UTF8,TYPE=FASTLOADCSV";
I don't have enough reputation to comment, but I feel that this info can be valuable to those navigating fast load via JDBC for the first time.
This code will get the full stack trace and is very helpful for diagnosing problems with fast load:
catch (SQLException ex){
for ( ; ex != null ; ex = ex.getNextException ())
ex.printStackTrace () ;
}
In the case of the code above, it works if you specify TYPE=FASTLOADCSV in the connection string, but when run multiple times will fail due to the creation of the error tables _ERR_1 and _ERR_2. Drop these tables and clear out the destination tables to run again.

Using Types.NVARCHAR with oracle JDBC driver to work with Cyrillic chars

I am trying to use the "New Methods for National Character Set Type Data in JDK 1.6", to get a standard JDBC solution to handle cyrillic chars, but when the execution reaches any line with NVARCHAR type, for instance:
preparedSelect.setObject(3, "суббота", Types.NVARCHAR);
Then I get this exception:
java.sql.SQLException: Invalid column type
at oracle.jdbc.driver.SQLStateMapping.newSQLException(SQLStateMapping.java:70)
at oracle.jdbc.driver.DatabaseError.newSQLException(DatabaseError.java:131)
at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:197)
at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:261)
at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:269)
at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:490)
at oracle.jdbc.driver.OraclePreparedStatement.setObjectCritical(OraclePreparedStatement.java:7922)
at oracle.jdbc.driver.OraclePreparedStatement.setObjectInternal(OraclePreparedStatement.java:7502)
at oracle.jdbc.driver.OraclePreparedStatement.setObject(OraclePreparedStatement.java:7975)
at oracle.jdbc.driver.OraclePreparedStatementWrapper.setObject(OraclePreparedStatementWrapper.java:222)
I also tried to use setNString() but I get an even more strange exception:
java.lang.AbstractMethodError: oracle.jdbc.driver.OraclePreparedStatementWrapper.setNString(ILjava/lang/String;)V
If I use java -Doracle.jdbc.defaultNChar=true myApplication with regular Types.VARCHAR, the Russian words are stored correctly. But using -Doracle.jdbc.defaultNChar=true is not an option since I'm working on a legacy application, I do not have control of running production environment, I'm just writing a component to it. Furthermore, this "Readme for NChar How-to" states that "This conversion has a substantial performance impact". So setting everything to NChar by default when only less than 1% of my tables needs this conversion in not a smart choice.
I'm using oracle thin driver and I have ojdbc6.jar and orai18n.jar in my classpath.
I'm looking for a standard JDBC solution. I can not use any methods or constants with "oracle" on them. OraclePreparedStatement is not an option for me.
I tried using Types.NVARCHAR with MSSQL Server and it runs fine.
I found the solution!
I was using ojdbc 11.2.0.1. When I switched to 11.2.0.2, I could get setNString() working properly.
But I'm still getting the same java.sql.SQLException: Invalid column type if I use setObject() with Type.NVARCHAR. Shame on you Oracle...
Anyway, the solution: switch to ojdbc 11.2.0.2
I managed to make it work some time ago with the following incantations. These methods are an excerpt from a bigger class.
import com.mchange.v2.c3p0.C3P0ProxyStatement;
import oracle.jdbc.OraclePreparedStatement;
static {
try {
SET_FORM_OF_USE_METHOD = OraclePreparedStatement.class.getDeclaredMethod("setFormOfUse", new Class[] { Integer.TYPE, Short.TYPE });
} catch (NoSuchMethodException ex) {
LOG.fatal("Can't find the setFormOfUse method", ex);
throw new ExceptionInInitializerError(ex);
}
}
public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws SQLException {
if (st instanceof OraclePreparedStatement) {
((OraclePreparedStatement)st).setFormOfUse(index, OraclePreparedStatement.FORM_NCHAR);
} else if (st instanceof C3P0ProxyStatement) {
try {
C3P0ProxyStatement c3p0St = (C3P0ProxyStatement) st;
c3p0St.rawStatementOperation(SET_FORM_OF_USE_METHOD, C3P0ProxyStatement.RAW_STATEMENT, new Object[]{index, OraclePreparedStatement.FORM_NCHAR});
} catch (IllegalAccessException ex) {
throw new UnexpectedException("Error calling setFormOfUse through C3P0", ex);
} catch (IllegalArgumentException ex) {
throw new UnexpectedException("Error calling setFormOfUse through C3P0", ex);
} catch (InvocationTargetException ex) {
throw new UnexpectedException("Error calling setFormOfUse through C3P0", ex);
}
} else {
throw new IllegalArgumentException("Unkown PreparedStatement implementation: " + st.getClass() + ". Maybe an unknown connection pool is hiding the OraclePreparedStatement?");
}
st.setString(index, (String) value);
}
The nullSafeSet method is structured to work directly on OraclePreparedStatement instances, or on C3P0ProxyStatement in case you have the C3P0 connection pool; other options will have to be used in case of different pools.
This method worked with the ojdbc14.jar from Oracle 10.2.0.4.

Categories