How to Dynamically Retrieve JDBC ResultSet Data - jdbc

The requirement is :-
The application will run dynamic SQLs and show the results in table format in JSP. The SQL passed to the application will change, which means the number, name, datatype of selected columns will change and so the result set will also change. The SQL is stored in a config.properties file, everytime we need to run a different SQL, we will just change the SQL in config.properties file. After the SQL is executed, from the ResultSet's Metadata object I have retrieved the column names and column datatypes by :-
ResultSetMetaData rsmd = rs.getMetaData(); // rs is the ResultSet
HashMap<String , String> hmap = new LinkedHashMap<String , String>();
for(int i=1;i<=rsmd.getColumnCount();i++)
{
hmap.put(rsmd.getColumnName(i), rsmd.getColumnTypeName(i));
}
hmap.entrySet().forEach(entry ->{System.out.println(entry.getKey() + " : " + entry.getValue());});
Output :-
TRADER : VARCHAR2
TRAN_NUM : NUMBER
STARTTIME : DATE
ERROR_DETAILS : CLOB
In JDBC, we have specific methods eg. rs.getString(columnName), rs.getInt(columnIndex), rs.getTimestamp(), rs.getClob() to get data of different data types. But in this scenario everything is dynamic, as columnName and columnDatatype will change everytime.
The ResultSet contains around 2000 rows.
How to write the logic, to check the column's datatype and apply the correct rs.getXXX() method to retrieve the ResultSet's data dynamically ?
Thanks & Regards
Saswata Mandal

I am able to do it by :-
while(rs.next())
{
JsonObject jsonRow = new JsonObject();
for(String colName : ResultSetColumnNames)
{
jsonRow.addProperty(colName, rs.getObject(colName)==null ? "NULL": rs.getObject(colName).toString());
}
jsonArry.add(jsonRow);
}
Thanks and Regards
Saswata Mandal

Related

Oracle XMLType - Loading from XML flat-file as chunks

Using Java 8 and Oracle 11g. Regarding loading XML data from a flat file into an Oracle XMLType field. I can make it work with this code:
private String readAllBytesJava7(String filePath) {
Files files;
Paths paths;
String content;
content = "";
try {
content = new String ( Files.readAllBytes( Paths.get(filePath) ) );
}
catch (IOException e) {
log.error(e);
}
return content;
}
pstmt = oracleConnection.prepareStatement("update MYTABLE set XML_SOURCE = ? where TRANSACTION_NO = ?");
xmlFileAsString = this.readAllBytesJava7(fileTempLocation);
xmlType = XMLType.createXML(oracleConnection, xmlFileAsString);
pstmt.setObject(1,xmlType);
pstmt.setInt(2, ataSpecHeader.id);
pstmt.executeUpdate();
But as you might surmise, that only works for small XML files... Anything too large will cause a memory exception.
What I'd like to do is load the XML file in "chunks" as described here:
https://docs.oracle.com/cd/A97335_02/apps.102/a83724/oralob2.htm
and
https://community.oracle.com/thread/4721
Those posts show how to load a BLOB/CLOB column from a flat-file by "chunks". I can make it work if the column is blob/clob, but I couldn't adapt it for an XMLType column. Most of what I found online in regards to loading an XMLType column deals with using the oracle-directory object or using sql-loader, but I won't be able to use those as my solution. Is there any kind of post/example that someone knows of for how to load an XML file into an XMLType column as "chunks"?
Additional information:
I'm trying to take what I see in the posts for blob/clob and adapt it for XMLType. Here's the issues I'm facing:
sqlXml = oracleConnection.createSQLXML();
pstmt = oracleConnection.prepareStatement("update MYTABLE set
XML_SOURCE = XMLType.createXML('<e/>') where 1=1 and TRANSACTION_NO = ?");
pstmt.setInt(1, ataSpecHeader.id);
pstmt.executeUpdate();
With blob/clob, you start out by setting the blob/clob field to "empty" (so it isn't null)... I'm not sure how to do this with XMLType... the closest I can get it just to set it to some kind of xml as shown above.
The next step is to select the blob/clob field and get the output stream on it. Something like what is shown here:
cmd = "SELECT XML_SOURCE FROM MYTABLE WHERE TRANSACTION_NO = ${ataSpecHeader.id} FOR UPDATE ";
stmt = oracleConnection.createStatement();
rset = stmt.executeQuery(cmd);
rset.next();
xmlType = ((OracleResultSet)rset).getOPAQUE(1);
//clob = ((OracleResultSet)rset).getCLOB(1);
//blob = ((OracleResultSet)rset).getBLOB(1);
clob = xmlType.getClobVal();
//sqlXml = rset.getSQLXML(1);
//outstream = sqlXml.setBinaryStream();
//outstream = blob.getBinaryOutputStream();
outstream = clob.getAsciiOutputStream();
//At this point, read the XML file in "chunks" and write it to the outstream object by doing: outstream.write
The lines that are commented-out are to show the different things I've tried. To re-state... I can make it work fine if the field in the table is a BLOB or CLOB. But I'm not sure what to do if it's an XMLType. I'd like to get an outstream handle to the XMLType field so I can write to it, as I would if it were a BLOB or CLOB. Notice for BLOB/CLOB it selects the blob/clob field with "for update" and then gets an Outstream on it so I can write to it. For XMLType, i tried getting the field to an XMLType java class and SQLXML java class, but it won't work that way. I also tried getting the field first as xmltype/sqlxml and then casting to blob/clob to then get an outstream, but it won't work either. The truth is, I'm not sure what I'm supposed to do in order to be able to write to the XMLType field as a stream/chunks.

Google Cloud SQL + RETURN_GENERATED_KEYS

I've got a table in my Google Cloud SQL database with an auto-incrementing column.
How do I execute an INSERT query via google-apps-script/JDBC and get back the value for the newly incremented column?
For example, my column is named ticket_id. I want to INSERT and have the new ticket_id value be returned in the result set.
In other words, if I have the following structure, what would I need to modify or how, so that I can do something like rs = stmt.getGeneratedKeys();
var conn = Jdbc.getCloudSqlConnection("jdbc:google:rdbms:.......
var stmt = conn.createStatement();
//build my INSERT sql statement
var sql = "insert into ......
var rs = stmt.executeUpdate(sql);
I see that there is a JDBC statement class with a member called RETURN_GENERATED_KEYS but I have so far not been smart enough to figure out how to properly manipulate that and get what I need. Is RETURN_GENERATED_KEYS a constant, is it an attribute, or how can I make use of it?
It seems like the documentation with the Apps Script JDBC service is a bit lacking. I've created an internal task item for that. Thankfully, Apps Script JDBC API follows the Java JDBC API pretty closely. The key is to get the result set back using the stmt.getGeneratedKeys() call.
I built a sample table using the animals example from the MySQL docs and this sample below works nicely against that and logs the next incremented ID.
function foo() {
var conn = Jdbc.getCloudSqlConnection("jdbc:google:rdbms://<instance>/<db>");
var stmt = conn.createStatement();
var sql = "INSERT INTO animals (name) VALUES ('dog')";
var count = stmt.executeUpdate(sql,1)//pass in any int for auto inc IDs back
var rs = stmt.getGeneratedKeys();
//if you are only expecting one row back, no need for while loop
// just do rs.next();
while(rs.next()) {
Logger.log(rs.getString(1));
}
rs.close();
stmt.close();
conn.close();
}

How to retrieve multiple row from oracle 10g table through jsp page

I want to retrieve multiple row from oracle 10g tables through JSP using prepare statement.
The following JSP code creates a prepared statement:
String myquery = "SELECT * FROM EMPLOYEES WHERE DEPARTMENT = ?";
PreparedStatement mystatement = connection.prepareStatement(myquery);
The first line stores the SQL statement in a string variable called myquery , with a question mark ( ? ) serving as a placeholder for the SQL variable value. The second line creates a prepared statement object called mystatement .
Next, you assign a value to the SQL variable, as follows:
mystatement.setString(1, request.getParameter("myURLparam"));
The setString method assigns the value to the variable and takes two arguments. The first argument specifies the affected variable by its position (here, the first position in the SQL statement). The second argument specifies the variable's value. In this example, the value is provided by a URL parameter passed to the page.
Note: You must use different methods to assign non-string values to SQL variables. For example, to assign an integer to the variable, you would use the mystatement.setInt() method.
Finally, you generate the recordset, as follows:
ResultSet myresults = mystatement.execute();
Please find the sample code below from the below link http://oracle.ittoolbox.com/groups/technical-functional/oracle-apps-l/how-to-connect-to-database-in-jsp-using-jdbc-drivers-to-oracle-database-1569382
<%# page import="java.io.*, java.util.*, java.sql.*"%>
<%# page import="oracle.jdbc.driver.OracleConnection" %>
<%
OracleConnection conn1 = null;
ResultSet getCountRs = null;
PreparedStatement getCountStmt = null;
try{
conn1 = (OracleConnection)TransactionScope.getConnection();
getCountStmt = conn1.prepareStatement("SELECT
PROMPT,FUNCTION_ID,SUB_MENU_ID FROM FND_MENU_ENTRIES_VL WHERE MENU_ID
=:1 and prompt is not null");
getCountStmt.setInt(1,pMenuid);
getCountRs = getCountStmt.executeQuery();
while(getCountRs.next())
{
prompt[x]=getCountRs.getString(1);
function_id[x]=getCountRs.getInt(2);
submenu_id[x]=getCountRs.getInt(3);
x++;
}
}
catch(exception e) {}
%>

calling derby (java db) 'show tables' from jdbc

I need to enumerate the tables in a Derby (aka Java DB) database using JDBC in a Java program. All I am aware of for doing this is the SHOW TABLES command.
I first tried with something similar to this...
String strConnectionURL = "jdbc:derby:/path/to/derby/database;create=false";
Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
Connection connection = DriverManager.getConnection(strConnectionURL);
Statement statement = connection.createStatement();
boolean boResult = statement.execute("SHOW TABLES");
if (boResult) {
System.out.println("yay!");
}
...but that throws an exception:
java.sql.SQLSyntaxErrorException: Syntax error: Encountered "SHOW" at line 1, column 1.
So next I thought maybe I needed to use a CallableStatement so I tried this...
String strConnectionURL = "jdbc:derby:/path/to/derby/db;create=false";
Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
Connection connection = DriverManager.getConnection(strConnectionURL);
CallableStatement statement = connection.prepareCall("SHOW TABLES");
boolean boResult = statement.execute();
if (boResult) {
System.out.println("yippee!");
}
...but that throws the same exception:
java.sql.SQLSyntaxErrorException: Syntax error: Encountered "SHOW" at line 1, column 1.
So, can anyone help me enumerate the tables in my Derby (Java DB) database from JDBC?
EDIT: I'm looking around and starting to get a feeling this may be a general JDBC question. In other words, one could/would enumerate all a db's tables with the DatabaseMetaData object that can be retrieved from the Connection object. Looking into that (and looking forward to responses)...
EDIT 2: I found a pure JDBC solution, but am still happy to hear alternatives...
String strConnectionURL = "jdbc:derby:/path/to/db;create=false";
Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
Connection connection = DriverManager.getConnection(strConnectionURL);
DatabaseMetaData dbmd = connection.getMetaData();
ResultSet resultSet = dbmd.getTables(null, null, null, null);
while (resultSet.next()) {
String strTableName = resultSet.getString("TABLE_NAME");
System.out.println("TABLE_NAME is " + strTableName);
}
Show Tables is an ij command, not a base SQL statement, so you can't directly execute it. As you noted in your "EDIT 2", you can use the DatabaseMetaData to do this. Two other ways to do it are: you can select from the system catalogs (see http://db.apache.org/derby/docs/10.8/ref/rrefsistabs24269.html) , or you can use the "ij.runScript" method to run the ij tool from within your program, and pass it the "show tables" command (see http://db.apache.org/derby/docs/10.8/publishedapi/jdbc3/org/apache/derby/tools/ij.html)
As Bryan suggested ij.runScript - the code would look like this:
public void showTbls() throws Exception{
String sqlIn = "SHOW TABLES;";
InputStream stream = new ByteArrayInputStream(sqlIn.getBytes(StandardCharsets.UTF_8));
ij.runScript(conn,stream,StandardCharsets.UTF_8.name(), System.out,"UTF-8");
stream.close();
}
assumming conn is a opened derby Connection
But the disadvantage is that you are getting only string output. Not an ResultSet as you would get from:
Statement stmt = conn.createStatement();
ResultSet results = stmt.executeQuery("SELECT * FROM sys.systables");
or if you want only user table names you can use following SQL:
ResultSet results = stmt.executeQuery("SELECT TABLENAME FROM SYS.SYSTABLES WHERE TABLETYPE='T'");
A very similar output to
SHOW TABLES;
can be produced by using the following jdbc compliant query:
SELECT TABLENAME, (SELECT SCHEMANAME
FROM SYS.SYSSCHEMAS
WHERE SYS.SYSTABLES.SCHEMAID = SYS.SYSSCHEMAS.SCHEMAID)
AS SCHEMANAME
FROM SYS.SYSTABLES WHERE TABLETYPE='T'
It also shows you the probably useful SCHEMA information for each TABLE entry. Skip
TABLETYPE='T'
if you also want to see the system tables of your database as the user before has mentioned already.

Reading/Writing DataTables to and from an OleDb Database LINQ

My current project is to take information from an OleDbDatabase and .CSV files and place it all into a larger OleDbDatabase.
I have currently read in all the information I need from both .CSV files, and the OleDbDatabase into DataTables.... Where it is getting hairy is writing all of the information back to another OleDbDatabase.
Right now my current method is to do something like this:
OleDbTransaction myTransaction = null;
try
{
OleDbConnection conn = new OleDbConnection("PROVIDER=Microsoft.Jet.OLEDB.4.0;" +
"Data Source=" + Database);
conn.Open();
OleDbCommand command = conn.CreateCommand();
string strSQL;
command.Transaction = myTransaction;
strSQL = "Insert into TABLE " +
"(FirstName, LastName) values ('" +
FirstName + "', '" + LastName + "')";
command.CommandType = CommandType.Text;
command.CommandText = strSQL;
command.ExecuteNonQuery();
conn.close();
catch (Exception)
{
// IF invalid data is entered, rolls back the database
myTransaction.Rollback();
}
Of course, this is very basic and I'm using an SQL command to commit my transactions to a connection. My problem is I could do this, but I have about 200 fields that need inserted over several tables. I'm willing to do the leg work if that's the only way to go. But I feel like there is an easier method. Is there anything in LINQ that could help me out with this?
If the column names in the DataTable match exactly to the column names in the destination table, then you might be able to use a OleDbCommandBuilder (Warning: I haven't tested this yet). One area you may run into problems is if the data types of the source data table do not match those of the destination table (e.g if the source column data types are all strings).
EDIT
I revised my original code in a number of ways. First, I switched to using the Merge method on a DataTable. This allowed me to skip using the LoadDataRow in a loop.
using ( var conn = new OleDbConnection( destinationConnString ) )
{
//query off the destination table. Could also use Select Col1, Col2..
//if you were not going to insert into all columns.
const string selectSql = "Select * From [DestinationTable]";
using ( var adapter = new OleDbDataAdapter( selectSql, conn ) )
{
using ( var builder = new OleDbCommandBuilder( adapter ) )
{
conn.Open();
var destinationTable = new DataTable();
adapter.Fill( destinationTable );
//if the column names do not match exactly, then they
//will be skipped
destinationTable.Merge( sourceDataTable, true, MissingSchemaAction.Ignore );
//ensure that all rows are marked as Added.
destinationTable.AcceptChanges();
foreach ( DataRow row in destinationTable.Rows )
row.SetAdded();
builder.QuotePrefix = "[";
builder.QuoteSuffix= "]";
//forces the builder to rebuild its insert command
builder.GetInsertCommand();
adapter.Update( destinationTable );
}
}
}
ADDITION An alternate solution would be to use a framework like FileHelpers to read the CSV file and post it into your database. It does have an OleDbStorage DataLink for posting into OleDb sources. See the SqlServerStorage InsertRecord example to see how (in the end substitute OleDbStorage for SqlServerStorage).
It sounds like you have many .mdb and .csv that you need to merge into a single .mdb. This answer is running with that assumption, and that you have SQL Server available to you. If you don't, then consider downloading SQL Express.
Use SQL Server to act as the broker between your multiple datasources and your target datastore. Script each datasource as an insert into a SQL Server holding table. When all data is loaded into the holding table, perform a final push into your target Access datastore.
Consider these steps:
In SQL Server, create a holding table for the imported CSV data.
CREATE TABLE CsvImport
(CustomerID smallint,
LastName varchar(40),
BirthDate smalldatetime)
Create a stored proc whose job will be to read a given CSV filepath, and insert into a SQL Server table.
CREATE PROC ReadFromCSV
#CsvFilePath varchar(1000)
AS
BULK
INSERT CsvImport
FROM #CsvFilePath --'c:\some.csv'
WITH
(
FIELDTERMINATOR = ',', --your own specific terminators should go here
ROWTERMINATOR = '\n'
)
GO
Create a script to call this stored proc for each .csv file you have on disk. Perhaps some Excel trickery or filesystem dir piped commands can help you create these statements.
exec ReadFromCSV 'c:\1.csv
For each .mdb datasource, create a temp linked server.
DECLARE #MdbFilePath varchar(1000);
SELECT #MdbFilePath = 'C:\MyMdb1.mdb';
EXEC master.dbo.sp_addlinkedserver #server = N'MY_ACCESS_DB_', #srvproduct=N'Access', #provider=N'Microsoft.Jet.OLEDB.4.0', #datasrc=#MdbFilePath
-- grab the relevant data
--your data's now in the table...
INSERT CsvImport(CustomerID,
SELECT [CustomerID]
,[LastName]
,[BirthDate]
FROM [MY_ACCESS_DB_]...[Customers]
--remove the linked server
EXEC master.dbo.sp_dropserver #server=N'MY_ACCESS_DB_', #droplogins='droplogins'
When you're done importing data into that holding table, create a Linked Server in your SQL Server instance. This is the target datastore. SELECT the data from SQL Server into Access.
EXEC master.dbo.sp_addlinkedserver #server = N'MY_ACCESS_TARGET', #srvproduct=N'Access', #provider=N'Microsoft.Jet.OLEDB.4.0', #datasrc='C:\Target.mdb'
INSERT INTO [MY_ACCESS_TARGET]...[Customer]
([CustomerID]
,[LastName]
,[BirthDate])
SELECT Customer,
LastName,
BirthDate
FROM CsvImport

Resources