Overhead with Microsoft JDBC driver when executing a stored procedure - java

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.

Related

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);
}

Neo4j JDBC setting ResultSet type to something other than default

I am running a Neo4j server (2.0.3) and am accessing it via Cypher queries through the Neo4j JDBC. It works well for most of the uses I have had so far.
I currently want to run a query that will return a result set that is of the type - TYPE_SCROLL_SENSITIVE and not the default TYPE_FORWARD_ONLY.
Here is the code I use to create my Statement and return the ResultSet:
Statement all = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);
results = all.executeQuery(query);
However when I try and use the method beforeFirst() on the ResultSet I get an SQLException. Which is a result of either a closed ResultSet or one that is TYPE_FORWARD_ONLY. I have used the isClosed() method to ensure it is not closed.
Within the Neo4j JDBC Driver there is the following createStatement method:
#Override
public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException
{
return debug(new Neo4jStatement(this));
}
So from what I can see, it is simply ignoring the type and concurrency constants that are passed to it. I realize not all JDBC drivers allow for different type result sets. I guess I am asking why the Neo4j one does not? And if not, are there any plans to implement support for this? I would find it useful, and thought others might as well?

execute batch with oracle prepared statement

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();
}

different results from query in java code and on oracle db server: but no connection error

I have java code that connects to a remote oracle 11g EE db server. If i run a particular query in sqlplus it returns one result
SQL> SELECT COURSENAME from COURSES where skillID=1;
COURSENAME
--------------------
basic
But if I run the same query from the java code below it returns no results. I can copy the query syntax out of the query variable in the java debugger and running it on oracle so I know there is no syntax issue with the query. Also, it is not SQL exceptions or class not found exceptions so it seems to be running the query successfully -- just returning zero results.
What might be going on?
private String getCourseForSkill(int skillID){
try{
Class.forName("oracle.jdbc.OracleDriver");
String query="SELECT COURSENAME from COURSES where skillID=" + skillID ;
con = DriverManager.getConnection(url, user, password);
Statement stmt = con.createStatement();
rs = stmt.executeQuery(query);
rs.next();
return rs.getString("COURSENAME");
}
catch (ClassNotFoundException ex){
System.out.println(ex.getMessage());
}
catch (SQLException ex) {
System.out.println(ex.getMessage());
}
return null;
}
I think you're connecting to different Oracle instances, or more likely, as different Oracle users in the two cases
#GreyBeardedGeek the URL looks like "jdbc:oracle:thin:#website:port:orcl I get to the manual query by doing ssh#website, authenticating and then running command=sqlplus
Safer to run sqlplus <username>/<password>#<orainstancename>, because you can explicitly specify the oracle instance ID. In your case, it seems your program is using jdbc connection jdbc:oracle:thin:#website:port:orcl, so your orainstancename would be 'orcl' - just ensure that your tnsnames.ora file has the instance 'orcl' with the same 'port' as used by the jdbc connection
How to debug a little more
Run the following code:
con = DriverManager.getConnection(url, user, password);
con.setAutoCommit(false);
String insert="INSERT INTO COURSES (SKILLID, COURSE)"+ // can add other columns
"values (?, ?) );" // add ? for other columns
PreparedStatement ps = con.createPreparedStatement();
ps.setInt(1, 999);
ps.setString(2, "Complete Bullwarks");
// can set other columns
ps.executeUpdate(insert);
con.commit();
NOW connect manually, re-run your original select statement & see if the added row is there. If no error in java and no new row in Oracle: extremely likely you're using 2 different Oracle instances/schemas.
ALSO rerun your original java select code, but with SkillID=999 - extremely likely it will work.
Cheers
I had to do a commit to add the rows. When I typed commit; into the sql plus terminal then the remote jdbc connection could 'see' the rows. I am used to SQL server where you don't have to explicitly do these kinds of commits when using linq-to-sql or sql management studio.
It can be three issues.
1) skillID <> 1 in your Java code. Add debug and check.
2a) You are connecting to another database.
2b) You are connecting to the same database but SELECTING from a table in another schema.
To check 2a and 2b:
select user from dual; -- connect username
select name from v$database; -- database name
select host_name from v$instance; -- host name database is running on
This query returns all three into one result.
select user || '' || d.name || '' || i.host_name
from v$database d, v$instance i;
Assuming you are actually connecting to the same database this is caused by not committing the INSERT in the sql*plus connection.
Oracle by default does not run in auto-commit mode when connecting via OCI (which sql*plus uses to connect). Any DML(INSERT ...) executed in sql*plus will not be visible to any other session until it is committed. This is because Oracle provides a read committed isolation level by default. The only thing visible to other users across sessions are write locks.
It doesn't matter if you connect the second connection via JDBC or OCI, it won't see the changes till you commit the first connection.
To test this out try opening 2 sql*plus connections and run the following:
-- Executing DDL in Oracle causes an implicit commit before and after the
-- command so the second connection will see the existence of this table:
CREATE TABLE foobar ( x VARCHAR(1) );
Execute this in connection #1 - you should get zero (we haven't inserted anything yet):
SELECT COUNT(*) FROM foobar;
Execute this in connection #2:
INSERT INTO foobar ( x ) VALUES ( 'A' );
Execute this in connection #1 - you should still get zero (INSERT is not committed so connection #1 cannot see it):
SELECT COUNT(*) FROM foobar;
Execute this in connection #2:
COMMIT;
Execute this in connection #1 - you should get 1 (it's committed now):
SELECT COUNT(*) FROM foobar;

fastest way to query database in java

I have a MSSQL Database, and I have a stored procedure for any possible query, most of them just return a row of data with 3 columns or just execute an INSERT
How in java to connect to the DB and execute a stored procedure, and retrieve some data ?
A connection pool like DBCP makes a big difference. The connection time can be save this way.
Prepared statements can help the database to skip query parsing. The parsed statements will be cached.
Batch updates help when you're executing a statement repeatedly.
Setting the right fetch size is another optimization for queries.
Use the MSSQL JDBC Driver to create a connection to the database
In jdbc, you need to create a CallableStatement to execute the procedure. It's like this:
.
CallableStatement callable = null;
try {
String sqlCommand = "{call yourProcNameHere (?, ? /* ... */)}";
callable = conn.prepareCall(sqlCommand);
// ...
}
catch (SQLException e) {
// ...
}
finally {
/ ...
}
By reading and working through a JDBC Tutorial.

Categories

Resources