We just upgraded to DB2 10.5 from 9.5, this process was working fine until the upgrade was performed on the server. When i run the jar file from a linux server, I get the following error however when i run the exact same code from eclipse on my windows computer, it works just fine! I am also getting a similar error if I calll this sp from DB2 control center. I am looking to know what is causing this and how can i fix this error?
SQL4306N Java stored procedure or user-defined function "ESADBM.GETNEXTID",
specific name "WHDBRMM_UTILS" could not call Java method "GetNextID",
signature "(Ljava/lang/String;[I)V". SQLSTATE=42724
Explanation:
The Java method given by the EXTERNAL NAME clause of a CREATE PROCEDURE
or CREATE FUNCTION statement could not be found. Its declared argument
list may not match what the database expects, or it may not be a
"public" instance method.
User response:
Ensure that a Java instance method exists with the "public" flag and the
expected argument list for this call.
sqlcode: -4306
sqlstate: 42724.
Here is the code:
package pkgUtil_v4_0_0_0;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.hibernate.exception.JDBCConnectionException;
public class DBSequence {
public static final String SEQ_CONTACTID = "ContactIDSeq";
public static final String SEQ_PROJECTID = "ProjectIDSeq";
public static final String SEQ_LOCATIONID = "LocationIDSeq";
public static final String SEQ_SOURCEID = "SourceIDSeq";
public static final String SEQ_SURVEYID = "SurveyIDSeq";
public static final String SEQ_LOGICALSURVEYID = "WageAreaIDSeq";
public static final String SEQ_WAGEDETAILID = "WageDetailIDSeq";
public static final String SEQ_ORGID = "OrgIDSeq";
public static final String SEQ_OFFICEID = "RegionNumberSeq";
public static final String SEQ_LETTERID = "LetterIDSeq";
public static final String SEQ_DODGEID = "DodgeIDSeq";
public static final String SEQ_CRAFTID = "CraftIDSeq";
public static final String SEQ_CRAFTTITLEID = "CraftTitleIDSeq";
public static final String SEQ_ANALYSTID = "AnalystIDSeq";
public static final String SEQ_LETTERTEMPLATEID = "LetterTemplateIDSeq";
public static final String SEQ_RECRATESID = "RecRatesIDSeq";
public static final String SEQ_BRIDGESCDID = "BridgeSCDIDSeq";
public static String drvr = "";
public static Connection con = null;
// utility function
public static int getNextId(Connection lcon, String sequence) throws SQLException {
Boolean bFlag;
PreparedStatement stmt = null;
int id = 0;
String sql = "select next value for esadbm." +
sequence + " from SYSIBM.sysdummy1";
// System.out.println("String = "+sequence);
stmt = lcon.prepareStatement(sql);
ResultSet resultSet = stmt.executeQuery();
if (resultSet.next()) {
id = resultSet.getInt(1);
}
resultSet.close();
stmt.close();
return id;
}
// Stored Procedure Entry Point
public static void getNextId(String sequence, int[] seq) throws SQLException, Exception {
System.out.println("String = "+sequence);
System.out.println("Array = "+seq);
if (drvr.length() == 0) {
drvr = "jdbc:default:connection";
con = DriverManager.getConnection(drvr);
}
drvr = "";
seq[0] = getNextId(con, sequence);
con.close();
}
// test procedure
public static void main(String args[])throws SQLException, Exception {
try {
System.out.println("Connecting to DB " + args[0]);
Class.forName("com.ibm.db2.jcc.DB2Driver");
drvr = "jdbc:db2:" + args[0];
// System.out.println(drvr+args[1] + args[2]);
con = DriverManager.getConnection("jdbc:db2:" + args[0], args[1],args[2]);
// System.out.println(con);
System.out.println("DB Connection Successful");
con = DriverManager.getConnection(drvr, args[1], args[2]);
Statement st = con.createStatement();
String query = "set schema = 'ESADBM'";
st.execute(query);
System.out.println("Getting ID");
int id = getNextId(con, SEQ_SOURCEID);
System.out.println("Returned : " + Integer.toString(id));
}
catch (ClassNotFoundException cnfe) {
cnfe.printStackTrace();
}
catch (SQLException sqle) {
sqle.printStackTrace();
}
catch (JDBCConnectionException e) {
System.out.println("Unable to connect to database");
e.printStackTrace();
}
}
}
Here is the stored procedure:
CREATE PROCEDURE "ESADBM "."GETNEXTID"
(
IN SEQUENCE CHARACTER(40),
OUT ID INTEGER
)
DYNAMIC RESULT SETS 0
SPECIFIC WHDBRA_UTILS
EXTERNAL NAME 'pkgUtil_v4_0_0_0.DBSequence!getNextId()'
LANGUAGE JAVA
PARAMETER STYLE JAVA
NOT DETERMINISTIC
FENCED THREADSAFE
MODIFIES SQL DATA
NO DBINFO;
Libraries for external routines, including Java classes and JAR files for Java routines, must be present in a certain location in the DB2 instance directory. When you upgrade your DB2 version, a new instance is created, but those libraries are not copied automatically (which, by the way, makes sense as there is a good chance that they need to be rebuilt).
The error message indicates that the instance cannot find the Java class file that implements GETNEXTID -- that would be DBSequence.class. The class needs to be copied to the sqllib/function directory in the DB2 10.5 instance home on the database server, as explained in the manual. You will probably need also to create pkgUtil_v4_0_0_0 under sqllib/function for the correct package structure. Make sure you compile the Java source using the same JDK version as the one used by the DB2 instance to run the program.
Once you do that, execute CALL SQLJ.REFRESH_CLASSES() in the DB2 client of your choice to make sure DB2 reloads the most current version. After that your stored procedure should work correctly.
Having said that, I don't really understand why you use such a convoluted way of retrieving a SQL sequence value.
my code is:
public AppMetaDBO getAppMetaRecord() throws SQLException {
JdbcOperations jdbcOperations = new JdbcTemplate(dataSource);
String query = "SELECT * FROM " + TABLE_APP_META;
logger.config("Executing SQL query:[" + query + "]");
return jdbcOperations.queryForObject(query, new AppMetaRowMapper());
}
Where AppMetaRowMapper is:
public class AppMetaRowMapper implements org.springframework.jdbc.core.RowMapper<AppMetaDBO> {
#Override
public AppMetaDBO mapRow(ResultSet resultSet, int i) throws SQLException {
return new AppMetaDBO(resultSet.getDouble(COL_LAST_SUPPORTED_VER));
}
}
And AppMetaDBO is simply:
#Data
#AllArgsConstructor
public class AppMetaDBO implements Serializable {
private double last_supported_version;
}
This throws a EmptyResultDataAccessExceptioneven though the DB table looks like this:
It is important to note that the same server is able to write to the db with no issues, and even read from a different table with no issues.
The code that reads without issues is very similar and works fine:
UserDBO result = null;
try {
JdbcOperations jdbcOperations = new JdbcTemplate(dataSource);
String query = "SELECT *" + " FROM " + TABLE_USERS + " WHERE " + COL_UID + "=" + quote(uid);
logger.config("Executing SQL query:[" + query + "]");
result = jdbcOperations.queryForObject(query, new UserDboRowMapper());
} catch(EmptyResultDataAccessException ignored) {}
return result;
Also important to note that the db server is not on a remote machine. In the local machine these issues are non-existent.
Please help I have absolutely no idea what I am doing wrong.
Thanks in advance.
The result should be like [1,2,3,4],
but i get garbled result like this
[com.test.db.Network#383c7b61, com.test.db.Network#7f87898,
com.test.db.Network#6b93f47a, com.test.db.Network#50fb09cc]
Below is my related class
private static void doLocationList(PrintWriter responseOut) throws Exception
{
//---testing show network ID list
int x=0;
Network network = new Network();
responseOut.println("this is I: " +network.getNetworkID(x));
......
}
This is from another class
public static List<Network> getNetworkID(int networkID) throws Exception
{
List<Network> idList = new ArrayList<Network>();
Connection conn = getConnection();
PreparedStatement Statement = conn.prepareStatement("Select id from network");
ResultSet result = Statement.executeQuery();
while(result.next()) {
Network network = new Network();
network.setId(result.getInt("id"));
idList.add(network);
}
return idList;
}
Any idea? Please help.
If you want to print the list of objects directly, you need to override the toString() method in the Network class and specify how to print the values when an object of this class is printed directly.
The default toString() method is as shown in Object class docs. Have a look at this post about toString()
had several apps with jdbc and Oracle 10g. Now I´m changing the apps for use c3p0. But I have some problems working with Oracle types.
I Have this Oracle type:
CREATE OR REPLACE
TYPE DATAOBJ AS OBJECT
(
ID NUMBER,
NAME VARCHAR2(50)
)
And this Oracle function:
CREATE OR REPLACE FUNCTION F_IS_DATA_OBJECT (datar in DATAOBJ) RETURN varchar2 IS
tmpVar varchar2(150);
BEGIN
tmpVar := 'Data object:';
if datar.id is not null then
tmpVar := tmpVar || 'id=' || datar.ID;
end if;
if datar.name is not null then
tmpVar := tmpVar || 'name=' || datar.name;
end if;
return tmpVar;
EXCEPTION
WHEN NO_DATA_FOUND THEN
NULL;
WHEN OTHERS THEN
RAISE;
END F_IS_DATA_OBJECT;
then I have a app in Java with c3p0 with next classes:
Dataobj.class to represent the object type:
package c3p0pruebas.modelo;
import java.io.Serializable;
import java.sql.SQLData;
import java.sql.SQLException;
import java.sql.SQLInput;
import java.sql.SQLOutput;
public class Dataobj implements SQLData, Serializable {
private String name;
private Integer id;
public Dataobj() {
}
public String getSQLTypeName() {
return "DATAOBJ";
}
public void writeSQL(SQLOutput stream) throws SQLException {
stream.writeInt(id.intValue());
stream.writeString(name);
}
public void readSQL(SQLInput stream, String typeName) throws SQLException {
id = new Integer(stream.readInt());
name = stream.readString();
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
... and its gets and sets ....
And the main class and main method:
Connection connection = DBConnectionManager.getInstance().getConnection("Mypool"); //I use a class to get connection
CallableStatement cs = null;
String error = "";
try {
/*
//First I made a NativeExtractor of the connection, but the result is the same, I got it from Spring framework.
//C3P0NativeJdbcExtractor extractor = new C3P0NativeJdbcExtractor();
//OracleConnection newConnection = (OracleConnection) extractor.getNativeConnection(connection);
//cs = (OracleCallableStatement) newConnection.prepareCall("{? = call F_IS_DATA_OBJECT(?)}");
*/
//Creates the object
Dataobj obj = new Dataobj();
obj.setId(new Integer(33));
obj.setName("myName");
cs = connection.prepareCall("{? = call F_IS_DATA_OBJECT(?)}");
cs.registerOutParameter(1, OracleTypes.VARCHAR);
cs.setObject(2, obj);
cs.execute();
error = cs.getString(1);
System.out.println("Result: " + error);
} catch (SQLException e) {
e.printStackTrace();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
closeDBObjects(null,cs,null);
}
closeDBObjects(null, null, connection); //Close connection
The execution gets:
Data object: id=33.
I cant get the String (Varchar2) value, the name string.
With oracle arrays of object type, I have the same problem, It worked nice with JDBC. When I worked with Arrays, also, it hasn´t the string values:
//Here I use a NativeConnection ...
Dataobj arrayOfData[] = new Dataobj[myDataObj.size()];
... //Makes the array of DataObj.
ArrayDescriptor descriptor = ArrayDescriptor.createDescriptor("OBJ_ARRAY", newConnection);
ARRAY arrayDatas = new ARRAY(descriptor, newConnection, arrayOfData);
//In this step, objects of arrayDatas haven´t the name string...
Thanks!!!
OK, It finally works.
Searching, We found out the answer:
We change data definition in the database and now it works:
CREATE OR REPLACE
TYPE "DATAOBJ" AS OBJECT
(
vid NUMBER,
vname NCHAR(50)
)
Thanks!
I had the same problem and i solved without change VARCHAR2 to NCHAR, because for me, the NCHAR doesn't appear the String in the Oracle, stay "?" in all the positions.
I changed the oracle driver of the WAR to the version of my database, in my case was 11.2.0.1.0:
http://www.oracle.com/technetwork/database/enterprise-edition/jdbc-112010-090769.html
And i put another driver, that is the NLS for Oracle Objects and Collections:
http://download.oracle.com/otn/utilities_drivers/jdbc/112/orai18n.jar
With this, i solved the problem and the VARCHAR2 worked fine.
Good luck.
I am new to apache derby and I cant seem to make work
CREATE TABLE IF NOT EXISTS table1 ...
as can be achieved in MySql etc. I am getting a 'Syntax error: Encountered "NOT" at line 1, column 17.', when I try to run this SQL statement in my Java program.
I checked in the documentation page for Derby Db Create Statements, but couldn't find such an alternative.
Create the table, catch the SQLException and check SQL status code.
The full list of error codes can be found here but I couldn't find Table <value> already exists; it's probably X0Y68. The code you need is X0Y32.
Just run the code once and print the error code. Don't forget to add a test to make sure the code works; this way, you can catch changes in the error code (should not happen ...).
In my projects, I usually add a helper class with static methods so I can write:
} catch( SQLException e ) {
if( DerbyHelper.tableAlreadyExists( e ) ) {
return; // That's OK
}
throw e;
}
Another option is to run a SELECT against the table and check the status code (which should be 42X05). But that's a second command you need to send and it doesn't offer any additional information.
What's worse, it can fail for other reasons than "Table doesn't exist", so the "create-and-ignore-error" is better IMO.
Derby does not support that sql-statement.
In my program I parse all the Tables from the Database into a Set and check if the table exists there.
Like this:
private Set<String> getDBTables(Connection targetDBConn) throws SQLException
{
Set<String> set = new HashSet<String>();
DatabaseMetaData dbmeta = targetDBConn.getMetaData();
readDBTable(set, dbmeta, "TABLE", null);
readDBTable(set, dbmeta, "VIEW", null);
return set;
}
private void readDBTable(Set<String> set, DatabaseMetaData dbmeta, String searchCriteria, String schema)
throws SQLException
{
ResultSet rs = dbmeta.getTables(null, schema, null, new String[]
{ searchCriteria });
while (rs.next())
{
set.add(rs.getString("TABLE_NAME").toLowerCase());
}
}
the query you are executing does not supported by Derby db. Instead, if you know the name of the table you can find if table exists or not quite easily.
public boolean isTableExist(String sTablename) throws SQLException{
if(connection!=null)
{
DatabaseMetaData dbmd = connection.getMetaData();
ResultSet rs = dbmd.getTables(null, null, sTablename.toUpperCase(),null);
if(rs.next())
{
System.out.println("Table "+rs.getString("TABLE_NAME")+"already exists !!");
}
else
{
System.out.println("Write your create table function here !!!");
}
return true;
}
return false;
}
Catch is to specify name of the table in Uppercase else you won't be able to find table name in metadata.
to check if table is exist :
Connection con = DriverManager.getConnection(url);
ResultSet res = con.getMetaData().getTables(null, Schema_Name, table_name.toUpperCase(), null);//Default schema name is "APP"
if(res.next())
{
//do some thing;
}else{
JOptionPane.showMessageDialog(null, table_name +" not exist");
}
to show all tables name :
Connection con = DriverManager.getConnection(url);
ResultSet res = con.getMetaData().getTables(null, Schema_Name, "%", null);//Default schema name is "APP"
while(res.next())
{
JOptionPane.showMessageDialog(null, res.getString(3) + " is exist");//Show table name
}else{
JOptionPane.showMessageDialog(null, table_name +" not exist");
}
Following Aaron Digulla's lead with a DerbyUtils class to check if the table exists, this is the solution I came up with :
Calling class
public void createTable(String name) {
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
connection = daoFactory.getConnection();
String sql = String.format(SQL_CREATE_TABLE, name);
preparedStatement = connection.prepareStatement(sql, Statement.NO_GENERATED_KEYS);
preparedStatement.execute();
} catch (SQLException e) {
if(DerbyUtils.tableAlreadyExists(e)) { //check if the exception is because of pre-existing table.
logger.info("Talbe " + name + " already exists. No need to recreate");
} else {
logger.error(e.getMessage() + " : " + e.getStackTrace());
}
} finally {
close(connection, preparedStatement); //DAOUtils silently closes
}
}
DerbyUtils
public class DerbyUtils {
public DerbyUtils() {
//empty constructor -- helper class
}
public static boolean tableAlreadyExists(SQLException e) {
boolean exists;
if(e.getSQLState().equals("X0Y32")) {
exists = true;
} else {
exists = false;
}
return exists;
}
}
See also
https://db.apache.org/derby/docs/10.2/ref/rrefexcept71493.html
I know this was marked with an answer but in case anyone wanted another way of checking I wanted to post anyway. Here I check the table metadata with a method that returns a boolean, true if exists, false if it doesn't. Hope it helps others if they are looking.
private static Connection conn = null;
private static Statement st = null;
private static ResultSet rs = null;
private static DatabaseMetaData dmd;
public Boolean firstTime()
{
try
{
dmd = conn.getMetaData();
rs = dmd.getTables(null, "APP", "LOGIN", null);
return !rs.next();
} catch (SQLException ex)
{
Logger.getLogger(Database.class.getName()).log(Level.SEVERE, null, ex);
return false;
}
}
Another solution with 2 conditions:
Willing to drop table before creating each time, with the same being present in a .sql file
Are using Spring and hence willing to use spring-test as a Maven dependency, your life can become much simpler with it's #Sql annotation
So, firstly adding this as a dependency to your pom:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.2.5.RELEASE</version>
<scope>test</scope>
</dependency>
Secondly, assuming you have an sql that drops, creates table a in a file
rectangle.sql:
DROP TABLE rectangles;
CREATE TABLE rectangles (
id INTEGER NOT NULL PRIMARY KEY,
width INTEGER NOT NULL,
height INTEGER NOT NULL
);
And you have a test class BlahTest that should run this sql before doing whatever test it is to run, simply add the following #Sql annotation to your class:
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.jdbc.SqlConfig;
import org.springframework.test.context.jdbc.SqlConfig.ErrorMode;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes=XyzClientConfig.class)
#Sql(scripts="/sql/ddl/rectangle.sql", config=#SqlConfig (errorMode=ErrorMode.IGNORE_FAILED_DROPS))
public class BlahTest {
...
}
The specified config attribute value's #SqlConfig has the magic that makes it skip the drop statement errors in case the table doesn't exist. I believe it's been written to specifically target these types of databases that don't support IF EXISTS for dropping / table creation (which derby really should, even if it's not part of the SQL standard at the moment)
This answer is way late, but it might be helpful for someone.
The following Java (standard JDBC) code can be used to check whether a table exists or not, and if it does then it can be created;
String query = "SELECT TRUE FROM SYS.SYSTABLES WHERE TABLENAME = ? AND TABLETYPE = 'T'"; // Leave TABLETYPE out if you don't care about it
PreparedStatement ps = connection.prepareStatement(query);
ps.setString(1, "TABLE_NAME"); // Must be in capitals
ResultSet rs = ps.executeQuery();
if ( rs.next() && rs.getBoolean(1) )
{
// Table exists
}
else
{
// Table does NOT exist ... create it
}
Here is a solution that will you can script in SQL.
Create a Class like the following:
package user.fenris.spring.extensions;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.SingleConnectionDataSource;
public class SqlCreateIfNotExists {
private static Log log = LogFactory.getLog(SqlCreateIfNotExists.class);
public static void createTableIfNotExists(String tablename, String ddl) throws SQLException {
Connection conn = DriverManager.getConnection("jdbc:default:connection");
if (conn != null) {
JdbcTemplate template = new JdbcTemplate(new SingleConnectionDataSource(conn, true));
int count = template.queryForInt("select count(*) from SYS.SYSTABLES where TABLENAME = ?", tablename);
log.debug("Count: " + count);
if (count == 0) {
log.debug("Executing sql statement: " + ddl);
template.execute(sql);
} else {
log.debug("Table exists. Skipping sql execution...");
}
}
}
}
Note: you don't have to use spring, you can write it in straight JDBC, but then you have to know how to do it correctly. (Left as an exercise for the reader). Also, you could rewrite this to parse out the table name from the ddl parameter. Another thing would be to do proper error handling.
Make sure the class is compiled and placed in the classpath of the VM the database will be running in.
Write your SQL script:
-- 2K for ddl statement should be enough. You want more? Seriously?
create procedure CreateTableIfNotExists(in tablename varchar(128), in ddl varchar(2048))
PARAMETER STYLE JAVA
MODIFIES SQL DATA
language java
external name 'user.fenris.spring.extensions.SqlCreateIfNotExists.createTableIfNotExists';
call CreateTableIfNotExists('TABLE_NAME_MUST_BE_ALL_CAPS',
'create table TABLE_NAME_MUST_BE_ALL_CAPS
(entry_id int generated always as identity not null,
entry_timestamp timestamp,
username varchar(128) not null,
note varchar(1024) not null,
primary key (entry_id))');
-- you don't have to drop this, but you would have to create a similar
-- procedure to create the CreateTableIfNotExists procedure,
-- (i.e. CreateProcedureIfNotExists) but then it's turtles all the way down
drop procedure CreateIfNotExists;
???
profit
try {
connection.createStatement().execute("create table channels(channel varchar(20),topic varchar(20))");
} catch (Exception e) {
// TODO Auto-generated catch block
// e.printStackTrace();
}
Surround the create statement by try-catch.and make sure comment the e.printstacktace();
if it is already exists it does not show error ,otherwise it create table..!!