oracle 11g resultSet how to get the table name - java

I found there is some problem in Oracle 11g to get table name from interface(ResultSet.getMetaData().getTableName(int column));
It always show the empty string.
Is there something wrong for oracle database or jdbc driver? If the jdbc driver's problem , Can I get another jdbc driver to resolve this issue?
Thanks in advance!

According to the documentation this is not supported:
but does not implement the getSchemaName and getTableName methods because Oracle Database does not make this feasible
Earlier Oracle drivers did have this feature, but it needed to be enabled explicitly because of its performance implications. As far as I can tell from the documentation this is no longer available in more recent drivers.

Please look into the following http://docs.oracle.com/javase/7/docs/api/java/sql/ResultSetMetaData.html

You could use:
DatabaseMetaData metadata = currentConnection.getMetaData();
String[] names = {"TABLE"};
ResultSet tables = metadata.getTables(null,"%", "%", names);
while (tables.next()) {
String tableName = tables.getString("TABLE_NAME");
String tableSchema = tables.getString("TABLE_SCHEM");
}
ResultSet columns = metadata.getColumns(null, "%", tableName, "%");
while (columns.next()) {
String columnName = columns.getString("COLUMN_NAME");
String datatype = columns.getString("TYPE_NAME");
int datasize = columns.getInt("COLUMN_SIZE");
int nullable = columns.getInt("NULLABLE");
}
Read this for more info.

after running into this specifical issue face-first a few days ago, I finally came up with a solution that does the job. Of course it's neither pretty nor... well... anything, but it works.
Basically, I check every table in my database against the columns in the ResultSet.
I hope someone else can use this. Took me about a day to get this straight.
Note: I use a CachedRowSet instead of a ResultSet, which doesn't require me to keep the database connection open the whole time.
private static String getTableNameByCols(ResultSetMetaData rsmd, DatabaseMetaData dbmd) throws SQLException{
String errorString = "No matching table found for the given column Set";
String ret = null, origColName, origDatatype, tableName;
String[] names = {"TABLE"};
ResultSet tables = dbmd.getTables(null, username, "%", names);
// get all the columns out of the rsmd and put them into an Array
Integer numberOfColumns = rsmd.getColumnCount();
String[] origColNames = new String[numberOfColumns+1];
String[] origColTypeNames = new String[numberOfColumns+1];
for (int i=1; i<numberOfColumns+1; i++){
origColNames[i] = rsmd.getColumnName(i);
origColTypeNames[i] = rsmd.getColumnTypeName(i);
}
ResultSet columns = null;
while (tables.next()) {
tableName = tables.getString("TABLE_NAME");
columns = dbmd.getColumns(null, null, tableName, null);
CachedRowSet crs = new CachedRowSetImpl();
crs.populate(columns);
Integer tablesNumberOfColumns = crs.size();
int i = 1;
if (numberOfColumns.intValue() == tablesNumberOfColumns.intValue()){
while (crs.next()) {
origColName = origColNames[i];
origDatatype = origColTypeNames[i];
String colName = crs.getString(4);
String datatype = crs.getString(6);
//int datasize = columns.getInt("COLUMN_SIZE");
//int nullable = columns.getInt("NULLABLE");
if (origColName.equals(colName) && origDatatype.equals(datatype) ){
ret = tableName;
} else {
ret = null;
}
i += 1;
} // looked at all the columns
crs.close();
}// same # of columns check over
if (ret != null) {
break;
}
columns.close();
}
verify(ret, errorString);
return ret;
}
The surrounding method:
private static boolean updateLocalTable(ResultSet rs){
ResultSetMetaData rsmd;
DatabaseMetaData dbmd;
String table_name;
boolean ret = false;
try {
rsmd = rs.getMetaData();
dbmd = conn.getMetaData();
table_name = getTableNameByCols(rsmd, dbmd);
/* ... do stuff with it ... */
} catch (Exception e) {
print("kablooey! \n" + e.getStackTrace());
}
return ret;
}

Related

How to get the unique columns from a given table?

Lets say I have a table called "person" and it has 4 columns (name, last_name, city, pincode). Two of those columns (name, last_name) are unique columns. I want to get these two columns from the JDBC driver. How can I get this information from a JDBC driver?
You can find information about unique columns (defined as constraints or unique indexes), using DatabaseMetaData.getIndexInfo, passing true for the fourth parameter (unique).
For example
DatabaseMetaData md = connection.getMetaData();
// NOTE: You may need to use "PERSON"
try (ResultSet rs = md.getIndexInfo(null, null, "person", true, true)) {
while (rs.next()) {
String indexName = rs.getString("INDEX_NAME");
String columnName = rs.getString("COLUMN_NAME");
if (indexName == null || columnName == null) {
continue;
}
System.out.printf("%s: %s%n", indexName, columnName);
}
}
Here you have a simple example based on MySql:
public class Test {
static final String DB_URL = "jdbc:mysql://localhost:3306/yourDatabase";
static final String USER = "----";
static final String PASS = "somePass";
static final String QUERY = "SHOW KEYS FROM YOUR_TABLE WHERE Key_name = 'PRIMARY'\n";
public static void main(String[] args) {
// Open a connection
final ArrayList<String> primaryKeyColumns = new ArrayList<>();
try(Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(QUERY);) {
while (rs.next()) {
primaryKeyColumns.add(rs.getString(5));
}
} catch (SQLException e) {
e.printStackTrace();
}
System.out.println(primaryKeyColumns);
}
}
Look at the used query in this way you can fetch more specific information.

How do I get the last inserted primary key? [duplicate]

I want to INSERT a record in a database (which is Microsoft SQL Server in my case) using JDBC in Java. At the same time, I want to obtain the insert ID. How can I achieve this using JDBC API?
If it is an auto generated key, then you can use Statement#getGeneratedKeys() for this. You need to call it on the same Statement as the one being used for the INSERT. You first need to create the statement using Statement.RETURN_GENERATED_KEYS to notify the JDBC driver to return the keys.
Here's a basic example:
public void create(User user) throws SQLException {
try (
Connection connection = dataSource.getConnection();
PreparedStatement statement = connection.prepareStatement(SQL_INSERT,
Statement.RETURN_GENERATED_KEYS);
) {
statement.setString(1, user.getName());
statement.setString(2, user.getPassword());
statement.setString(3, user.getEmail());
// ...
int affectedRows = statement.executeUpdate();
if (affectedRows == 0) {
throw new SQLException("Creating user failed, no rows affected.");
}
try (ResultSet generatedKeys = statement.getGeneratedKeys()) {
if (generatedKeys.next()) {
user.setId(generatedKeys.getLong(1));
}
else {
throw new SQLException("Creating user failed, no ID obtained.");
}
}
}
}
Note that you're dependent on the JDBC driver as to whether it works. Currently, most of the last versions will work, but if I am correct, Oracle JDBC driver is still somewhat troublesome with this. MySQL and DB2 already supported it for ages. PostgreSQL started to support it not long ago. I can't comment about MSSQL as I've never used it.
For Oracle, you can invoke a CallableStatement with a RETURNING clause or a SELECT CURRVAL(sequencename) (or whatever DB-specific syntax to do so) directly after the INSERT in the same transaction to obtain the last generated key. See also this answer.
Create Generated Column
String generatedColumns[] = { "ID" };
Pass this geneated Column to your statement
PreparedStatement stmtInsert = conn.prepareStatement(insertSQL, generatedColumns);
Use ResultSet object to fetch the GeneratedKeys on Statement
ResultSet rs = stmtInsert.getGeneratedKeys();
if (rs.next()) {
long id = rs.getLong(1);
System.out.println("Inserted ID -" + id); // display inserted record
}
When encountering an 'Unsupported feature' error while using Statement.RETURN_GENERATED_KEYS, try this:
String[] returnId = { "BATCHID" };
String sql = "INSERT INTO BATCH (BATCHNAME) VALUES ('aaaaaaa')";
PreparedStatement statement = connection.prepareStatement(sql, returnId);
int affectedRows = statement.executeUpdate();
if (affectedRows == 0) {
throw new SQLException("Creating user failed, no rows affected.");
}
try (ResultSet rs = statement.getGeneratedKeys()) {
if (rs.next()) {
System.out.println(rs.getInt(1));
}
rs.close();
}
Where BATCHID is the auto generated id.
I'm hitting Microsoft SQL Server 2008 R2 from a single-threaded JDBC-based application and pulling back the last ID without using the RETURN_GENERATED_KEYS property or any PreparedStatement. Looks something like this:
private int insertQueryReturnInt(String SQLQy) {
ResultSet generatedKeys = null;
int generatedKey = -1;
try {
Statement statement = conn.createStatement();
statement.execute(SQLQy);
} catch (Exception e) {
errorDescription = "Failed to insert SQL query: " + SQLQy + "( " + e.toString() + ")";
return -1;
}
try {
generatedKey = Integer.parseInt(readOneValue("SELECT ##IDENTITY"));
} catch (Exception e) {
errorDescription = "Failed to get ID of just-inserted SQL query: " + SQLQy + "( " + e.toString() + ")";
return -1;
}
return generatedKey;
}
This blog post nicely isolates three main SQL Server "last ID" options:
http://msjawahar.wordpress.com/2008/01/25/how-to-find-the-last-identity-value-inserted-in-the-sql-server/ - haven't needed the other two yet.
Instead of a comment, I just want to answer post.
Interface java.sql.PreparedStatement
columnIndexes « You can use prepareStatement function that accepts columnIndexes and SQL statement.
Where columnIndexes allowed constant flags are Statement.RETURN_GENERATED_KEYS1 or Statement.NO_GENERATED_KEYS[2], SQL statement that may contain one or more '?' IN parameter placeholders.
SYNTAX «
Connection.prepareStatement(String sql, int autoGeneratedKeys)
Connection.prepareStatement(String sql, int[] columnIndexes)
Example:
PreparedStatement pstmt =
conn.prepareStatement( insertSQL, Statement.RETURN_GENERATED_KEYS );
columnNames « List out the columnNames like 'id', 'uniqueID', .... in the target table that contain the auto-generated keys that should be returned. The driver will ignore them if the SQL statement is not an INSERT statement.
SYNTAX «
Connection.prepareStatement(String sql, String[] columnNames)
Example:
String columnNames[] = new String[] { "id" };
PreparedStatement pstmt = conn.prepareStatement( insertSQL, columnNames );
Full Example:
public static void insertAutoIncrement_SQL(String UserName, String Language, String Message) {
String DB_URL = "jdbc:mysql://localhost:3306/test", DB_User = "root", DB_Password = "";
String insertSQL = "INSERT INTO `unicodeinfo`( `UserName`, `Language`, `Message`) VALUES (?,?,?)";
//"INSERT INTO `unicodeinfo`(`id`, `UserName`, `Language`, `Message`) VALUES (?,?,?,?)";
int primkey = 0 ;
try {
Class.forName("com.mysql.jdbc.Driver").newInstance();
Connection conn = DriverManager.getConnection(DB_URL, DB_User, DB_Password);
String columnNames[] = new String[] { "id" };
PreparedStatement pstmt = conn.prepareStatement( insertSQL, columnNames );
pstmt.setString(1, UserName );
pstmt.setString(2, Language );
pstmt.setString(3, Message );
if (pstmt.executeUpdate() > 0) {
// Retrieves any auto-generated keys created as a result of executing this Statement object
java.sql.ResultSet generatedKeys = pstmt.getGeneratedKeys();
if ( generatedKeys.next() ) {
primkey = generatedKeys.getInt(1);
}
}
System.out.println("Record updated with id = "+primkey);
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
}
I'm using SQLServer 2008, but I have a development limitation: I cannot use a new driver for it, I have to use "com.microsoft.jdbc.sqlserver.SQLServerDriver" (I cannot use "com.microsoft.sqlserver.jdbc.SQLServerDriver").
That's why the solution conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS) threw a java.lang.AbstractMethodError for me.
In this situation, a possible solution I found is the old one suggested by Microsoft:
How To Retrieve ##IDENTITY Value Using JDBC
import java.sql.*;
import java.io.*;
public class IdentitySample
{
public static void main(String args[])
{
try
{
String URL = "jdbc:microsoft:sqlserver://yourServer:1433;databasename=pubs";
String userName = "yourUser";
String password = "yourPassword";
System.out.println( "Trying to connect to: " + URL);
//Register JDBC Driver
Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver").newInstance();
//Connect to SQL Server
Connection con = null;
con = DriverManager.getConnection(URL,userName,password);
System.out.println("Successfully connected to server");
//Create statement and Execute using either a stored procecure or batch statement
CallableStatement callstmt = null;
callstmt = con.prepareCall("INSERT INTO myIdentTable (col2) VALUES (?);SELECT ##IDENTITY");
callstmt.setString(1, "testInputBatch");
System.out.println("Batch statement successfully executed");
callstmt.execute();
int iUpdCount = callstmt.getUpdateCount();
boolean bMoreResults = true;
ResultSet rs = null;
int myIdentVal = -1; //to store the ##IDENTITY
//While there are still more results or update counts
//available, continue processing resultsets
while (bMoreResults || iUpdCount!=-1)
{
//NOTE: in order for output parameters to be available,
//all resultsets must be processed
rs = callstmt.getResultSet();
//if rs is not null, we know we can get the results from the SELECT ##IDENTITY
if (rs != null)
{
rs.next();
myIdentVal = rs.getInt(1);
}
//Do something with the results here (not shown)
//get the next resultset, if there is one
//this call also implicitly closes the previously obtained ResultSet
bMoreResults = callstmt.getMoreResults();
iUpdCount = callstmt.getUpdateCount();
}
System.out.println( "##IDENTITY is: " + myIdentVal);
//Close statement and connection
callstmt.close();
con.close();
}
catch (Exception ex)
{
ex.printStackTrace();
}
try
{
System.out.println("Press any key to quit...");
System.in.read();
}
catch (Exception e)
{
}
}
}
This solution worked for me!
I hope this helps!
You can use following java code to get new inserted id.
ps = con.prepareStatement(query, Statement.RETURN_GENERATED_KEYS);
ps.setInt(1, quizid);
ps.setInt(2, userid);
ps.executeUpdate();
ResultSet rs = ps.getGeneratedKeys();
if (rs.next()) {
lastInsertId = rs.getInt(1);
}
It is possible to use it with normal Statement's as well (not just PreparedStatement)
Statement statement = conn.createStatement();
int updateCount = statement.executeUpdate("insert into x...)", Statement.RETURN_GENERATED_KEYS);
try (ResultSet generatedKeys = statement.getGeneratedKeys()) {
if (generatedKeys.next()) {
return generatedKeys.getLong(1);
}
else {
throw new SQLException("Creating failed, no ID obtained.");
}
}
Most others have suggested to use JDBC API for this, but personally, I find it quite painful to do with most drivers. When in fact, you can just use a native T-SQL feature, the OUTPUT clause:
try (
Statement s = c.createStatement();
ResultSet rs = s.executeQuery(
"""
INSERT INTO t (a, b)
OUTPUT id
VALUES (1, 2)
"""
);
) {
while (rs.next())
System.out.println("ID = " + rs.getLong(1));
}
This is the simplest solution for SQL Server as well as a few other SQL dialects (e.g. Firebird, MariaDB, PostgreSQL, where you'd use RETURNING instead of OUTPUT).
I've blogged about this topic more in detail here.
With Hibernate's NativeQuery, you need to return a ResultList instead of a SingleResult, because Hibernate modifies a native query
INSERT INTO bla (a,b) VALUES (2,3) RETURNING id
like
INSERT INTO bla (a,b) VALUES (2,3) RETURNING id LIMIT 1
if you try to get a single result, which causes most databases (at least PostgreSQL) to throw a syntax error. Afterwards, you may fetch the resulting id from the list (which usually contains exactly one item).
In my case ->
ConnectionClass objConnectionClass=new ConnectionClass();
con=objConnectionClass.getDataBaseConnection();
pstmtGetAdd=con.prepareStatement(SQL_INSERT_ADDRESS_QUERY,Statement.RETURN_GENERATED_KEYS);
pstmtGetAdd.setString(1, objRegisterVO.getAddress());
pstmtGetAdd.setInt(2, Integer.parseInt(objRegisterVO.getCityId()));
int addId=pstmtGetAdd.executeUpdate();
if(addId>0)
{
ResultSet rsVal=pstmtGetAdd.getGeneratedKeys();
rsVal.next();
addId=rsVal.getInt(1);
}
If you are using Spring JDBC, you can use Spring's GeneratedKeyHolder class to get the inserted ID.
See this answer...
How to get inserted id using Spring Jdbctemplate.update(String sql, obj...args)
If you are using JDBC (tested with MySQL) and you just want the last inserted ID, there is an easy way to get it. The method I'm using is the following:
public static Integer insert(ConnectionImpl connection, String insertQuery){
Integer lastInsertId = -1;
try{
final PreparedStatement ps = connection.prepareStatement(insertQuery);
ps.executeUpdate(insertQuery);
final com.mysql.jdbc.PreparedStatement psFinal = (com.mysql.jdbc.PreparedStatement) ps;
lastInsertId = (int) psFinal.getLastInsertID();
connection.close();
} catch(SQLException ex){
System.err.println("Error: "+ex);
}
return lastInsertId;
}
Also, (and just in case) the method to get the ConnectionImpl is the following:
public static ConnectionImpl getConnectionImpl(){
ConnectionImpl conexion = null;
final String dbName = "database_name";
final String dbPort = "3306";
final String dbIPAddress = "127.0.0.1";
final String connectionPath = "jdbc:mysql://"+dbIPAddress+":"+dbPort+"/"+dbName+"?autoReconnect=true&useSSL=false";
final String dbUser = "database_user";
final String dbPassword = "database_password";
try{
conexion = (ConnectionImpl) DriverManager.getConnection(connectionPath, dbUser, dbPassword);
}catch(SQLException e){
System.err.println(e);
}
return conexion;
}
Remember to add the connector/J to the project referenced libraries.
In my case, the connector/J version is the 5.1.42. Maybe you will have to apply some changes to the connectionPath if you want to use a more modern version of the connector/J such as with the version 8.0.28.
In the file, remember to import the following resources:
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import com.mysql.jdbc.ConnectionImpl;
Hope this will be helpful.
Connection cn = DriverManager.getConnection("Host","user","pass");
Statement st = cn.createStatement("Ur Requet Sql");
int ret = st.execute();

How to check database for duplicates before inserting?

I would like to check the database for duplicates before inserting into the database. It is only considered a duplicate when plateNo, driverID and resDate match.
Here is how I get the data that will be inserted to the database
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
String client = (String) clientCmb.getSelectedItem();
String[] cparts = client.split("-");
String cpart = cparts[0];
String driver = (String) driverCmb.getSelectedItem();
String[] dparts = driver.split("-");
String dpart = dparts[0];
String van = (String) vanCmb.getSelectedItem();
java.util.Date oDate = jXDatePicker2.getDate();
DateFormat oDateFormat = new SimpleDateFormat("yyyy-MM-dd");
String date = oDateFormat.format(oDate);
model2.addRow(cpart, dpart, van, date);
}
And here's the code for my addRow method
public void addRow(String client, String driver, String van, String res){
try {
String sqlRes = "Select * from reservation";
rs = st.executeQuery(sqlRes);
rs.moveToInsertRow();
rs.updateString("clientID", client);
rs.updateString("plateNo", van);
rs.updateString("driverID", driver);
rs.updateString("resDate", res);
rs.insertRow();
rs.moveToCurrentRow();
rs = st.executeQuery(sqlRes);
this.fireTableDataChanged();
} catch (SQLException ex) {
Logger.getLogger(MyModel2.class.getName()).log(Level.SEVERE, null, ex);
}
Let the database do the work for you. Define a unique index/constraint specifying that those three values are unique in the table:
create unique index unq_reservation_3 on reservation(plateNo, driverID, resDate);
If you attempt to insert a duplicate -- or do an update that results in a duplicate -- then the database will return an error. You simply need to catch the error.
Use MERGE statement: T-SQL or ORACLE, or for MySQL:
PreparedStatement p = con.prepareStatement("
INSERT INTO reservation tgt (clientID, plateNo, driverID, resDate)
SELECT (? As clientID, ? As plateNo, ? As driverID, ? As resDate)
FROM DUAL ins
LEFT JOIN reservation ref
ON ref.resDate = ins.resDate
AND (ref.plateNo = ins.plateNo OR ref.driverID = ins.driverID)
WHERE ref.clientID IS NULL;
");
p.setString(1, client);
p.setString(2, van);
p.setString(3, driver);
p.setString(4, res);
return p.executeUpdate(); /* 1 - Success | 0 - Ignored Duplicate */

JDBC Metadata Fetch Decimal Precision

I am doing a metadata fetch from teradata using jdbc and I would like to get all the datatypes. However for DB fields containing DECIMAL(10,4) I am not able to get the precision. I am attaching my code snippet. Suggestions would be welcome!
DatabaseMetaData md = con.getMetaData();
ResultSet tbls = md.getTables(null, schema, name, null);
if(false == tbls.next()){
nonExistingRdbmsTables.add(name);
LOGGER.error("Table with name {} in schema {} does not exists in source database", name, schema);
continue;
}
RdbmsTableMetadata tableMD = new RdbmsTableMetadata();
List<String> pkList = new ArrayList();
ResultSet primaryKeys = md.getPrimaryKeys(null, schema, name);
while(primaryKeys.next()){
String pk = primaryKeys.getString("COLUMN_NAME");
pkList.add(pk);
}
tableMD.setPkList(pkList);
LOGGER.info("*****Primary key list for table {} is {}", name, pkList);
tableMD.setSchema(schema);
tableMD.setName(name);
tableMD.setTargetName(targetName);
tableMD.setTargetSchema(targetSchema);
List<Column> colList = new ArrayList<>();
ResultSet cols = md.getColumns(null, schema, name, null);
while (cols.next()) {
String columnName = cols.getString("COLUMN_NAME");
String columnType = cols.getString("TYPE_NAME");
String columnLength = cols.getString("COLUMN_SIZE");
}
Is there any way I can query the precision details from jdbc metadata?
For future referencers:-
int precision = cols.getInt("DECIMAL_DIGITS");
This worked fine! Thanks to alex and Mark.
ResultSetMetaData provides getScale(int col) and getPrecision(int col) methods. If those don't give you what you want, you're probably out of luck.

Delete all tables in Derby DB

How do i delete all the tables in the schema on Apache Derby DB using JDBC?
Thanks are due to the blog:
Step 1:
Run the SQL statement, but don't forget to replace the schema name 'APP' with your your schema name in the 2 occurrences below:
SELECT
'ALTER TABLE '||S.SCHEMANAME||'.'||T.TABLENAME||' DROP CONSTRAINT '||C.CONSTRAINTNAME||';'
FROM
SYS.SYSCONSTRAINTS C,
SYS.SYSSCHEMAS S,
SYS.SYSTABLES T
WHERE
C.SCHEMAID = S.SCHEMAID
AND
C.TABLEID = T.TABLEID
AND
S.SCHEMANAME = 'APP'
UNION
SELECT 'DROP TABLE ' || schemaname ||'.' || tablename || ';'
FROM SYS.SYSTABLES
INNER JOIN SYS.SYSSCHEMAS ON SYS.SYSTABLES.SCHEMAID = SYS.SYSSCHEMAS.SCHEMAID
where schemaname='APP';
Step 2:
The result of the above execution is a set of SQL statements, copy them to the SQL editor, execute them, then the constraints and the tables are dropped.
For actual code that does this, check CleanDatabaseTestSetup.java in the Derby test suite section of the Derby distribution.
Do a little method in java in which you execute a
DROP TABLE [tablename]
tablename is passed by parameter.
And another method in which you loop over a record set formed by the query
SELECT tablename FROM SYSTABLES
calling the first method.
Derby latest documentation
I think most db providers don't allow DROP TABLE * (or similar).
I think the best way would be to SHOW TABLES and then go through each deleting in a loop via a resultset.
HTH.
JDBC allows you to solve your task in a database agnostic way:
Open the connection
Grab the DatabaseMetaData
Use it to list all tables in your database JavaDoc
Iterate over the resultset and fire the DROP TABLE for each table
you must generate schema and table name from Derby DB system catalog.
Order all tables by relation.
Generate java statement for drop all tables
Use autoCommit() method and set this method to false. for manual commit or rollback transactions when got errors.
Run you java process.
Good Luck.
A simpler solution is to use JDBC to run "drop database foo" then "create database foo". However, this will cause all objects in the DB to be deleted (i.e. not just tables).
If you're working from the command prompt rather than through JDBC, this should get you started.
SELECT 'DROP TABLE ' || schemaname ||'.' || tablename || ';'
FROM SYS.SYSTABLES
INNER JOIN SYS.SYSSCHEMAS ON SYS.SYSTABLES.SCHEMAID = SYS.SYSSCHEMAS.SCHEMAID
;
A simple solution is to do right click -> disconnect then delete the folder containing your database and reconnect it.
Download Squirrel SQL from http://squirrel-sql.sourceforge.net/
Connect to the database.
Expand the TABLE node.
Select the tables that you want to drop.
Right click and select -> Scripts -> Drop table scripts
Run the generated queries
You can even select delete records to empty the selected tables.
For those wanting to delete all schemas programmatically without having to manually copy-paste SQL each time, here's code lifted from org.apache.derbyTesting.junit.CleanDatabaseTestSetup and org.apache.derbyTesting.junit.JDBC. You just call dropAllSchemas(connection);
public static void dropAllSchemas(Connection conn) throws SQLException {
DatabaseMetaData dmd = conn.getMetaData();
SQLException sqle = null;
// Loop a number of arbitary times to catch cases
// where objects are dependent on objects in
// different schemas.
for (int count = 0; count < 5; count++) {
// Fetch all the user schemas into a list
List<String> schemas = new ArrayList<String>();
ResultSet rs = dmd.getSchemas();
while (rs.next()) {
String schema = rs.getString("TABLE_SCHEM");
if (schema.startsWith("SYS"))
continue;
if (schema.equals("SQLJ"))
continue;
if (schema.equals("NULLID"))
continue;
schemas.add(schema);
}
rs.close();
// DROP all the user schemas.
sqle = null;
for (String schema : schemas) {
try {
dropSchema(dmd, schema);
} catch (SQLException e) {
sqle = e;
}
}
// No errors means all the schemas we wanted to
// drop were dropped, so nothing more to do.
if (sqle == null)
return;
}
throw sqle;
}
/**
* Constant to pass to DatabaseMetaData.getTables() to fetch
* just tables.
*/
public static final String[] GET_TABLES_TABLE = new String[] {"TABLE"};
/**
* Constant to pass to DatabaseMetaData.getTables() to fetch
* just views.
*/
public static final String[] GET_TABLES_VIEW = new String[] {"VIEW"};
/**
* Constant to pass to DatabaseMetaData.getTables() to fetch
* just synonyms.
*/
public static final String[] GET_TABLES_SYNONYM =
new String[] {"SYNONYM"};
/**
* Drop a database schema by dropping all objects in it
* and then executing DROP SCHEMA. If the schema is
* APP it is cleaned but DROP SCHEMA is not executed.
*
* TODO: Handle dependencies by looping in some intelligent
* way until everything can be dropped.
*
*
* #param dmd DatabaseMetaData object for database
* #param schema Name of the schema
* #throws SQLException database error
*/
public static void dropSchema(DatabaseMetaData dmd, String schema) throws SQLException{
Connection conn = dmd.getConnection();
Statement s = dmd.getConnection().createStatement();
// Triggers
PreparedStatement pstr = conn.prepareStatement(
"SELECT TRIGGERNAME FROM SYS.SYSSCHEMAS S, SYS.SYSTRIGGERS T "
+ "WHERE S.SCHEMAID = T.SCHEMAID AND SCHEMANAME = ?");
pstr.setString(1, schema);
ResultSet trrs = pstr.executeQuery();
while (trrs.next()) {
String trigger = trrs.getString(1);
s.execute("DROP TRIGGER " + escape(schema, trigger));
}
trrs.close();
pstr.close();
// Functions - not supported by JDBC meta data until JDBC 4
// Need to use the CHAR() function on A.ALIASTYPE
// so that the compare will work in any schema.
PreparedStatement psf = conn.prepareStatement(
"SELECT ALIAS FROM SYS.SYSALIASES A, SYS.SYSSCHEMAS S" +
" WHERE A.SCHEMAID = S.SCHEMAID " +
" AND CHAR(A.ALIASTYPE) = ? " +
" AND S.SCHEMANAME = ?");
psf.setString(1, "F" );
psf.setString(2, schema);
ResultSet rs = psf.executeQuery();
dropUsingDMD(s, rs, schema, "ALIAS", "FUNCTION");
// Procedures
rs = dmd.getProcedures((String) null,
schema, (String) null);
dropUsingDMD(s, rs, schema, "PROCEDURE_NAME", "PROCEDURE");
// Views
rs = dmd.getTables((String) null, schema, (String) null,
GET_TABLES_VIEW);
dropUsingDMD(s, rs, schema, "TABLE_NAME", "VIEW");
// Tables
rs = dmd.getTables((String) null, schema, (String) null,
GET_TABLES_TABLE);
dropUsingDMD(s, rs, schema, "TABLE_NAME", "TABLE");
// At this point there may be tables left due to
// foreign key constraints leading to a dependency loop.
// Drop any constraints that remain and then drop the tables.
// If there are no tables then this should be a quick no-op.
ResultSet table_rs = dmd.getTables((String) null, schema, (String) null,
GET_TABLES_TABLE);
while (table_rs.next()) {
String tablename = table_rs.getString("TABLE_NAME");
rs = dmd.getExportedKeys((String) null, schema, tablename);
while (rs.next()) {
short keyPosition = rs.getShort("KEY_SEQ");
if (keyPosition != 1)
continue;
String fkName = rs.getString("FK_NAME");
// No name, probably can't happen but couldn't drop it anyway.
if (fkName == null)
continue;
String fkSchema = rs.getString("FKTABLE_SCHEM");
String fkTable = rs.getString("FKTABLE_NAME");
String ddl = "ALTER TABLE " +
escape(fkSchema, fkTable) +
" DROP FOREIGN KEY " +
escape(fkName);
s.executeUpdate(ddl);
}
rs.close();
}
table_rs.close();
conn.commit();
// Tables (again)
rs = dmd.getTables((String) null, schema, (String) null,
GET_TABLES_TABLE);
dropUsingDMD(s, rs, schema, "TABLE_NAME", "TABLE");
// drop UDTs
psf.setString(1, "A" );
psf.setString(2, schema);
rs = psf.executeQuery();
dropUsingDMD(s, rs, schema, "ALIAS", "TYPE");
// drop aggregates
psf.setString(1, "G" );
psf.setString(2, schema);
rs = psf.executeQuery();
dropUsingDMD(s, rs, schema, "ALIAS", "DERBY AGGREGATE");
psf.close();
// Synonyms - need work around for DERBY-1790 where
// passing a table type of SYNONYM fails.
rs = dmd.getTables((String) null, schema, (String) null,
GET_TABLES_SYNONYM);
dropUsingDMD(s, rs, schema, "TABLE_NAME", "SYNONYM");
// sequences
if ( sysSequencesExists( conn ) )
{
psf = conn.prepareStatement
(
"SELECT SEQUENCENAME FROM SYS.SYSSEQUENCES A, SYS.SYSSCHEMAS S" +
" WHERE A.SCHEMAID = S.SCHEMAID " +
" AND S.SCHEMANAME = ?");
psf.setString(1, schema);
rs = psf.executeQuery();
dropUsingDMD(s, rs, schema, "SEQUENCENAME", "SEQUENCE");
psf.close();
}
// Finally drop the schema if it is not APP
if (!schema.equals("APP")) {
s.executeUpdate("DROP SCHEMA " + escape(schema) + " RESTRICT");
}
conn.commit();
s.close();
}
/**
* Return true if the SYSSEQUENCES table exists.
*/
private static boolean sysSequencesExists( Connection conn ) throws SQLException
{
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = conn.prepareStatement
(
"select count(*) from sys.systables t, sys.sysschemas s\n" +
"where t.schemaid = s.schemaid\n" +
"and ( cast(s.schemaname as varchar(128)))= 'SYS'\n" +
"and ( cast(t.tablename as varchar(128))) = 'SYSSEQUENCES'" );
rs = ps.executeQuery();
rs.next();
return ( rs.getInt( 1 ) > 0 );
}
finally
{
if ( rs != null ) { rs.close(); }
if ( ps != null ) { ps.close(); }
}
}
/**
* Escape a non-qualified name so that it is suitable
* for use in a SQL query executed by JDBC.
*/
public static String escape(String name)
{
StringBuffer buffer = new StringBuffer(name.length() + 2);
buffer.append('"');
for (int i = 0; i < name.length(); i++) {
char c = name.charAt(i);
// escape double quote characters with an extra double quote
if (c == '"') buffer.append('"');
buffer.append(c);
}
buffer.append('"');
return buffer.toString();
}
/**
* Escape a schema-qualified name so that it is suitable
* for use in a SQL query executed by JDBC.
*/
public static String escape(String schema, String name)
{
return escape(schema) + "." + escape(name);
}
/**
* DROP a set of objects based upon a ResultSet from a
* DatabaseMetaData call.
*
* TODO: Handle errors to ensure all objects are dropped,
* probably requires interaction with its caller.
*
* #param s Statement object used to execute the DROP commands.
* #param rs DatabaseMetaData ResultSet
* #param schema Schema the objects are contained in
* #param mdColumn The column name used to extract the object's
* name from rs
* #param dropType The keyword to use after DROP in the SQL statement
* #throws SQLException database errors.
*/
private static void dropUsingDMD(
Statement s, ResultSet rs, String schema,
String mdColumn,
String dropType) throws SQLException
{
String dropLeadIn = "DROP " + dropType + " ";
// First collect the set of DROP SQL statements.
ArrayList<String> ddl = new ArrayList<String>();
while (rs.next())
{
String objectName = rs.getString(mdColumn);
String raw = dropLeadIn + escape(schema, objectName);
if (
"TYPE".equals( dropType ) ||
"SEQUENCE".equals( dropType ) ||
"DERBY AGGREGATE".equals( dropType )
)
{ raw = raw + " restrict "; }
ddl.add( raw );
}
rs.close();
if (ddl.isEmpty())
return;
// Execute them as a complete batch, hoping they will all succeed.
s.clearBatch();
int batchCount = 0;
for (Iterator i = ddl.iterator(); i.hasNext(); )
{
Object sql = i.next();
if (sql != null) {
s.addBatch(sql.toString());
batchCount++;
}
}
int[] results;
boolean hadError;
try {
results = s.executeBatch();
//Assert.assertNotNull(results);
//Assert.assertEquals("Incorrect result length from executeBatch", batchCount, results.length);
hadError = false;
} catch (BatchUpdateException batchException) {
results = batchException.getUpdateCounts();
//Assert.assertNotNull(results);
//Assert.assertTrue("Too many results in BatchUpdateException", results.length <= batchCount);
hadError = true;
}
// Remove any statements from the list that succeeded.
boolean didDrop = false;
for (int i = 0; i < results.length; i++)
{
int result = results[i];
if (result == Statement.EXECUTE_FAILED)
hadError = true;
else if (result == Statement.SUCCESS_NO_INFO || result >= 0) {
didDrop = true;
ddl.set(i, null);
}
//else
//Assert.fail("Negative executeBatch status");
}
s.clearBatch();
if (didDrop) {
// Commit any work we did do.
s.getConnection().commit();
}
// If we had failures drop them as individual statements
// until there are none left or none succeed. We need to
// do this because the batch processing stops at the first
// error. This copes with the simple case where there
// are objects of the same type that depend on each other
// and a different drop order will allow all or most
// to be dropped.
if (hadError) {
do {
hadError = false;
didDrop = false;
for (ListIterator<String> i = ddl.listIterator(); i.hasNext();) {
String sql = i.next();
if (sql != null) {
try {
s.executeUpdate(sql);
i.set(null);
didDrop = true;
} catch (SQLException e) {
hadError = true;
}
}
}
if (didDrop)
s.getConnection().commit();
} while (hadError && didDrop);
}
}
PS: This code came in handy for when I migrated my database from H2 that does support DROP ALL OBJECTS, to Apache Derby which does not (headache). The only reason I migrated away from H2 is that it's a fully in-memory database and was getting too big for my server's RAM, so I decided to try Apache Derby. H2 is far easier and more user-friendly than Derby, I highly recommend it. I'm sad that I can't afford the RAM to keep using H2.
By the way, for those affected by Derby's lack of LIMIT or UPSERT, see this post about substituting FETCH NEXT instead of LIMIT and this one about correctly using MERGE INTO.

Categories

Resources