Calling Oracle "DEFINE" from Java - java

I need to define some variables in Oracle to be used further down our application's database installation scripts. Basically, the way our installer works now is it reads in the script files and calls each one via JDBC in Java. I need to have Oracle do the variable substitution on the database side, as there are procedures, triggers, create statements, etc. that will need to refer to them (So like "CREATE TABLE &&MYSCHEMA.TBL_NAME ...").
The problem I am having is that the DEFINE statement is throwing an error when calling it from Java (example):
private static void testDefineVariables() {
String url = "jdbc:oracle:thin:#localhost:1521:LOCALDEV";
String username = "SYSTEM";
String password = "manager42";
Connection conn = null;
Statement stmt = null;
try {
conn = DriverManager.getConnection(url, username, password);
stmt = conn.createStatement();
//Execute the sql
stmt.execute("DEFINE MYSCHEMA='TESTSCHEMA'");
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally {
try {
if(stmt != null)
stmt.close();
if(conn != null)
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
The error is:
java.sql.SQLSyntaxErrorException: ORA-00900: invalid SQL statement
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:439)
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:395)
at oracle.jdbc.driver.T4C8Oall.processError(T4C8Oall.java:802)
at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:436)
at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:186)
at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:521)
at oracle.jdbc.driver.T4CStatement.doOall8(T4CStatement.java:194)
at oracle.jdbc.driver.T4CStatement.executeForRows(T4CStatement.java:1000)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1307)
at oracle.jdbc.driver.OracleStatement.executeInternal(OracleStatement.java:1882)
at oracle.jdbc.driver.OracleStatement.execute(OracleStatement.java:1847)
at oracle.jdbc.driver.OracleStatementWrapper.execute(OracleStatementWrapper.java:301)
I am using an Oracle 11g R2 database with the ojdbc6-11.2.0.1.0.jar JDBC driver. I am able to execute the statement successfully from the sqldeveloper tool as well as the SQLPlus console, but not from the Java application. Do I need to configure additional properties on the database driver? Can I make this type of call from this driver?
I know some people right off the bat may suggest using variable bindings on the Java side, but this is not an option. The scripts need to be executable from both the SQL interface and the installer. There are other reasons apart from this one, which I won't go into.
I am also hoping to get this to work with the sql-maven-plugin, but that may not be possible based on this JIRA:
Add Oracle SQLPlus syntax for substitution variables
If anyone has any suggestions or know how to get this to work, I would greatly appreciate it.

I don't think DEFINE will work outside of SQLPLUS - JAVA uses JDBC
and assumes the argument to execute() is valid SQL. If you're able to
use DEFINE outside SQLPLUS it means the utility you're using is
intended to be compatible with or a partial replacement of SQLPLUS.
DEFINE is an SQLPLUS command - SQLPLUS is an ORACLE utility.
According to this URL Define is not an SQL statement
http://www.adp-gmbh.ch/ora/sqlplus/define.html

Both DEFINE and the substitution variable syntax &&MYSCHEMA.TBL_NAME are SQL*Plus commands. They are not valid SQL or PL/SQL constructs. You won't be able to use them as-is via JDBC.
Depending on the reasons that you say you don't want to go into
Your Java application can call out to the operating system to invoke SQL*Plus and pass in the script
Your Java application can implement whatever SQL*Plus features your scripts rely on. Rather than executing DEFINE MYSCHEMA='TESTSCHEMA', your application would need to maintain, say, a HashMap that maps a variable like myschema to a value like TESTSCHEMA. Your application would then have to parse the individual SQL statements looking for text like &&MYSCHEMA, replace that with the value from its local HashMap, and then send the resulting SQL string to the database server. Depending on how much of the SQL*Plus functionality you need to replicate, that could be a reasonably significant undertaking. Many PL/SQL IDEs (such as Toad or SQL Developer) implement a subset of SQL*Plus's functionality-- I don't know of any that have tried to implement every last SQL*Plus feature.

Related

iseries - java external stored procedure returning result sets

I am trying to build a SP to return a result set from a remote iSeries and I just can't seem to do it. I can return result sets from the local database, but not remote when I use JT400native.jar (also tried just the jt400.jar) to connect. Anyone have any idea what I am doing wrong?
My SP is defined as this.
CREATE PROCEDURE MYLIB.TEST
(INOUT I INTEGER)
EXTERNAL NAME 'jproc1.returnTEST'
PARAMETER STYLE JAVA
MODIFIES SQL DATA
DYNAMIC RESULT SETS 1
LANGUAGE JAVA
Here the java code behind it which works, it will return result sets from the local database to the client (run sql scripts in iNavigator)
import java.sql.*;
import com.ibm.db2.app.*;
public class jproc1 {
public static void returnTEST(int[] myInputInteger, ResultSet[] myResultSet) throws Exception {
Connection con = DriverManager.getConnection("jdbc:default:connection");
Statement stmt1 = con.createStatement();
String sql1 = "select TEST FROM MYLIB.TEST";
myInputInteger[0] = 5;
myResultSet[0] = stmt1.executeQuery(sql1);
}
}
When I change the SP to to replace the connection with one to a remote iSeries it won't return the result set back to the client, it does however return the first out variable myInputInteger just fine. I believe I have everything setup correctly, I have all the Jars I need registered. The important part here is that internally to the java program I get the result set from the remote iSeries, I can loop through it, count it, dump it to the IFS, it just won't pass it back to the client (Run SQL Scripts in iNavigator).
import java.sql.*;
import com.ibm.db2.app.*;
public class jproc1 {
public static void returnTEST(int[] myInputInteger, ResultSet[] myResultSet) throws Exception {
Class.forName ("com.ibm.as400.access.AS400JDBCDriver").newInstance ();
String url = "jdbc:as400://remoteiseries;naming=sql;prompt=false;user=myuser;password=mypass;translate binary=true";
Connection con = DriverManager.getConnection(url);
Statement stmt1 = con.createStatement();
String sql1 = "select TEST FROM MYLIB.TEST";
myInputInteger[0] = 5
myResultSet[0] = stmt1.executeQuery(sql1);
}
}
So, what am I missing? I have tried a ton of variations including using DB2GENERAL parameter types (and corresponding changes to java program per chapter 7 on the DB2 UDB for iSeries manual). No matter what I do it won't return those remote result sets back to client, and I don't get any errors.
TIA.
What version of IBM i?
DB2 for IBM i has long been behind in its federation support...
Only recently, v7.1 TR 4, has it been possible to do a
insert into localtbl
select * from remotetbl
But you still can't do
select * from localtbl join remotetbl using (key)
As the system only really supports one DB connection at a time in a job.
Interestingly, this article gives a technique for using UDTF to get around the one connection limit. I don't know enough about the internals to understand why a UDTF would work but an SP wouldn't. But you might try changing from an SP to a UDTF.

HSQLDB over JDBC: execute SQL statements in batch

I need to initialize a database from my Java application. For reasons of code maintainability, I would like to maintain the SQL code separately from the Java code (it is currently in a separate source file).
The first few lines of the file are as follows:
-- 1 - Countries - COUNTRIES.DAT;
drop table Countries if exists;
create table Countries(
CID integer,
ECC varchar(2),
CCD varchar(1),
NAME varchar(50));
I read the SQL code from the file and store it in a string. Then I do:
PreparedStatement stmt = dbConnection.prepareStatement(sqlString);
This fails with the following exception:
java.sql.SQLSyntaxErrorException: unexpected token: CREATE : line: 2
This looks as if JDBC doesn't like multiple SQL statements in a single PreparedStatement. I have also tried CallableStatement and prepareCall(), with the same result.
Does JDBC provide a way to pass the entire SQL script in one go?
The JDBC standard (and the SQL standard for that matter) assumes a single statement per execute. Some drivers have an option to allow execution of multiple statements in one execute, but technically that option violates the JDBC standard. There is nothing in JDBC itself that supports multi-statement script execution.
You need to separate the statements yourself (on the ;), and execute them individually, or find a third-party tool that does this for you (eg MyBatis ScriptRunner).
You might also want to look at something like flyway or liquibase.
To run a hardcoded / loaded from file queries you can use execute like:
Statement stmt = null;
String query = "drop table Countries if exists;
create table Countries(
CID integer,
ECC varchar(2),
CCD varchar(1),
NAME varchar(50));";
try {
stmt = con.createStatement();
stmt.execute(query);
} catch (SQLException e ) {
JDBCTutorialUtilities.printSQLException(e);
} finally {
if (stmt != null) { stmt.close(); }
}
If you want to run dynamic queries for example to append values you have to use PreparedStatement. For running a query from a file it's not recommended to put dynamic queries in it.

How to "package" an SQL database

I am looking for a way to save an SQL database and then reference it by means other than localhost which would not work because it is being used on other computers.
I realize that my terminology may not be correct in asking for a means to "package" an SQL database however I am not very sure how to put my desire such a concise title.
I have a database that I created through mySQL here: http://gyazo.com/fcac155a60c0d2587442c3e4807ef98a
I can access this database with no problems through the following code...
try
{
//Get connection
Connection myConn = DriverManager.getConnection("jdbc:mysql://localhost:3306/term_database","root", "_cA\"#8X(XHm+++E");
//**********
//Connection myConn = DriverManager.getConnection("jdbc:mysql:translationDatabase","root", "_cA\"#8X(XHm+++E");
//**********
//create statement
Statement myStmt = myConn.createStatement();
//execute sql query
ResultSet myRs = myStmt.executeQuery("select * from terms WHERE idNumber=" +termNumber);
//process result set
while(myRs.next()){
term= (myRs.getString(language));
}
}
catch (Exception exc)
{
exc.printStackTrace();
}
However, I assume that my users will be on different computers and so a "//localhost" reference will not work. They do not have access to the internet either. So I aim to include the database in my program's files to be downloaded with the software or to include it in the jar. I was not able to find any means to do that online. The code I surrounded with *'s was an attempt to reference translationDatabase.sql which I saved through the program mySQL into my software's directory but it did not work as shown here: http://gyazo.com/e9d4339435dedecab4e7ad960e9b13b6
To recap: I am looking for a way to save an SQL database and then reference it by means other than localhost which would not work because it is being used on other computers.
The idiomatic terminology is "embedded" or "serverless" database.
There are several pure-java solutions. There is also the popular SQLite, which you can manipulate via its command line client, or via a third-party JDBC driver (example 1, example 2)
Any of the above solutions will require that you convert your existing MySQL database to the target system..
Alternatively, you may consider bundling your application with MySQL server (possibly with an automated installation process, so that installation is invisible to the end-user).

How to enter SQL instruction in controller with play 2 and java

I'm developing a web application with Play 2.1.0 and programming it with Java and I need to have access to data already saved in a DB to modify them.
I tried to create a new instance without the new operator and reference it to my object saved in the database, but even if there is no pointer error, it won't change values of attributes. I couldn't figure out why, so I've decided to enter SQL queries directly.
Same thing, it does not seems to have any mistake, but it won't change anything... I think this comes from a bad link to the database :
Here is my code in application.java :
public static Result modifyQuestionnaire(Long id) throws SQLException {
Statement stmt = null;
Connection con = DB.getConnection();
try {
stmt = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
String query = "SELECT * FROM WOQ.questionnaire WHERE id=id";
ResultSet uprs = stmt.executeQuery(query);
uprs.updateString("name", "baba");
uprs.updateRow();
} catch(Exception e) {
e.printStackTrace();
} finally {
if (stmt!=null) {
stmt.close();
}
}
return redirect(routes.Application.questionnaire(id));
}
And I also try to enter an UPDATE query directly, still the same..
I've looked everywhere and did not find any solution (except Anorm but it seems to work with Scala language)
Btw, if anyone knows a solution with a second instance that refers to the same object (it seems possible but as I say, there is no error but no actions neither), it's fine for me.
Huh, you showed as that you are trying to create totally new connection, so I supposed, that you don't want to use Ebean, but in case when you are already use it, you can just use its methods for the task:
(copied) There are some options in Ebean's API, so you should check it and choose one:
Update<T> - check in the sample for #NamedUpdates annotation
Ebean.createUpdate(beanType, updStatement)
SqlUpdate - you can just perform raw SQL update, without need for giving the entity type

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.

Categories