how to print Statement (CallableStatement) in Java? - java

How can I print this OracleCallableStatement ?
ocstmt = (OracleCallableStatement) connection.prepareCall("{?= call
package.method(id => ?, name=>?)}");
ocstmt.registerOutParameter(1, OracleTypes.CURSOR);
ocstmt.setInt(2, obj.getId());
ocstmt.setString(3, obj.getName());
ocstmt.execute();
resultSet = ocstmt.getCursor(1);
What I mean is how can I know that what query goes to the database, how can I print the query? because sometimes it gives me error like "Wrong type" that is why I want to look at this query

Are you using log4j?
If so, add loggers for sql like below.
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
If you are using a ORM framework such as ibatis, you could add additional logger like below.
log4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=DEBUG

Yes, you can do this. You can either wrap your callable statement in a proxy that can substitute the actual values when you print it (and show the sql), or hunt around for a driver that has a meaningful toString. javaworld article There is also p6spy, and others.
Stored procedures are harder, but still doable.

You can't get the SQL by printing the Statement.
Is the example you posted one of the "sometimes" that triggers the error?
Why do you have to case this to an OracleCallableStatement? What part of the call is not the standard CallableStatement?

In general, use myObject.toString() to see what it prints. You may or may not be able to see the full query though. If you can't get it to go, the first thing that I would look at is the API documentation(javadocs for that Oracle library or driver that you're using)

I'm not sure if I understand the question, but it seems like you want to see this:
String sql = "{?= call package.method(id => ?, name=>?)}";
System.out.println(sql);
ocstmt = (OracleCallableStatement) connection.prepareCall(sql);
...

It's possible to use proxy jdbc driver to log all jdbc database actions.
this driver can prints all statements with values and all results.

My solution is use ProxyDataSourceBuilder(use it in Spring Boot project).
#Bean
public DataSource dataSource() {
SLF4JQueryLoggingListener loggingListener = new SLF4JQueryLoggingListener();
return ProxyDataSourceBuilder
.create(datasource)
.listener(loggingListener)
.build();
}
...
#Autowired
private DataSource dataSource;
Connection connection = dataSource.getConnection();
ocstmt = (OracleCallableStatement) connection.prepareCall("{?= call
package.method(id => ?, name=>?)}");
And just turn on logging in application.yml:
logging:
level:
net.ttddyy.dsproxy.listener.logging: debug

Try
> java -classpath ojdbc8.jar oracle.jdbc.driver.OracleSql false false "<your sql here>"
That will print the SQL that the driver sends to the database, among other things. This is not documented nor supported, but it's been around forever.

I thought it might be useful if you are looking whether executed query has value or not
System.out.println("value : "+CallableStatement.execute());
i.e The "false" returned by "CallableStatement.execute()" means that the JDBC statement didn't read any rows, (so there's no ResultSet to read). This is what you'd expect, as stored procedures don't directly return ResultSets, they only return values for any OUT or INOUT parameters.

Related

Apache Drill "limit 0" query while using Spring datasource

TL;DR
I have a Spring Boot application that makes use of parquet files stored on the file system. To access them we are using Apache Drill.
Since I have multiple users that might access them, I've set up a connection pool in Spring.
When I'm using the connection pool, Drill somehow executes a "limit 0" query before executing my actual query, and this affect performances. The same "limit 0" query is NOT executed when I run my queries through a simple Statement obtained from direct Connection.
This seems to be related to the fact that Spring JdbcTemplate makes use of PreparedStatements instead of simple Statements.
Is there a way to get rid of those "limit 0" queries?
-- Details --
The connection pool in the Spring configuration class looks like this:
#Bean
#ConfigurationProperties(prefix = "datasource.parquet")
#Qualifier("parquetDataSource")
public DataSource parquetDataSource() {
return DataSourceBuilder.create().build();
}
And the corresponding properties in the development profile YML file are:
datasource:
parquet:
url: jdbc:drill:drillbit=localhost:31010
jdbcUrl: jdbc:drill:drillbit=localhost:31010
jndiName: jdbc/app_parquet
driverClassName: org.apache.drill.jdbc.Driver
maximumPoolSize: 5
initialSize: 1
maxIdle: 10
maxActive: 20
validation-query: SELECT 1 FROM sys.version
test-on-borrow: true
When I execute a query using the JdbcTemplate created with the mentioned Drill DataSource, 3 different queries might be executed:
the validation query SELECT 1 FROM sys.version;
a "limit 0" query that looks like SELECT * FROM (<my actual query>) LIMIT 0;
my actual query.
Here's the execution code (parquetJdbcTemplate is an instance of a class that extends org.springframework.jdbc.core.JdbcTemplate):
parquetJdbcTemplate.query(sqlQuery, namedParameters,
resultSet -> {
MyResultSet result = new MyResultSet();
while (resultSet.next()) {
// populate the "result" object
}
return result;
});
Here's a screenshot from the Profile page of my Drill monitor:
The bottom query is the "limit 0" one, then in the middle you have the validation query and on top (even if the query is not shown) the actual query that returns the data I want.
As you can see, the "limit 0" query takes more than 1/3 of the entire execution time to run. The validation query is fine, since the execution time is negligible and it's needed to check the connection.
The fact is, when I execute the same query using a Connection through the Drill driver (thus, with no pool), I only see my actual query in the UI monitor:
public void executeQuery(String myQuery) {
Class.forName("org.apache.drill.jdbc.Driver");
Driver.load();
Connection connection = DriverManager.getConnection("jdbc:drill:drillbit=localhost:31010");
Statement st = connection.createStatement();
ResultSet resultSet = st.executeQuery(myQuery);
while (resultSet.next()) {
// do stuff
}
}
As you can see, the total execution time improves by a lot (~14 seconds instead of ~26), just because the "limit 0" query is not executed.
As far as I know, those "limit 0" queries are executed to validate and get information about the underlying schema of the parquet files. Is there a way to disable them while using the connection pool? I ideally would like to still use PreparedStatements over simple Statements, but I could switch to simple Statements if needed, because I have full control over those queries (so, no SQL injection should be possible unless someone hacks the deployed artifacts).
You are right Drill executes limit 0 prior prepared statements to get information about schema. I don't think there is a way to disable such behavior. Though I can recommend to enable planner.enable_limit0_optimization option which is false by default, this may speed limit 0 query execution. Another way to speed limit 0 queries is to indicate schema explicitly using casts through the view usage or directly in queries.
Regarding not showing query, I think this was fixed in the latest Drill version.

CallableStatement with parameter names on PostgreSQL

I've tried to call a stored procedure with parameter names specified, but the JDBC failed to accept the parameters. It says:
Method org.postgresql.jdbc4.Jdbc4CallableStatement.setObject(String,Object) is not yet implemented.
I use postgresql-9.2-1003.jdbc4
I there any other way to do this?
I know that I can just specify the sequence number. But I want to specify the parameter names as it is more convenient for me to do so.
My code:
String call_statement = "{ ? = call procedure_name(?, ?, ?) }";
CallableStatement proc = connection.prepareCall(call_statement);
proc.registerOutParameter(1, Types.OTHER);
proc.setObject("param1", 1);
proc.setObject("param2", "hello");
proc.setObject("param3", true);
proc.execute();
ResultSet result = (ResultSet)proc.getObject(1);
Unfortunately, using the parameter names is not a feature supported by the implementation of the JDBC 4 driver for the PostgreSQL database. See the code of this JDBC 4 implementation in GrepCode.
However, you can still continue to use an integer (variable or literal) to indicate the position of the parameter.
It's 2020 here and the standard open source JDBC driver for Postgres still doesn't support named parameter notation for CallableStatement.
Interestingly, EnterpriseDB driver does support it (with that said - I tried to use EDB JDBC driver - it indeed supports named parameters but it does so many things differently, if at all, that we ruled out this option entirly, for those other reasons)
A solution which worked for us - is to use this "hack" (pseudo-code, YMMV):
String sql = "SELECT * FROM PROC(IN_PARAM1 => ?, IN_PARAM2 => ?, IN_PARAM => ?)";
PreparedStatement ps = connection.prepareStatement(sql);
ps.setObject("IN_PARAM1", 1);
ps.setObject("IN_PARAM2", "hello");
ps.setObject("IN_PARAM3", true);
ps.execute();
ResultSet result = (ResultSet)ps.getObject(1);
The killer feature of this notation - is the ability to call SPs with optional params (it could be achieved by having optional ordinal params, but if you have more than a few of them - it becomes a nightmare, as one needs to pass so many nulls, that it's too easy to miscount, and those are very hard to spot)
There are also additional benefits, like ability to return multiple ResultSets (refcursors), ability to use maps as params, etc.
P.S.: we also use the same trick for Node.js with node-postgres - works well for years.

Generic query for both oracle and sql server databases?

I am working on a functionality where i need to check whether database is down or not. i have to use single query which works for both oracle and sqlserver dbs. is there any single query which checks whether db is up or not?
similar to select * from dual;
Thanks!
I'd go with a connection.setAutoCommit(true) and not with a select.
I think it would be best to use the Connection's function to check the server is available.
There should be an Exception when it fails to connect and you can check the state to see if it's still open.
To address your specific need, if you decide to continue with a query.
THIS IS NOT BEST PRACTICE
I don't know of a simple way to do what you wish, Oracle and SQL don't share the same naming for system objects. BUT run that command, it won't work on SQL, but the exception won't be of type 'Server is Down' and you can use it in your try/catch.
THIS IS NOT BEST PRACTICE
Hope it makes sense.
Better way is to obtain the connection and then use the database metadata information like the product version or product name to ensure the database is up or not.
Eg:
try{
Con = DriverManager.getConnection(databaseURL,username,password);
databasemetadata = con.getMetaData();
String databaseName = databasemetedata.getDatabaseProductName();
If(databaseName.equals("<desireddabase>"))
{
//database up and running
}
}
catch(Exception e)
{
// error in connection ...
}
The java.sql.Connection has a method
isValid(timeOut)
Returns true if the connection has not been closed and is still valid. The driver shall submit a query on the connection or use some other mechanism that positively verifies the connection is still valid when this method is called.

Query from PreparedStatement

Is there any way to get the Oracle query from PreparedStatement .I know its not possible as such I can't use log4jdbc or p6spy as it is a secured application and using this will create bigger problems..toString won't work as I am using Oracle? I can't change PreparedStatement to Statement either.
If only need for debug time then You can use DebuggableStatement follow this article
I don't think you should be doing it this way, as there is no officially documented API for this.
If you can mess with the code, why cannot you use log4jdbc ?
Oracle JDBC also supports java.util.logging, which you could try to enable.
If you are just interested in the SQL itself, you can turn on session tracing on the Oracle server.
Or maybe you can put your code to where the statement is being prepared (using something like #pinichi is suggesting)?
But just for fun, poking around with the debugger, with my version of Oracle JDBC, I can do
if (stmt instanceof oracle.jdbc.driver.OraclePreparedStatement) {
String x = ((oracle.jdbc.driver.OraclePreparedStatement) stmt)
.getOriginalSql();
System.out.println(x);
}
If you just want to check SQL statement you can also go straight to the database and check v$sql table.
There you can find all sqls and other information about query. More info: http://download.oracle.com/docs/cd/B19306_01/server.102/b14237/dynviews_2113.htm

Created a Java Web app/MySql app

Started coming up with a java web app for online user interaction. Decided to use a MySql DB for data storage. I have already created the tables with the proper/expected data types. My question is I always thought the next step would be to creat stored procedures like Search/Add/Delete/etc.. that the user could envoke from the page. So in my java code I could just call the procedure ex:
CallableStatement cs;
Try
{
String outParam = cs.getString(1); // OUT parameter
// Call a procedure with one in and out parameter
cs = connection.prepareCall("{call SearchIt(?)}");
cs.registerOutParameter(1, Types.VARCHAR);
cs.setString(1, "a string");
cs.execute();
outParam = cs.getString(1);
}
catch (SQLException e) {
}
but if my application was not in the need for stored procedures because the user actions would be simple enough to execute simple tedious queries. How could I set up my Java and Sql code to handle that. Could I just have the "Select" or "Update" statements in my code to manipulate the data in my MySQL DB. If so how would that syntax look like?
This URL has documentation on using prepared statements which is what you want to use to avoid security flaws (SQL Injection and such).
http://download.oracle.com/javase/tutorial/jdbc/basics/prepared.html
here's an example from that page
PreparedStatement updateSales = connection.prepareStatement(
"UPDATE COFFEES SET SALES = ? WHERE COF_NAME LIKE ? ");
updateSales.setInt(1, 75);
updateSales.setString(2, "Colombian");
updateSales.executeUpdate():
Just use Statement, or PreparedStatement.
http://download.oracle.com/javase/1.4.2/docs/api/java/sql/Statement.html
In a similar way to what you did, just call :
Statement stm = Connection.createStatement();
then execute your SQL :
stm.execute("SELECT * FROM MYTABLE");
grab the resultset and check out the results.
Beware though - this is bad bad as far as security goes - as others have mentioned, PreparedStatements are a bit more secure, but still not 100%.
To be honest, although basic JDBC is pretty simple, I really hate all the SQL strings littered around your code. If you want something a bit more elegant have a quick look at hibernate - it hides all the hackiness from you, and is also pretty easy to setup.

Categories