I am writing a set of Eclipse console-based word games and have integrated an embedded Derby DB driver for storing a users result history.
My question is regarding Derby table initialization.
The Derby Database connection itself has a condition checker:
"jdbc:derby:dbName;create=true" So if the DB exists, it connects, if not it creates.
I am stuck on how to do this with a TBL in the Database. Even with the help from this similar question: how to create table if it doesn't exist using Derby Db
I've included my code below. My code throws a sql exception at the query for the Table 'RPS'. (My dbName is 'RPSdb' and TBL name is 'RPS'.)
This exception is caught by my catch block and passed to a static method in a different class ('DerbyHelper'). This static method is setup right now to always return true for the time being.
My question is how to I code my catch block and corresponding helper class so that I can implement the above mentioned TBL functionality?
Connection conn = null;
ArrayList<Statement> statements = new ArrayList<Statement>(); // list of Statements, PreparedStatements
Statement s;
ResultSet rs = null;
try {
conn = DriverManager.getConnection(protocol + dbName + ";create=true");
conn.setAutoCommit(false);
s = conn.createStatement();
statements.add(s);
rs = s.executeQuery("select * from RPS");
rs.next();
int cG = rs.getInt(1) + corGuess;
int iCG = rs.getInt(2) + inCorGuess;
s.executeUpdate("UPDATE RPS SET corGuesses = " + cG
+ ", inCorGuesses= " + iCG);
conn.commit();
}
catch( SQLException e ) {
if( DerbyHelper.tableAlreadyExists( e ) ) {
// what do I do here??
}
}
Interface java.sql.Connection has method getMetaData() which returns java.sql.DatabaseMetaData and that interface contains method getTables() which will tell you whether your database table exists, or not.
Related
I would like to do something like this:
BEGIN;
LOCK TABLE MY_TABLE IN ACCESS SHARE MODE NOWAIT;
SELECT * from MY_TABLE;
COMMIT TRANSACTION;
but this query returns an empty ResultSet after executing the query from prepared statement in JDBC, is it possible to wrap a SELECT statement in a transaction? Or should I change approach completely?
Basically I would need it to behave exactly like the select would (performance-wise too), but fail if there is an exclusive lock on the table.
EDIT:
Some context: Fail SELECT Query if table is locked in PostgreSQL (parent question)
Java code:
PreparedStatement sm = SimpleStatementWrapper.wrap(conn).prepareSelectStatementLockNowait(
SQL,
ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_READ_ONLY
);
ResultSet rs = sm.executeQuery();
private String wrapSql(String sql) {
String trimmed = sql.trim();
return "BEGIN; " +
"LOCK TABLE PING_TASK IN ACCESS SHARE MODE NOWAIT; "
+ (trimmed.endsWith(Constant.SEMI_COLON) ? trimmed : trimmed + Constant.SEMI_COLON)
+ " COMMIT TRANSACTION;";
}
// connectionCache is the same connection that was passed in .wrap(conn)
#Override
public PreparedStatement prepareSelectStatementLockNowait(String sql) throws SQLException {
return this.connectionCache.prepareStatement(wrapSql(sql));
}
To clarify what I was saying in the comments, I would use JDBC to control the transaction. That might look something like,
private static final String LOCKSQL = "LOCK TABLE PING_TASK IN ACCESS SHARE MODE NOWAIT";
#Override
public PreparedStatement prepareSelectStatementLockNowait(String sql)
throws SQLException {
this.connectionCache.setAutoCommit(false);
try (PreparedStatement ps1 = this.connectionCache.prepareStatement(LOCKSQL)) {
ps1.execute();
}
return this.connectionCache.prepareStatement(sql);
}
Then you can use
ResultSet rs = sm.executeQuery();
while (rs.next()) {
// ...
}
conn.commit(); // commit the transaction
As per my requirement, I need to pull records from Teradata db. While trying to pull data, I am getting exception as follows.
com.teradata.jdbc.jdbc_4.util.JDBCException: [Teradata Database] [TeraJDBC 14.10.00.09] [Error 3932] [SQLState 25000] Only an ET or null statement is legal after a DDL Statement.
at com.teradata.jdbc.jdbc_4.util.ErrorFactory.makeDatabaseSQLException(ErrorFactory.java:307)
at com.teradata.jdbc.jdbc_4.statemachine.ReceiveInitSubState.action(ReceiveInitSubState.java:108)
at com.teradata.jdbc.jdbc_4.statemachine.StatementReceiveState.subStateMachine(StatementReceiveState.java:321)
at com.teradata.jdbc.jdbc_4.statemachine.StatementReceiveState.action(StatementReceiveState.java:202)
at com.teradata.jdbc.jdbc_4.statemachine.StatementController.runBody(StatementController.java:122)
at com.teradata.jdbc.jdbc_4.statemachine.StatementController.run(StatementController.java:113)
at com.teradata.jdbc.jdbc_4.TDStatement.executeStatement(TDStatement.java:380)
at com.teradata.jdbc.jdbc_4.TDStatement.executeStatement(TDStatement.java:322)
at com.teradata.jdbc.jdbc_4.TDStatement.doNonPrepExecuteQuery(TDStatement.java:310)
at com.teradata.jdbc.jdbc_4.TDStatement.executeQuery(TDStatement.java:1085)
at com.uprr.netcontrol.wkfl_mgmt.eventhandler.eventprocessor.TeradataSelectFromVolatileTable.main(TeradataSelectFromVolatileTable.java:26)
I think I got this exception because of using Volatile table in my query. But I need to use volatile table as part of query to make grouping on a particular column to get comma separated column data. Please, suggest me how to get result set while using volatile table in query. Here, I am enclosing sample java program to reproduce the exception.
package com.sample.package;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TeradataSelectFromVolatileTable {
public static void main(String[] args) throws SQLException {
String url = "jdbc:teradata://<TERADATA_TABLE_NAME>";
final String query =
"CREATE VOLATILE TABLE VT_TEMP AS (" + "SELECT PERS_ID,FIR_NAME FROM <TABLE_NAME> AS PERSON)"
+ "WITH DATA UNIQUE PRIMARY INDEX(PERS_ID) ON COMMIT PRESERVE ROWS;" + // volatile table created.
"SELECT * FROM VT_TEMP;"; // pulling rows.
Connection con = null;
try {
System.out.println(" Looking for the Teradata JDBC driver... ");
// Loading the Teradata JDBC driver
Class.forName("com.teradata.jdbc.TeraDriver");
System.out.println(" JDBC driver loaded. \n");
con = DriverManager.getConnection(url, "<USER_ID>", "<PASSWORD>");
Statement stmt = con.createStatement();
System.out.println(" Statement object created. \n");
ResultSet rset = stmt.executeQuery(query);
//Some operations on resultset goes here...........
System.out.println(" total column count " + rset.getMetaData().getColumnCount());
} catch (Exception e) {
e.printStackTrace();
} finally {
// Close the statement
con.close();
System.out.println("\n Connection object closed. \n");
}
}
}
I think you may need to wrap your CT statement in an explicit, Teradata-mode transaction to resolve the error you are receiving:
public class TeradataSelectFromVolatileTable {
public static void main(String[] args) throws SQLException {
String url = "jdbc:teradata://<TERADATA_TABLE_NAME>";
final String query =
"BT;" + // Begin Teradata-mode Transaction
"CREATE VOLATILE TABLE VT_TEMP AS (" +
"SELECT PERS_ID,FIR_NAME FROM <TABLE_NAME> AS PERSON)"
+ "WITH DATA UNIQUE PRIMARY INDEX(PERS_ID) ON COMMIT PRESERVE ROWS;" + // volatile table created. +
"ET;" + // End Teradata-mode Transaction
"SELECT * FROM VT_TEMP;"; // pulling rows.
Connection con = null;
try {
System.out.println(" Looking for the Teradata JDBC driver... ");
// Loading the Teradata JDBC driver
Class.forName("com.teradata.jdbc.TeraDriver");
System.out.println(" JDBC driver loaded. \n");
con = DriverManager.getConnection(url, "<USER_ID>", "<PASSWORD>");
Statement stmt = con.createStatement();
System.out.println(" Statement object created. \n");
ResultSet rset = stmt.executeQuery(query);
//Some operations on resultset goes here...........
System.out.println(" total column count " + rset.getMetaData().getColumnCount());
} catch (Exception e) {
e.printStackTrace();
} finally {
// Close the statement
con.close();
System.out.println("\n Connection object closed. \n");
}
}
}
I am working on a web application using Java and MySQL.
I created a method that is supposed to return an ArrayList of the respective column name based on the various tables in the database.
However, when I debugged the method, I realised the while(rs.next()) causes an error. I used this site for reference, hence I am not sure what went wrong.
This is the code. Thanks.
// Returns the the all the columns in the table
public ArrayList getColumnName(String tableName) throws SQLException {
ResultSet rs = null;
List<String> columnName = new ArrayList<String>();
Statement st = null;
Connection con = null;
try {
// Get a connection from the connection factory
con = DriverManager.getConnection("jdbc:mysql://localhost:3306/information_schema", "root", "xxxx");
// Create a Statement object so we can submit SQL statements to the driver
st = con.createStatement();
StringBuilder sql = new StringBuilder("SELECT column_name FROM information_schema.columns " +
"WHERE table_schema = 'testDB' AND table_name = '");
sql.append(tableName).append("'");
rs = st.executeQuery(sql.toString());
while (rs.next()) { // getting error..
columnName.add(rs.getString("column_name"));
}
} catch (SQLException ex) {
Logger.getLogger(ModificationPage.class.getName()).log(Level.SEVERE, null, ex);
} finally {
if (con != null || st != null) {
st.close();
con.close();
}
}
return (ArrayList) columnName;
}
According to the Javadoc of 1.6 (not sure which version of Java you're using):
Throws:
SQLException - if a database access error occurs or this method is called on a closed result set
It's very, very unlikely that if you actually got to the line where rs.next() was called, that a database error occurred just then. So, the most likely result is that the result set was closed.
Please alter your code to the following and see if you still get the error on the same line:
while (!rs.isClosed() && rs.next()) { // getting error..
columnName.add(rs.getString("column_name"));
}
Also, Holy SQL Injection Attack, Batman!
Taking the raw string as you're doing and enclosing it within single quotes leads this code to have an SQL injection vulnerability. Basically all a malicious user has to do is end your query with a single quote (') and run a query of their own afterwards.
So, the exception never happens ?
A query error should be thrown at rs = st.executeQuery(sql.toString()) if that were the case, but if it make it to whileand didn't iterate, it's because of an empty resultset
Maybe you're passing wrong arguments to the query ?
This post showed executing multiple queries in a single JDBC invocation (against a SQL Server database) by separating them with semicolons. When I tried to do the same with Oracle 10G, an error "invalid character" propped up :
class db
{
public static void main(String aa[])throws Exception
{
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection conn = DriverManager.getConnection("jdbc:oracle:thin:#//192.168.10.29:1521/ttt","username","password");
PreparedStatement stat = conn.prepareStatement("select voila from app where rownum<4; select code from process where rownum<4");
stat.execute();
while (stat.getMoreResults()){
ResultSet rs = stat.getResultSet();
while (rs.next()){
System.out.println(rs.getString(1));
}
}
conn.close();
}
}
What am I doing wrong ?
You are doing nothing wrong (except to assume that all DBMS work the same)
Oracle (and its JDBC driver) simply does not support this.
You need to run each SELECT individually.
Btw: this is one of the reason that some SQL injection attacks don't work with Orace - especially the famous "little bobby tables" cartoon.
It's possible to get multiple result sets back from Oracle into JDBC in a single call. There are a few ways to do it; a good post at Oracle-Base shows how.
The mechanism I use is to make an anonymous block in a callable statement, then bind a SYS_REFCURSOR for each result set as an output parameter.
Here's some code that does just that. It's lazy for error handling, but it gets the idea across:
public void getMultiple() throws Exception {
// get connection
Connection conn = DriverManager.getConnection(TestConfig.JDBC_URL, TestConfig.DB_USERNAME, TestConfig.DB_PASSWORD);
// here's the statement; it uses an anonymous block. In that block,
// we've declared two SYS_REFCURSOR objects which are opened over our
// SELECT statements. Once the statements are opened, we bind the
// SYS_REFCURSOR objects so they can be retrieved from JDBC
String s =
"DECLARE" +
" l_rs1 SYS_REFCURSOR; " +
" l_rs2 SYS_REFCURSOR; " +
"BEGIN "+
" OPEN l_rs1 FOR " +
" SELECT 'Moose' FROM DUAL;" +
" OPEN l_rs2 FOR " +
" SELECT 'Squirrel' FROM DUAL; " +
" ? := l_rs1;" +
" ? := l_rs2;" +
"END;";
// prepare the callable statement, registering
// the output parameter we want
CallableStatement cs = conn.prepareCall(s);
cs.registerOutParameter(1, OracleTypes.CURSOR);
cs.registerOutParameter(2, OracleTypes.CURSOR);
// execute the callable statement
cs.execute();
// retrieve the result sets by getting the bound output objects and
// casting them to Java ResultSet objects
ResultSet rs1 = (ResultSet) cs.getObject(1);
ResultSet rs2 = (ResultSet) cs.getObject(2);
// advance the first result set and print the string it yields
rs1.next();
System.out.printf("Result set 1 has '%s'\n", rs1.getString(1));
// advance the second result set and print the string it yields
rs2.next();
System.out.printf("Result set 2 has '%s'\n", rs2.getString(1));
// close everything up
rs2.close();
rs1.close();
cs.close();
conn.close();
}
I hope that helps you out!
When I execute the following code, I get an exception. I think it is because I'm preparing in new statement with he same connection object. How should I rewrite this so that I can create a prepared statement AND get to use rs2? Do I have to create a new connection object even if the connection is to the same DB?
try
{
//Get some stuff
String name = "";
String sql = "SELECT `name` FROM `user` WHERE `id` = " + userId + " LIMIT 1;";
ResultSet rs = statement.executeQuery(sql);
if(rs.next())
{
name = rs.getString("name");
}
String sql2 = "SELECT `id` FROM `profiles` WHERE `id` =" + profId + ";";
ResultSet rs2 = statement.executeQuery(sql2);
String updateSql = "INSERT INTO `blah`............";
PreparedStatement pst = (PreparedStatement)connection.prepareStatement(updateSql);
while(rs2.next())
{
int id = rs2.getInt("id");
int stuff = getStuff(id);
pst.setInt(1, stuff);
pst.addBatch();
}
pst.executeBatch();
}
catch (Exception e)
{
e.printStackTrace();
}
private int getStuff(int id)
{
try
{
String sql = "SELECT ......;";
ResultSet rs = statement.executeQuery(sql);
if(rs.next())
{
return rs.getInt("something");
}
return -1;
}//code continues
The problem is with the way you fetch data in getStuff(). Each time you visit getStuff() you obtain a fresh ResultSet but you don't close it.
This violates the expectation of the Statement class (see here - http://docs.oracle.com/javase/7/docs/api/java/sql/Statement.html):
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. All execution methods in the Statement interface implicitly close a statment's current ResultSet object if an open one exists.
What makes things even worse is the rs from the calling code. It is also derived off-of the statement field but it is not closed.
Bottom line: you have several ResultSet pertaining to the same Statement object concurrently opened.
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.
I guess after while(rs2.next()) you are trying to access something from rs1. But it's already closed since you reexecuted statement to get rs2 from it. Since you didn't close it, I beleive it's used again below.