execute batch with oracle prepared statement - java

I try to add batch prepared statement with following code:
Connection c = ...
PreparedStatement ps = c.prepareStatement(query1);
ps.setObject(....)
...
ps.addBatch(query2); // SqlException : Unsupported feature
Does not oracle jdbc driver support batches, or I am doing something wrong?
I am using oracle thin driver. Version from MANIFEST.MF Implementation-Version: 11.2.0.1.0.
java.sql.SQLException: Unsupported feature
at oracle.jdbc.driver.OraclePreparedStatement.addBatch(OraclePreparedStatement.java:9803)
at oracle.jdbc.driver.OracleStatementWrapper.addBatch(OracleStatementWrapper.java:285)
at org.jboss.resource.adapter.jdbc.WrappedStatement.addBatch(WrappedStatement.java:731)
at <application classes>

You are creating a PreparedStatement using query1 and adding query2 to an already prepared statetement of which it doesn't belong to.
If you are using PreparedStatement, I suggest using the PreparedStatement.addBatch() method instead.
PreparedStatement ps = c.prepareStatement(query1);
ps.setObject(....);
ps.addBatch(); //Voila

The JDBC specification explicitly requires PreparedStatement (and CallableStatement) implementations to throw an SQLException if you call any of the execute, executeUpdate, executeQuery or addBatch methods that accept a query string.
See for example the Javadoc on Statement.addBatch(String sql):
Throws:
SQLException - if a database access error occurs, this method is called on a closed Statement, the driver does not support batch updates, the method is called on a PreparedStatement or CallableStatement
(emphasis mine)
With PreparedStatement you can only use the setXXX methods, then use addBatch() to batch sets of parameter values for the prepared query (and repeat that for a different set of parameter values). You cannot batch different queries the way you can with a normal Statement.
The way to use batching with PreparedStatement is roughly like this:
try (PreparedStatement ps = c.prepareStatement(query1)) {
while (moreParameterValueSets) {
ps.setObject(....)
//...
ps.addBatch();
}
ps.executeBatch();
}

Related

Java HSQLDB connection issue

I have been making a program that is using HSQL to connect to a database that I created. For some reason some methods in my class can call on the database and perform commands, while other parts cannot. I keep getting this error,
java.sql.SQLFeatureNotSupportedException: feature not supported
and here is the method,
public List<CustomerInfo> DBgetInfo(String Customer)
throws ClassNotFoundException, SQLException {
Class.forName("org.hsqldb.jdbcDriver");
Connection con = DriverManager.getConnection(urlConnection, userId,
password);
Statement stmt= con.createStatement();
String query = "SELECT * FROM PUBLIC.CUSTOMER";
ResultSet rs = stmt.executeQuery(query);
rs.first(); //The error happens on this line
rs.close();
stmt.close();
con.close();
}
I have ran the debugger multiple times and there error is in this method on the rs.first line. I have tried remaking the DB, reimporting all the files, checking to make sure the command is correct, and ect... The weird thing is that earlier in this class I have a method very similar to this, but it has no issues. I really can't figure out what the problem is.
According to the documentation this error occurs:
Throws:
SQLException - if a database access error occurs, this method is called on a closed result set or the result set type is TYPE_FORWARD_ONLY
SQLFeatureNotSupportedException - if the JDBC driver does not support this method
Earlier on the same page, there is a section on HSQL specific details for result sets. To call first you need to modify your statement creation:
ResultSet object generated by HSQLDB is by default of ResultSet.TYPE_FORWARD_ONLY (as is standard JDBC behavior) and does not allow the use of absolute and relative positioning methods. If a statement is created with:
Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
then the ResultSet objects it produces support using all of the absolute and relative positioning methods of JDBC2 to set the position of the current row...
But you might want to think about why you need to call first.

What is wrong with this getGeneratedKeys() JDBC statement?

This code fragment
PreparedStatement statement = connect.prepareStatement(
query, Statement.RETURN_GENERATED_KEYS);
statement.executeUpdate(query);
try (ResultSet generatedKeys = statement.getGeneratedKeys()) {
...
}
keeps throwing
java.sql.SQLException: Generated keys not requested. You need to specify Statement.RETURN_GENERATED_KEYS to Statement.executeUpdate() or Connection.prepareStatement().
I'd say that generatedkeys ARE requested at creation of the statement, but apparently I'm wrong. What is the issue here? (I'm new to JDBC. What I'm trying to do is retrieving the primary key value which has been automatically set (AUTO_INCREMENTed) during the preceding record insertion. Maybe there is a better way to do that... I'm using mysql.)
Your problem is this line:
statement.executeUpdate(query);
don't pass the query parameter in use the no parameter version:
statement.executeUpdate();
The first version doesn't use the prepared statement you already made but instead makes a new one using your query String (without the RETURN_GENERATED_KEYS option)
Remove the parameter from
statement.executeUpdate(query);
change to
statement.executeUpdate();
because you have already prepared the statement in
PreparedStatement statement = connect.prepareStatement(
query, Statement.RETURN_GENERATED_KEYS);

How to execute this Oracle statement on JDBC

I have trouble executing the following using a JDBC prepared statement:
CREATE OR REPLACE TRIGGER Time_trg BEFORE INSERT ON Time FOR EACH ROW
BEGIN
SELECT Time_seq.NEXTVAL INTO :new.id FROM dual;
END;
The code:
try {
PreparedStatement statement = connection.prepareStatement( sql );
preparedStatement.executeUpdate();
} finally {
statement.close();
}
I'm getting this error:
java.sql.SQLException: Missing IN or OUT parameter at index:: 1
I'm working on a database agnostic solution so I need something that is portable. So what is oracle's problem?
There is no need to write our own stored procedure to do this. Oracle provides a built-in stored procedure we can use: DBMS_UTILITY.EXEC_DDL_STATEMENT:
DBMS_UTILITY.EXEC_DDL_STATEMENT('create table t1 (id number)');
In fact this is safer than the workaround procedure suggested in the accepted answer as it doesn't allow the execution of DML and so is protected against SQL injection
Use oracle.jdbc.OraclePreparedStatement
OraclePreparedStatement statement = (OraclePreparedStatement)connection.prepareStatement( sql );
As this is much specific to Oracle, regular PrepareStatement doesn't help. Oracle provides a wrapper for the same, with additional functionalities as well.
Similarly, Oracle provides OracleCallableStatement similar to CallableStatement
WorkAround: (When PreparedStatement has to be used - Risk of being misused
CREATE PROCEDURE EXECUTE_MY_DDL(INSTRING VARCHAR2)
AS
BEGIN
EXECUTE IMMEDIATE INSTRING;
END;
Reference JavaDoc
Since you cannot have any bind variables in Oracle DDL anyway, why use a PreparedStatement? You can use a static statement instead and shouldn't run into this problem:
try (Statement s = connection.createStatement()) {
s.executeUpdate(sql);
}

Is there any way to prepare a sql statement in Java without using a Connection object?

I'm pretty new to JDBC, so this is probably a very straightforward question.
I have to run several SQL statements, so I'm trying to write a generic "runSQLResultSet" method that takes a String sql statement and returns a ResultSet. I want it to take care of opening the database connection, executing the statement, storing the ResultSet in a CachedRowSetImpl object (so that it will persist after the connection is closed), and closing the connection. I created a method that does this and it works.
My problem now is that I want to be able to use it for dynamic statements that are built with variables. I looked around, and it seems that I should really change my method to take a PreparedStatement instead of just a plain String. Then I can build the PreparedStatement on the other side and pass it to the method. The problem is that I can't seem to create a PreparedStatement without a Connection object. I can open the connection before preparing the statement, but that defeats my purpose of factoring out the database processing into the runSQLResultSet method. I need a way to build a SQL statement with dynamic components, without a connection object, and pass it to a method that will then execute it. Is there any way to do this with a PreparedStatement? Is there any other statement object I can use instead? Otherwise - is there any better way to do this?
public ResultSet excuteStatement(String statement, Object... params){
statement = conn.prepareStatement(statement);
int i = 1;
for (Object o:params){
statement.setObject(i++,o);
}
ResultSet rs = statement.executeQuery();
return rs;
}
You cannot create one without a DB connection. A PreparedStatement will be precompiled in the DB and thus really needs an open connection.
You can instead also just consider to dynamically build the SQL string instead. Generating the PreparedStatement placeholders (the ? things) in a loop and and using String#format() to put them in the SQL string. u can also consider to just pass the variables to your runSQLResultSet method and build there instead.
As per the comments, here's an example:
try {
connection = database.getConnection();
statement = connection.prepareStatement(SQL);
setValues(statement, values);
// ...
.
public static void setValues(PreparedStatement preparedStatement, Object... values) throws SQLException {
for (int i = 0; i < values.length; i++) {
preparedStatement.setObject(i + 1, values[i]);
}
}
If I understand you correctly, you want to use the same query with different variables inserted into the query. You can have your method return the ResultSet like it currently does and pass the variables in as parameters to the method.
then you can put the parameters into the query inside of the method.
public ResultSet getResult(String param1, String param2){
statement = conn.prepareStatement(yourQuery);// conn must be an open connection
statement.setString(1,param1);
statement.setString(2,param2);
ResultSet rs = statement.execusteQuery();
return rs;
}
That's a basic example of how you might do something like that if I understood your question correctly.
what you are missing is the concept of Database connection pooling, you should never be instantiaing connections directly, the pool controls the database connections under the covers. You can look at my open source project SQL Construction Kit for some inspiration on one light weight way to deal with JDBC and how to build dynamic SQL statements using Factory and Builder patterns.

Overhead with Microsoft JDBC driver when executing a stored procedure

I am using Microsoft JDBC Driver 2.0 with SQL Server 2005. To explain my question better, let me start with a sample code to call a stored procedure.
public static void executeSproc(Connection con)
{
CallableStatement cstmt = con.prepareCall("{call dbo.getEmployeeManagers(?)}");
cstmt.setInt(1, 50);
ResultSet rs = cstmt.executeQuery();
while (rs.next()) {
// print results in the result set
}
rs.close();
cstmt.close();
}
Using SQL Profiler I see that the JDBC driver generates the following sql statements to make the call -
declare #P1 int
set #P1=1
exec sp_prepexec #P1 output, N'#P0 int', N'EXEC getEmployeeManagers #P0', 50
select #P1
So this means when I execute a stored procedure using a CallableStatement, the sp_prepexec
statement is called. And later when I close the statement, the sp_unprepare
is called. This seems to be the default behavior of the JDBC driver. The
problem is, the overhead to generate a prepared statement and then close it
has performance impact. Is there a way for the driver to execute the stored
procedure directly? Why can't the driver just do this -
exec getEmployeeManagers #P0=50
Try using the jTDS driver for SQLServer. I use it at work and it seems to be a lot better than the driver provided by MS.

Categories

Resources