I use a Row Set to pass query results in my selenium framework. Occasionally the data access object throws the following
java.sql.SQLException: No suitable driver found for jdbc:jtds:sqlserver://MYDatabasename:1433/DB
It uses this same driver and rowset to access and only fails occasionally. Any help would be appreciated.
RowSet:
public static RowSet GetRowSet(String SqlQuery, String[] Parameters, String DB){
CachedRowSet rs;
String ROWSET_IMPL_CLASS = "com.sun.rowset.CachedRowSetImpl";
rs = null;
try {
Class<?> c = Class.forName(ROWSET_IMPL_CLASS);
rs = (CachedRowSet) c.newInstance();
rs.setUrl(Configuration.DBConnString + DB);
rs.setUsername(Configuration.DBUser );
rs.setPassword(Configuration.DBPwd );
rs.setReadOnly(true);
rs.setCommand(SqlQuery);
for (int p=0;
p<Parameters.length;
p++)
{
rs.setString(p+1, Parameters[p]);
}
rs.execute();
Example of code:
public void examplevoid(String string, String string2)
throws Exception {
RowSet RoS = null;
RoS = Example.GetExample(string, string2);
while (RoS.next()) {
String Example = RoS.getString("Example");
selenium.click(Example)
selenium.waitForPageToLoad(setup.timeoutsetting);
}
RoS.close();
Which uses and in turn calls the rowset:
public static RowSet GetExample(String string, String string2) throws
String[] Parameters = {string, string2};
RowSet ExampleRowSet= null;
ExampleRowSet = DataAccess.GetRowSet("Some SQL HERE", Parameters, Configuration.DB);
return Example;
That seems impossible. Either the driver class is loaded, or it isn't. Once loaded, successive calls to DriverManager.getConnection() with the same JDBC URL should never give that error. What else is going on?
Edit: The only questionable thing I see is that all of your Configuration.* properties appear to be fields in a class somewhere. If some of those properties are changing values between tests, maybe your JDBC driver is causing that exception to be thrown because of a bad property value, like the Configuration.DB or Configuration.DBConnString. If it's fairly repeatable, try changing
rs.setUrl(Configuration.DBConnString + DB);
to
String url = Configuration.DBConnString + DB;
log.debug("Using JDBC URL: " + url);
rs.setUrl(url);
When the exception happens, see if the string looks different.
Related
As a back-end developer, I would like to call stored procedure connecting Oracle 9i database to fetch the single output string.
In reality, the result of the output parameter as string, returns empty.
Would you please tell me what to modify the work?
Here is my rest controller:
#GetMapping(value = { "/third"})
public String getVariable (){
String result = "" ;
Connection conn;
conn = getConnection() ;
CallableStatement cstmt = null;
try {
String SQL = "{call pkg1234.get_pc_lookup_value_second(?,?)}";
cstmt = conn.prepareCall (SQL);
cstmt.setString(1, "TES");
cstmt.registerOutParameter(2, oracle.jdbc.OracleTypes.VARCHAR );
cstmt.execute();
String dddresult = cstmt.getString(2);
System.out.println(" Record :"+dddresult);
result = dddresult;
}
catch (SQLException e) {
System.out.println(" go this");
e.printStackTrace();
}
return result ;
}
Here is my stored procedure:
PROCEDURE get_pc_lookup_value_second (
i_lookup_code IN VARCHAR2,
o_lookup_value OUT VARCHAR2
)
o_lookup_value := '456';
END get_pc_lookup_value_second;
Try my own library for simplicity:
<dependency>
<groupId>com.github.buckelieg</groupId>
<artifactId>db-fn</artifactId>
<version>0.3.4</version>
</dependency>
And then in code:
try(DB db = new DB("your-connection-string")) {
String result = db.procedure("{call pkg1234.get_pc_lookup_value_second(?,?)}", P.in("TES"), P.out(JDBCType.VARCHAR)).call(cs -> cs.getString(2)).orElse("Unknown");
}
See more here
You have reported the following as your code:
try(DB db = new DB("jdbc:oracle:thin:username/password#10.8.12.6:1521:dev")) {
String thisString = db.procedure("{call b_pc_mob_portal_pkg.get_pc_lookup_value_second(?,?)}",
P.in("TES" , "i_lookup_code"),
P.out(JDBCType.LONGNVARCHAR , "o_lookup_value" )).call(cs -> cs.getString(2)).orElse("Unknown");
System.out.println( "thisString String :" + thisString );
}
You have reported that this gives:
[Ljava.lang.Object; cannot be cast to [Lbuckelieg.fn.db.P;
You have asked:
How can I check on whether this method need to be revised.
This is because you try to combine named parameters with anonymous ones. Either remove parameter names:
db.procedure("{call b_pc_mob_portal_pkg.get_pc_lookup_value_second(?,?)}", P.in("TES"), P.out(JDBCTtype.VARCHAR))...
or add names to procedure call statement:
db.procedure("{call b_pc_mob_portal_pkg.get_pc_lookup_value_second(:i_lookup_code,:o_lookup_value)}", P.in("i_lookup_code", "TES"), P.out(JDBCType.VARCHAR , "o_lookup_value" ))...
You have also reported that you have tried this code:
try(DB db = new DB("jdbc:oracle:thin:username/password#10.8.12.6:1521:dev")) {
String thisString = db.procedure("{call b_pc_mob_portal_pkg.get_pc_lookup_value_second(?,?)}",
P.in("TES" ),
P.out(JDBCType.VARCHAR )).call(cs -> cs.getString(2)).orElse("Unknown");
System.out.println( "thisString String :" + thisString );
}
And you have said:
when I do it in this way, it gives:
buckelieg.fn.db.SQLRuntimeException: [registerOutParameter not implemented]
I think the problem is in JDBC driver. Which version do you use? Can it be updated for newer one?
You have said that this is your database:
And also that you have downloaded:
OJDBC7.jar
https://www.oracle.com/database/technologies/jdbc-drivers-12c-downloads.html
Here is the Oracle IDE Version fetched by the Oracle SQL Developer
Very interesting. I will look into this issue.
As a temporal workaround I suggest to do the following:
Rewrite procedure as follows:
CREATE OR REPLACE FUNCTION b_pc_mob_portal_pkg.get_pc_lookup_value_second(IN param1 VARCHAR2 DEFAULT 'TES')
RETURN VARCHAR2 IS
BEGIN
RETURN '456';
END b_pc_mob_portal_pkg.get_pc_lookup_value_second;
And then in code:
String value = db.select("SELECT b_pc_mob_portal_pkg.get_pc_lookup_value_second(?) AS output FROM DUAL", "TES").single(rs -> rs.getString("output")).orElse("Unknown");
How about procedure?
Have you tried latest version (0.3.6) of library?
<dependency>
<groupId>com.github.buckelieg</groupId>
<artifactId>db-fn</artifactId>
<version>0.3.6</version>
</dependency>
#raju,
It may help you,Using Jpa Template with Springboot annotation.
Controller class
{
#Autowired
B b;
#GetMapping(value = { "/third"})
public String getVariable (){
return b.getInfo();
}
}
#service
B
{
#Autowired
A a;
public String getInfo()
{
return a.getoutput();
}
#repository
interface A extends JpaRepository<Mention your Entity,String>()
{
#procedure(name="Execute your_procedure name")
//return type
public String getoutput();
}
}
I am trying to use STRUCT for the insertion of massive data in my table DATA_TABLE, but generate error (java.sql.SQLException: Fail to convert to internal representation) with data type field CLOB and I can't find a solution to my problem, my code:
My table
CREATE TABLE DATA_TABLE (DAT_ID NUMBER,
DAT_CODE VARCHAR2(10),
DAT_TEXT CLOB);
Create type object
CREATE OR REPLACE TYPE TY_OBJ_DATA AS OBJECT (DAT_ID NUMBER,
DAT_CODE VARCHAR2(10),
DAT_TEXT CLOB);
create type table from type object
CREATE OR REPLACE TYPE TY_TABLE_DATA AS TABLE OF SCHEMA.TY_OBJ_DATA;
My simplified java method
public static void bulkData(List<DataTable> listDataInfo) throws Exception {
DataSource ds = (DataSource) getEntityManager().getEntityManagerFactory().getProperties().get("javax.persistence.jtaDataSource");
OracleConnection connection = ds.getConnection().unwrap(OracleConnection.class);
try{
StructDescriptor typeTableObject = StructDescriptor.createDescriptor("SCHEMA.TY_OBJ_DATA", conect);
STRUCT[] structData = new STRUCT[DataTable.size()];
int counter= 0;
for (DataTable d : listDataInfo) {
Clob clob = connection.createClob();
STRUCT m = new STRUCT(typeTableObject, connection,
new Object[]{d.getDatId(),
d.getDatCode,
clob.setString(1, d.getDatText)});
structData [counter++] = m;
}
ArrayDescriptor tyTable = ArrayDescriptor.createDescriptor("SCHEMA.TY_TABLE_DATA", connection);
ARRAY array = new ARRAY(tyTable, connection, structData);
String sqlQuery = "{ CALL PACKAGE_BULK.PL_BULK_DATA }";
CallableStatement cst = conect.prepareCall(sqlQuery);
cst.setArray(1, array );
cst.execute();
} catch (Exception e) {
throw new Exception(e);
} finally {
try {
connection.close();
} catch (SQLException e) {
throw new Exception(e);
}
}
}
I omit the package code, because it is working correctly and is not my main problem. i Use ojdbc6 version 11.2.0, java 8 and Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit.
Is it possible to use STRUCT with fields of type CLOB? I am doing something wrong? my DatText field when it is remapped is of the String type and that was the best conversion that I managed to do from String to a clob but I still have problems, any idea how I can solve this? Thank you.
I know this is very old, but as I just came by:
I solved this issue by simply skipping the descriptors and using:
cst.setArray(i, ((OracleConnection) connection).createOracleArray("TY_TABLE_DATA", shiftArrayOneUp(new Object[]{d.getDatId(),
d.getDatCode(),
d.getDatText()}));
shiftArrayOneUp is just adding one empty value at the beginning (oracle array starts at 1, not at 0)
private Object[] shiftArrayOneUp(Object[] values) {
Object[] result = new Object[values.length + 1];
System.arraycopy(values, 0, result, 1, values.length);
return result;
}
As I had to adjust my code accordingly, I did not test what I was writing here.
Maybe some small adjustments need to be done
I'm using CachedRowSet. But when I call the insertRow() method, there is a SQLException failed to insert row.
Here is my code:
static final String DATABASE_URL = "jdbc:mysql://localhost:3306/javapos";
static final String USERNAME = "root";
static final String PASSWORD = "sbc";
public static void main (String [] agr) throws SQLException
{
CachedRowSetImpl rs = new CachedRowSetImpl();
rs.setUrl(DATABASE_URL);
rs.setUsername(USERNAME);
rs.setPassword(PASSWORD);
rs.setCommand("select * from uom order by itemid");
rs.execute();
while(rs.next()){
System.out.println(rs.getString("itemid") + " - " + rs.getString("uom"));
}
rs.moveToInsertRow();
rs.updateString(2,"Sample code");
rs.insertRow();
rs.moveToCurrentRow();
rs.acceptChanges();
}
When you call insertRow(), the Reference Implementation of CachedRowSet performs a check if all required columns have been populated and otherwise it throws an exception (source from Grepcode CachedRowSet.insertRow(), line numbers don't exactly match):
if (onInsertRow == false ||
insertRow.isCompleteRow(RowSetMD) == false) {
throw new SQLException(resBundle.handleGetObject("cachedrowsetimpl.failedins").toString());
}
The check is performed in InsertRow.isCompleteRow(RowSetMetaData):
public boolean isCompleteRow(RowSetMetaData RowSetMD) throws SQLException {
for (int i = 0; i < cols; i++) {
if (colsInserted.get(i) == false &&
RowSetMD.isNullable(i + 1) == ResultSetMetaData.columnNoNulls) {
return false;
}
}
return true;
}
In other words, when inserting a row you must provide a value for all columns that are not nullable (this includes the primary key). There seem to be two ways to work around this:
Setting a (random) value. This does require that your primary key is always generated (even if a value is provided).
Explicitly setting the column to null using updateNull. Using setNull doesn't work: it provides the same error, and using setObject(idx, null) results in a NullPointerException
When using your code with these changes I get an SQLException when calling acceptChanges as the implementation doesn't disable autoCommit (it seems to have been commented out), but it does explicitly call commit (which is invalid when in autoCommit). This doesn't seem to be easy to solve, except maybe explicitly providing a connection on execute, or creating your own implementation.
I think these kind of issues actually demonstrate how little the RowSet implementations are actually used (otherwise they would already have been flushed out long ago).
Note however that if this were the actual code you needed and don't need the disconnected characteristics of the CachedRowSet, then you could simply use an updatable result set.
Example:
beginAddRow(crs);
crs.updateString("TABLE_TYPE", "TABLE");
continueAddRow();
crs.updateString("TABLE_TYPE", "INDEX");
endAddRow();
static public CachedRowSet beginAddRow(CachedRowSet crs) throws SQLException {
crs.moveToInsertRow(); // onInsertRow = true
return crs;
}
static public CachedRowSet continueAddRow(CachedRowSet crs) throws SQLException {
crs.insertRow();
crs.moveToCurrentRow();
crs.moveToInsertRow();
return crs;
}
static public CachedRowSet endAddRow(CachedRowSet crs) throws SQLException {
crs.insertRow();
crs.moveToCurrentRow(); // onInsertRow = false;
crs.beforeFirst();
return crs;
}
I have executed a query using JDBC and traversing the resultset I have stored all fields in List in java.
List<String> dataList=new ArrayList<String>();
while(res.next())
{
dataList.add(res.getString(1));
dataList.add(res.getString(2));
dataList.add(res.getString(3));
dataList.add(res.getString(4));
dataList.add(res.getString(5));
dataList.add(res.getString(6));
dataList.add(res.getString(7));
}
Iterator<String> it= dataList.iterator();
As I have added directly into list so how can I get this 7 fields while traversing the iterator.
Means:
while(it.hasNext())
{
String f1=it.next();
}
Like wise everytime I want 7 fields at a time
and next 7, next 7....... so on
Using this while loop how can I get those 7 fields (one row in table having 7 field) at a time.
I get little bit confuse here. Please help me.
Thanks
What you want to do is actually create another object that stores all seven of the values.
Then create a list of these entries so that you can access one row at a time, which is what I think you are asking.
First create a class for the row.
private static class Entry {
String[] row;
public Entry ( ResultSet r ) {
row = new String [ 7 ];
for (int i = 1; i <= 7; i++) {
row[i] = r.getString(i);
}
}
}
Using that, you can then create a list of Entry objects.
List<Entry> entryList = new ArrayList <Entry> ();
while(res.next())
{
entryList.add ( new Entry ( res ) );
}
Then, you can go ahead and loop through entryList and get any specific entry you would want.
Of course, if you have specific values, it might be wise to create instance variables of type String for Entry rather than an array of Strings.
By that I mean you could do this:
private static class Entry {
String column1; // rather than name column1 use what the column semantically represents
String column2;
// ...
public Entry ( ResultSet r ) {
column1 = r.getString(1);
// ...
}
This way, you can also calls like r.getInt(i) for certain columns which have an different type other than String.
Good luck!
I think your List declaration should be
List<Any DAO Object> instead of List<String>
While fetching from resultset, create a DAO object, add all fetched data into that object and then add that object into the list.
Then you can iterate and get each DAO object at each iteration.
You can use DatabaseMetaData class,
private static final String DRIVER = "com.mysql.jdbc.Driver";
private static final String URL = "jdbc:mysql://localhost/testdb";
private static final String USERNAME = "root";
private static final String PASSWORD = "";
public static void main(String[] args) throws Exception {
Class.forName(DRIVER);
Connection connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);
DatabaseMetaData metadata = connection.getMetaData();
ResultSet resultSet = metadata.getColumns(null, null, "users", null);
while (resultSet.next()) {
String name = resultSet.getString("COLUMN_NAME");
String type = resultSet.getString("TYPE_NAME");
int size = resultSet.getInt("COLUMN_SIZE");
System.out.println("Column name: [" + name + "]; type: [" + type + "]; size: [" + size + "]");
}
connection.close();
}
Does anyone know whether there is a java library for parsing a MySQL schema? In code I want to be able to determine the tables and fields specified in a schema. Or will I need to write my own?
Thanks Richard.
Edit: Just want to avoid re-inventing the wheel unnecessarily :)
Answering my own question:
Am using jsqlparser http://jsqlparser.sourceforge.net/
This parses individual statements, not multiple statements such as found in a schema. So split the schema on ';'. It also doesn't like the '`' character, so these need to be stripped out. Code to get column names for a particular table:
public class BUDataColumnsFinder {
public static String[] readSql(String schema) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(schema)));
String mysql = "";
String line;
while ((line = br.readLine()) != null) {
mysql = mysql + line;
}
br.close();
mysql = mysql.replaceAll("`", "");
return mysql.split(";");
}
public static List<String> getColumnNames(String tableName, String schemaFile) throws JSQLParserException, IOException {
CCJSqlParserManager pm = new CCJSqlParserManager();
List<String> columnNames = new ArrayList<String>();
String[] sqlStatements = readSql(schemaFile);
for (String sqlStatement : sqlStatements) {
Statement statement = pm.parse(new StringReader(sqlStatement));
if (statement instanceof CreateTable) {
CreateTable create = (CreateTable) statement;
String name = create.getTable().getName();
if (name.equalsIgnoreCase(tableName)) {
List<ColumnDefinition> columns = create.getColumnDefinitions();
for (ColumnDefinition def : columns) {
columnNames.add(def.getColumnName());
}
break;
}
}
}
return columnNames;
}
public static void main(String[] args) throws Exception {
String schemaFile = "/home/john/config/bu-schema.sql";
String tableName = "records";
List<String> columnNames = BUDataColumnsFinder.getColumnNames(tableName, schemaFile);
for (String name : columnNames) {
System.out.println("name: " + name);
}
}
}
You may want to consider using code from Alibaba's Druid project. Although designed as a sophisticated connection pooling library, this project supports a very advanced parser and AST for ANSI SQL and non-ANSI dialects such as MySQL, Oracle, SQL Server, etc. The project is open source and bears the very liberal Apache License Version 2.0.
The main entry points into this part of the library is SQLUtils.java. You can use values returned from SQLUtils.parseStatements to access a typed model of the statements:
List<SQLStatement> statements = SQLUtils.parseStatements(sql, JdbcConstants.MYSQL);
for (SQLStatement statement : statements) {
if (statement instanceof MySqlCreateTableStatment) {
MySqlCreateTableStatment createTable = (MySqlCreateTableStatment) statement;
// Use methods like: createTable.getTableSource()
}
}
Why not just use DatabaseMetaData to find out the tables and columns? This presumes that the schema expressed in SQL has been run against the database you're connected to, but that's not a difficult assumption to satisfy.
MySQL might be able to simply import the data if you have the data in CSV format. I'd dig deeper into MySQL tools before I'd write Java code to do such a thing. If that doesn't work, I'd find an ETL tool to help me. Writing Java would be my solution of last resort.