How can one get the output from Oracle's dbms_output.get_lines
in a Java app using JDBC without creating additional objects in the database?

- 34,542
- 16
- 106
- 137
-
this may help https://community.oracle.com/thread/104787 – Ravinder Reddy Dec 15 '17 at 10:33
-
@RavinderReddy No it doesn't. That post is ancient and no solution is provided there. – Axel Fontaine Dec 15 '17 at 10:40
-
You could do [this](https://stackoverflow.com/a/19143017/266304) (which requires a new type) or [this](https://stackoverflow.com/a/30406896/266304) (which uses built-in types); and then call one of those over JDBC? Or there's [this post from Tom Kyte](https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:45027262935845)? A logging table might be more appropriate. Depends exactly what you need to achieve. – Alex Poole Dec 15 '17 at 10:43
-
@AlexPoole Thanks. I just clarified the question by adding "without creating additional objects in the database" as those rights will most likely not have been granted. – Axel Fontaine Dec 15 '17 at 10:45
-
2Note that in recent Oracle versions, `dbms_output.get_lines` is overloaded and can return a `dbmsoutput_linesarray`, which should be more JDBC-friendly than the old `dbms_output.chararray`. – William Robertson Dec 15 '17 at 10:46
-
@WilliamRobertson I'll happily accept an answer with code that demonstrates that. – Axel Fontaine Dec 15 '17 at 10:47
-
I don't know Java I'm afraid, but it should just be a case of calling `dbms_output.get_lines` like any other procedure and getting the results back in a `dbmsoutput_linesarray`, which is a predefined public collection type. – William Robertson Dec 15 '17 at 10:52
1 Answers
I've also blogged about this issue here. Here's a snippet that illustrates how this can be done:
try (CallableStatement call = c.prepareCall(
"declare "
+ " num integer := 1000;" // Adapt this as needed
+ "begin "
// You have to enable buffering any server output that you may want to fetch
+ " dbms_output.enable();"
// This might as well be a call to third-party stored procedures, etc., whose
// output you want to capture
+ " dbms_output.put_line('abc');"
+ " dbms_output.put_line('hello');"
+ " dbms_output.put_line('so cool');"
// This is again your call here to capture the output up until now.
// The below fetching the PL/SQL TABLE type into a SQL cursor works with Oracle 12c.
// In an 11g version, you'd need an auxiliary SQL TABLE type
+ " dbms_output.get_lines(?, num);"
// Don't forget this or the buffer will overflow eventually
+ " dbms_output.disable();"
+ "end;"
)) {
call.registerOutParameter(1, Types.ARRAY, "DBMSOUTPUT_LINESARRAY");
call.execute();
Array array = null;
try {
array = call.getArray(1);
System.out.println(Arrays.asList((Object[]) array.getArray()));
}
finally {
if (array != null)
array.free();
}
}
The above will print:
[abc, hello, so cool, null]
Note that the ENABLE
/ DISABLE
setting is a connection wide setting, so you can also do this over several JDBC statements:
try (Connection c = DriverManager.getConnection(url, properties);
Statement s = c.createStatement()) {
try {
s.executeUpdate("begin dbms_output.enable(); end;");
s.executeUpdate("begin dbms_output.put_line('abc'); end;");
s.executeUpdate("begin dbms_output.put_line('hello'); end;");
s.executeUpdate("begin dbms_output.put_line('so cool'); end;");
try (CallableStatement call = c.prepareCall(
"declare "
+ " num integer := 1000;"
+ "begin "
+ " dbms_output.get_lines(?, num);"
+ "end;"
)) {
call.registerOutParameter(1, Types.ARRAY, "DBMSOUTPUT_LINESARRAY");
call.execute();
Array array = null;
try {
array = call.getArray(1);
System.out.println(Arrays.asList((Object[]) array.getArray()));
}
finally {
if (array != null)
array.free();
}
}
}
finally {
s.executeUpdate("begin dbms_output.disable(); end;");
}
}
Note also that this will fetch a fixed size of 1000 lines at most. You may need to loop in PL/SQL or poll the database if you want more lines.
Using jOOQ to do this
Note that if you're a jOOQ user, you can auto-fetch server output on your queries:
DSLContext ctx = DSL.using(c,
new Settings().withFetchServerOutputSize(10));
And then you should see some DEBUG
log output for FetchServerOutputListener
, e.g.
DEBUG [LoggerListener ] Executing query : begin my_procedure(1, 2); end;
DEBUG [FetchServerOutputListener] <output here>
In addition to the above DEBUG
log, you'll get the information in ExecuteContext::serverOutput
This is also mentioned in the linked blog post
A note on calling DBMS_OUTPUT.GET_LINE
instead
Previously, there was a now-deleted answer that suggested individual calls to DBMS_OUTPUT.GET_LINE
instead, which returns one line at a time. I've benchmarked the approach comparing it with DBMS_OUTPUT.GET_LINES
, and the differences are drastic - up to a factor 30x slower when called from JDBC (even if there's not really a big difference when calling the procedures from PL/SQL).
So, the bulk data transferring approach using DBMS_OUTPUT.GET_LINES
is definitely worth it. Here's a link to the benchmark:

- 211,314
- 129
- 689
- 1,509
-
Is this the standard way to call a procedure that returns a public SQL collection? Seems like that would be a routine thing to do in Java applications that interact with Oracle. – William Robertson Dec 15 '17 at 11:52
-
1@WilliamRobertson: You wouldn't believe how long Oracle has ignored that feature request! Apparently, Oracle 12cR2's JDBC driver can transmit PL/SQL TABLE types. I haven't played around with it yet, so I omitted it from the answer. Likewise, it took them *forever* to support the PL/SQL BOOLEAN type from JDBC (and you still can't use these types from ordinary SQL statements). Crazy! – Lukas Eder Dec 15 '17 at 12:08
-
But you don't need a PL/SQL table type. Surely JDBC can handle schema-level SQL collection types? – William Robertson Dec 15 '17 at 12:30
-
@WilliamRobertson: You probably mean `dbmsoutput_linesarray`. You're right. I missed the fact that this is a schema level type. Will fix my answer. – Lukas Eder Dec 15 '17 at 13:35
-
@StewAshton: See my updated answer. The type is not anonymous, we know it in this case, so the approach is very simple. Besides, Jpublisher is deprecated, which is why I came up with jOOQ some time ago ;-) (in fact, no, but it's a nice coincidence) – Lukas Eder Dec 15 '17 at 13:41
-
Updated answer looks good :) Yes I meant `dbmsoutput_linesarray` which is a `varray(2147483647) of varchar2(32767)` for some reason, but a convenient schema-level type nonetheless. It has been around for several releases but most Google searches find ancient implementations from before it existed. – William Robertson Dec 15 '17 at 14:05
-
1Updated answer looks great and confirmed to work with Oracle 10, 11 and 12. – Axel Fontaine Dec 15 '17 at 16:04
-
May be its obvious, but possible could save so troubles to others. If the database character set is not equal to the charset of Java (UTF-8) the jar `orai18n.jar` must be on the classpath, otherwise `???` ist shown instead of the strings. – Marmite Bomber Apr 09 '18 at 09:28
-
1
-
-
what if another process is using dbms_output? How to get only the output of my procedure? How **dbms_output.get_lines** filters output lines of my procedure? Thanks – JRichardsz Apr 29 '21 at 16:52
-