JDBC Oracle stored procedure ref_cursor using vertx - java

I am trying to read an Oracle stored procedure returning a ref_cursor using vertx3. The same procedure is working if I edit it to return clob and use JDBCType.CLOB but for some reason I have to use ref_cursor. Can someone please help me?
JDBCClient client = JDBCClient.createShared(vertx, new JsonObject()
.put("url", "jdbc:oracle:thin:#localhost:8787:TEST")
.put("driver_class", "oracle.jdbc.OracleDriver")
.put("user", "user")
.put("password", "****"));
client.getConnection( connection -> {
if (connection.succeeded()) {
SQLConnection con = connection.result();
JsonObject params = new JsonObject()
.put("query", "{ call ? := package.procedure(?) }")
.put("paramsIn", new JsonArray().addNull().add(89))
.put("paramsOut", new JsonArray().add(JDBCType.REF_CURSOR));
con.callWithParams(params.getString("query"), params.getJsonArray("paramsIn"), params.getJsonArray("paramsOut"), query -> {
if(query.succeeded()){
ResultSet rs = query.result();
System.out.println(rs.toJson().toString())
}else{
System.out.println(req.body() + query.cause().toString());
}
});
} else {
System.out.println(connection.cause().toString())
}
});
and I get the error :
{ call ? := package.procedure(?) } java.sql.SQLException: Type de
colonne non valide: 2012

As of Vert.x 3.4.1, cursors are not supported. As a workaround, you could create your own javax.sql.DataSource and use it with Vertx.executeBlocking to invoke a JDBC java.sql.CallableStatement.
For the rest of your queries you can still use Vert.x APIs by creating a JDBCClient instance from your javax.sql.DataSource. This will avoid to maintain two different connection pools.

Have you tried using oracle.jdbc.OracleTypes.CURSOR from com.oracle:ojdbc8:12.2.0.1 instead of JDBCType.REF_CURSOR?

Related

How to get a oracle.jdbc.OracleConnection from an oracle.ucp.jdbc.PoolDataSource

Since ConnectionCaching ist deprecated in ojdbc now, i would like to use the Oracle Universal Connection Pool.
The Problem here is that i need to to obtain a oracle.jdbc.OracleConnection from it. I tried casting and unwrapping but i does not work. How do i get one?
I need the OracleConnection.createARRAY() Method to call a stored Procedure.
From the api docs, if you are getting an instance of UniversalPooledConnection the underlying connection can be obtained from getPhysicalConnection:
getPhysicalConnection
java.lang.Object getPhysicalConnection()
Gets the physical connection
that this UniversalPooledConnection decorates.
Returns: The physical
connection. Never null.
Thanks #6ton. I made it like this:
/* missing all error handling and resource management*/
universalConnectionPoolManager = UniversalConnectionPoolManagerImpl.getUniversalConnectionPoolManager();
poolDataSource = PoolDataSourceFactory.getPoolDataSource();
// setting up dataSource ....
poolDataSource.setConnectionPoolName(NAME);
universalConnectionPoolManager.createConnectionPool((UniversalConnectionPoolAdapter) poolDataSource);
universalConnectionPoolManager.startConnectionPool(NAME);
universalConnectionPool = universalConnectionPoolManager.getConnectionPool(NAME);
universalPooledConnection = universalConnectionPool.borrowConnection(universalConnectionPool.getConnectionRetrievalInfo());
physicalConnection = universalPooledConnection.getPhysicalConnection();
oracleConn = (OracleConnection) physicalConnection;
// ... query stuff
universalPooledConnection.heartbeat();
universalConnectionPool.returnConnection(universalPooledConnection);
universalConnectionPoolManager.destroyConnectionPool(NAME);
You can get the OracleConnection by using the unwrap method as shown below.
PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
pds.setURL("jdbc:oracle:thin:#(DESCRIPTION=(ADDRESS=(HOST=localhost)(PORT=1521)(PROTOCOL=tcp))(CONNECT_DATA=(SERVICE_NAME=myorcldbservice)))");
pds.setUser("hr");
pds.setPassword("hr");
pds.setConnectionPoolName("JDBC_UCP_POOL");
pds.setInitialPoolSize(5);
pds.setMinPoolSize(5);
pds.setMaxPoolSize(10);
try (Connection conn = pds.getConnection()) {
OracleConnection oc = conn.unwrap(OracleConnection.class);
} catch (SQLException e) {
System.out.println("UCPSample - SQLException occurred : " + e.getMessage());
}

createUserDefinedFunction : if already exists?

I'm using azure-documentdb java SDK in order to create and use "User Defined Functions (UDFs)"
So from the official documentation I finally find the way (with a Java client) on how to create an UDF:
String regexUdfJson = "{"
+ "id:\"REGEX_MATCH\","
+ "body:\"function (input, pattern) { return input.match(pattern) !== null; }\","
+ "}";
UserDefinedFunction udfREGEX = new UserDefinedFunction(regexUdfJson);
getDC().createUserDefinedFunction(
myCollection.getSelfLink(),
udfREGEX,
new RequestOptions());
And here is a sample query :
SELECT * FROM root r WHERE udf.REGEX_MATCH(r.name, "mytest_.*")
I had to create the UDF one time only because I got an exception if I try to recreate an existing UDF:
DocumentClientException: Message: {"Errors":["The input name presented is already taken. Ensure to provide a unique name property for this resource type."]}
How should I do to know if the UDF already exists ?
I try to use "readUserDefinedFunctions" function without success. Any example / other ideas ?
Maybe for the long term, should we suggest a "createOrReplaceUserDefinedFunction(...)" on azure feedback
You can check for existing UDFs by running query using queryUserDefinedFunctions.
Example:
List<UserDefinedFunction> udfs = client.queryUserDefinedFunctions(
myCollection.getSelfLink(),
new SqlQuerySpec("SELECT * FROM root r WHERE r.id=#id",
new SqlParameterCollection(new SqlParameter("#id", myUdfId))),
null).getQueryIterable().toList();
if (udfs.size() > 0) {
// Found UDF.
}
An answer for .NET users.
`var collectionAltLink = documentCollections["myCollection"].AltLink; // Target collection's AltLink
var udfLink = $"{collectionAltLink}/udfs/{sampleUdfId}"; // sampleUdfId is your UDF Id
var result = await _client.ReadUserDefinedFunctionAsync(udfLink);
var resource = result.Resource;
if (resource != null)
{
// The UDF with udfId exists
}`
Here _client is Azure's DocumentClient and documentCollections is a dictionary of your documentDb collections.
If there's no such UDF in the mentioned collection, the _client throws a NotFound exception.

Using MongoDb with Java Servlet

I am facing an issue with using Mongo DB on a Java servlet.
My servlet has many methods(~20) of accessing the database for retrieving and adding data. A very brief example of one :
public static String getSomething(String s) {
String json = "[]";
JSONArray jsonArray = new JSONArray();
DBCollection table;
try {
Mongo mongo = new Mongo("localhost", 27017);
DB db = mongo.getDB( "myDb" );
BasicDBObject quoteQuery = new BasicDBObject("abc", abc);
DBCursor cursor = table.find(quoteQuery);
try {
while(cursor.hasNext()) {
jsonArray.put(cursor.next());
}
} finally {
cursor.close();
}
// ...
Now the problem is when this Java servlet is deployed in the linux server, it works fine for 10 days or so.
After that it crashes.
When I go to mongodb.log in my var/log directory I get the following repetitive output:
"connection refused because too many open connections"
I am not sure on where to edit things now or how to deal with this. I have tried to grow the limit of open connections in the server but still have the same results.
Any suggestions?
from the API doc : http://api.mongodb.org/java/2.11.3/
public class Mongo extends Object
A database connection with internal connection pooling. For most applications, you should have one Mongo instance for the entire JVM.
You should create Mongo objects very sparingly, ideally even only one per classloader at any time. To reduce the number of Mongo objects you could create it in the servlet's init method and re-use that instance on every call.
EDIT: just had a look at our code, we manage the Mongo instance using a classic singleton class (and always fetch a Mongo using that class's getInstance() method) because if you have multiple servlets / entrypoints in your app just using init() will still generate one instance per servlet, and still won't satisfy the manual section cited by #FredClose
You may create the mongo object for once instead of creating it on each getSomething call.
public SomeClass{
static Mongo mongo = new Mongo("localhost", 27017);
static DB db = mongo.getDB( "myDb" );
public static String getSomething(String s) {
String json = "[]";
JSONArray jsonArray = new JSONArray();
DBCollection table;
try {
BasicDBObject quoteQuery = new BasicDBObject("abc", abc);
DBCursor cursor = table.find(quoteQuery);
while(cursor.hasNext()) {
jsonArray.put(cursor.next());
}
}
Actually the ideal case is not using static access at all and inject DB object from a central controller.
Your are creating connections in MongoDB, but you are not closing connections. For any database, it is very very important to close a connection, otherwise it will reach to its maximum limit and you wont be able to execute your program properly. Following code will be helpful i hope:
public static String getSomething(String s) {
String json = "[]";
JSONArray jsonArray = new JSONArray();
try {
MongoClient mongoClient = new MongoClient("localhost", 27017);
DB db = mongoClient.getDB("myDb");
DBCollection collection = db.getCollection("NAME OF YOUR COLLECTION");
BasicDBObject quoteQuery = new BasicDBObject("abc", "VARIABLE THAT YOU WANT TO FIND");
DBCursor cursor = collection.find(quoteQuery);
try {
while (cursor.hasNext()) {
jsonArray.put(cursor.next());
}
} finally {
cursor.close();
}
mongoClient.close();
} catch (Exception e) {
}
return jsonArray.toString();
}
In this code, 'MongoClient' is closed after its purpose is over.
Arun Gupta ‏#arungupta
New sample shows how to use Mongo within a #JavaEE7 app: New sample to show basic usage of Mongo in a Java EE application
As per above mentioned issue, its like you are creating the Mongo Object for every request.I will suggest to use the single Object through out your application.FOr this you can find the "MongoClient and connection pooling".
MongoClient will handle connection pooling for you automatically.
mongoClient = new MongoClient(URI, connectionOptions);
Here the mongoClient object holds your connection pool, and will give your app connections as needed. You should strive to create this object once as your application initializes and re-use this object throughout your application to talk to your database. The most common connection pooling problem we see results from applications that create a MongoClient object way too often, sometimes on each database request. If you do this you will not be using your connection pool as each MongoClient object maintains a separate pool that is not being reused by your application.

Connection cannot be cast to oracle.jdbc.OracleConnection

Why java.sql.Connection cannot be cast to oracle.jdbc.OracleConnection in code below?
My main goal is to pass to Oracle connection new user name and save it in 'SESSION' table in for example 'osuser' column because I want to trace in DB user changes and display it in the table.
#Repository
public class AuditLogDAOImpl implements AuditLogDAO {
#PersistenceContext(unitName="myUnitName")
EntityManager em;
#Resource(name = "dataSource")
DataSource dataSource;
public void init() {
try {
Connection connection = DataSourceUtils.getConnection(dataSource);
OracleConnection oracleConnection = (OracleConnection) connection; //Here I got cast exception!
String metrics[] = new String[OracleConnection.END_TO_END_STATE_INDEX_MAX];
metrics[OracleConnection.END_TO_END_CLIENTID_INDEX] = "my_new_username";
oracleConnection.setEndToEndMetrics(metrics, (short) 0);
java.util.Properties props = new java.util.Properties();
props.put("osuser", "newValue");
oracleConnection.setClientInfo(props);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
Here is error log:
10:42:29,251 INFO [STDOUT] org.jboss.resource.adapter.jdbc.jdk6.WrappedConnectionJDK6#bcc8cb
10:42:51,701 ERROR [STDERR] java.lang.ClassCastException: $Proxy286 cannot be cast to oracle.jdbc.OracleConnection
Generally I have 2 problem in this case:
why cast from Connection to OracleConnection fails and
what is the best way to implement my intend (I mean set the new user name to v$session.osuser in Oracle DB?
I work with Oracle 11g, Hibernate (using entity manager), data source via jndi.
Please help, thanks!
EDIT:
After some improvement the problem with casting still exists.
Improvement:
Connection connection = DataSourceUtils.getConnection(dataSource);
connection = ((org.jboss.resource.adapter.jdbc.WrappedConnection)connection).getUnderlyingConnection();
OracleConnection oracleConnection = (OracleConnection) connection;
Error:
java.lang.ClassCastException: $Proxy287 cannot be cast to org.jboss.resource.adapter.jdbc.WrappedConnection
The connection you are retrieving is probably a wrapped connection.
If you really need to get the underlying Oracle connection you should use:
if (connection.isWrapperFor(OracleConnection.class)){
OracleConnection oracleConnection= connection.unwrap(OracleConnection.class);
}else{
// recover, not an oracle connection
}
The isWrapperFor and unwrap methods are available since Java 1.6, and should be meaningfully implemented by the A/S connection wrappers.
The connection pool usually has a wrapper around the real connection instance, that's why your cast fails.
What you are doing wouldn't work anyway, because the parameters in the properties instance are only checked when the connection is established. As you have a connection that is already active, it won't change anything.
You need tou use DBMS_APPLICATION_INFO.SET_CLIENT_INFO() in order to change this for an existing connection.
This is just for people who come here via search on how to set metrics in OracleConnection, I spend great deal of time on this, so might help someone.
After you get your "connection" this should work:
DatabaseMetaData dmd = connection.getMetaData();
Connection metaDataConnection = null;
if(dmd != null)
{
metaDataConnection = dmd.getConnection();
}
if(!(metaDataConnection instanceof OracleConnection))
{
log.error("Connection is not instance of OracleConnection, returning");
return; /* Not connection u want */
}
OracleConnection oraConnection = (OracleConnection)metaDataConnection;
String[] metrics = new String[END_TO_END_STATE_INDEX_MAX]; // Do the rest below...
It works for me for OracleConnection, but I face diff issue when setting metrics:
short zero = 0;
oraConnection.setEndToEndMetrics(metrics, zero);
After proxying connection via my method where I set metrics few times, I get:
java.sql.SQLRecoverableException: No more data to read from socket
But I think it has to do with some Spring wiring inits or connection pool.
i had faced this issue when using spring to get connections. Typically , each layer adds a wrapper over the basic classes. i had just done connection.getClass().getName() to see the runtime type of the connection being retuned. It will be a Wrapper/proxy over which you can easily find the method to get the base OracleConnection type.
Try the following
I had encountered the same issue. We were using spring and it has a class called
NativeJdbcExtractor. It has many implementations and the following one works for TomCat. There is a specific implementation for Jboss called the JBossNativeJdbcExtractor
<bean id="jdbcExtractor" class="org.springframework.jdbc.support.nativejdbc.CommonsDbcpNativeJdbcExtractor"></bean>
In your DAO you can inject the bean and use the following method
protected NativeJdbcExtractor jdbcExtractor;
Connection conn=jdbcExtractor.getNativeConnection(oracleConnection);
You can access the inner OracleObject inside a Wrapper, in this case the wrapper
type is NewProxyConnection:
( I've used it in my project, it Worked ...no mistery, just use reflection)
Field[] fieldsConn= connection.getClass().getDeclaredFields();
Object innerConnObject = getFieldByName(fieldsConn,"inner").get(connection);
if(innerConnObject instanceof OracleConnection ){
OracleConnection oracleConn = (OracleConnection)innerConnObject;
//OracleConnection unwrap = ((OracleConnection)innerConnObject).unwrap();
// now you have the OracleObject that the Wrapper
}
//Method: Set properties of the ooject accessible.
public static Field getFieldByName(Field[] campos, String name) {
Field f = null;
for (Field campo : campos) {
campo.setAccessible(true);
if (campo.getName().equals(name)) {
f = campo;
break;
}
}
return f;
}
Not sure if my situation is related, but with my project, simply changing a database configuration setting actually causes unwrap to fail!
I'm using the Play framework with Scala; this works for me only when logSql=false:
db.withConnection { implicit c =>
val oracleConnection = c.unwrap(classOf[OracleConnection])
}
(this is just the Scala version of unwrapping an OracleConnection)
When I set logSql=true, I get:
com.sun.proxy.$Proxy17 cannot be cast to oracle.jdbc.OracleConnection
java.lang.ClassCastException: com.sun.proxy.$Proxy17 cannot be cast to
oracle.jdbc.OracleConnection
So something about the logSql configuration can actually cause unwrap to fail. No idea why.
With either configuration, my connection object is:
HikariProxyConnection#1880261898 wrapping
oracle.jdbc.driver.T4CConnection#6b28f065
isWrapperFor(OracleConnection) is true in both cases
This happens with Hikari Connection Pool and Bone CP. Maybe it's a bug in Oracle JDBC?
Oracle JDBC Driver version according to MANIFEST.MF
Implementation-Version: 11.2.0.3.0
Repository-Id: JAVAVM_11.2.0.4.0_LINUX.X64_130711
After trial and error.
This way works:
DelegatingConnection delConnection = new DelegatingConnection(dbcpConnection);
oraConnection = (oracle.jdbc.OracleConnection)delConnection.getInnermostDelegate();
But this way returned a null pointer for oraConnection:
DelegatingConnection delConnection = (DelegatingConnection) dbcpConnection;
oraConnection = (oracle.jdbc.OracleConnection)delConnection.getInnermostDelegate();
The following worked to get around AQ's TopicConnection.getTopicSession => JMS-112
//DEBUG: Native DataSource : weblogic.jdbc.common.internal.RmiDataSource
con = DataSource.getConnection();
debug("Generic SQL Connection: " + con.toString());
//DEBUG: Generic Connection: weblogic.jdbc.wrapper.PoolConnection_oracle_jdbc_driver_T4CConnection
if (con != null && con.isWrapperFor(OracleConnection.class)) {
WebLogicNativeJdbcExtractor wlne = new WebLogicNativeJdbcExtractor();//org.springframework to the rescue!!
java.sql.Connection nativeCon = wlne.getNativeConnection(con);
this.oraConnection = (OracleConnection) nativeCon;
debug("Unwrapp SQL Connection: " + this.oraConnection.toString());
}
//DEBUG: Native Connection: oracle.jdbc.driver.T4CConnection è
Now I could use this in the AQ-Factory w/o JMS-112
try casting like below
WrappedConnectionJDK6 wc = (WrappedConnectionJDK6) connection;
connection = wc.getUnderlyingConnection();

EclipseLink - Oracle Stored Procedure call newbie problem

I'm in charge of a task which I believe should be simple, but as I never did it before I have some trouble with it. I have succesfully created an EJB 3 project using EclipseLink which will call a number of stored procedures from an Oracle database. I've configured the datasource correctly, I can connect and execute simple stored procedures and functions (without parameters and returning a cursor); however, I am currently unable to execute stored procedures with parameters.
I'm using the EclipseLink wiki as a reference http://wiki.eclipse.org/Using_Basic_Query_API_(ELUG) .
The code is:
StoredProcedureCall call = new StoredProcedureCall();
call.setProcedureName("p_environment.startSession");
List<String[]> args = new ArrayList<String[]>();
args.add(new String[] { "user", "ae01403" });
args.add(new String[] { "application", "app_code" });
args.add(new String[] { "locale", "it_IT" });
for (String[] pair : args) {
call.addNamedArgumentValue(pair[0], pair[1]);
}
DataReadQuery query = new DataReadQuery(call);
for (String[] pair : args) {
query.addArgument(pair[0]);
}
Based on database documentation, as I have no access to the database itself, the procedure takes 3 VARCHAR IN parameters of the specified name. I then call the executeQuery method on the active session, but receive the "Wrong type or number of arguments" error. What am I doing wrong? Any help is appreciated.
EDIT: The stored procedure signature, as per documentation, is:
p_environment.startSession(as_user IN VARCHAR2,
as_application IN VARCHAR2,
as_locale IN VARCHAR2);
Many thanks!
In your code, call.addNamedArgumentValue(pair[0], pair[1]); sounds strange.
Don't you need to do this instead ?
call.addNamedArgument(procedureParameterName, argumentFieldName, argumentType);
It enables the mapping between your stored procedure parameters and the args you want to use.
For example, for you, it gives :
call.addNamedArgument(procedureUserParameterName, "user", String.class);
call.addNamedArgument(procedureUserParameterName, "application", String.class);
call.addNamedArgument(procedureUserParameterName, "locale", String.class);
You keep this :
DataReadQuery query = new DataReadQuery(call);
for (String[] pair : args) {
query.addArgument(pair[0]);
}
Then to call your Stored Procedure :
Session session = jpaEntityManager.getActiveSession();
List args = new ArrayList();
args.add(“ae01403”);
args.add(“app_code”);
args.add(“it_IT”);
List results = (List) session.executeQuery(query, args);

Categories

Resources