What to Do when JDBC Driver is Invalid? - java

I'm working on a Java project that uses an Oracle database. When I create a new Oracle driver:
jdbcDriver driver = new jdbcDriver();
...I check it to see if it is valid using:
if (!driver.isValid())
{
throw new UncheckedSqlException("jdbcDriver not valid");
}
What is the correct procedure if the driver is invalid? That is, there is no way to store the data and retry later asynchronously (i.e. without locking up the app), since the database is unavailable. Is the assumption that if I just retry a few times, I'll get a valid driver?
UPDATE
Thanks to EJP for correctly pointing out that jdbcDriver is a class in my code. That is correct -- it is a class in a code base I am working on. EJP requests to have the relevant details posted. Here they are.
package us.mydev.jdbc.util;
import java.sql.*;
import javax.naming.*;
import javax.sql.*;
import org.apache.log4j.Logger;
import us.mydev.data.exception.UncheckedSqlException;
public class jdbcDriver
{
[.....]
public jdbcDriver()
{
try
{
m_iActiveConnections++;
if (showLogMessages)
{
log.debug("jdbcDriver() created, hashcode = " + this.hashCode()
+ ",Active Connection Count is " + m_iActiveConnections);
log.debug("Data Source is " + DS_STANDARDUSER);
}
InitialContext ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup(DS_STANDARDUSER);
mydevGetJDBCConn getConn = new sqlConn(ds);
m_conn = getConn.getConnection();
m_iDatabaseType = getConn.getDBType();
}
catch (NamingException _exp)
{ // handle any errors
log.error("failed", _exp);
}
}
public boolean isValid() throws SQLException
{
return m_conn != null && m_conn.isValid(10);
}
[.....]
}
I am finding that after this code:
jdbcDriver driver = new jdbcDriver();
boolean driverIsValid = driver.isValid();
...driverIsValid is false about once in every 1000 or so database accesses. I would imagine that this is an artifact of my current dev system -- I have Oracle running in a virtual machine.
Would it be unusual (or unheard of) for driverIsValid to be false in production in a situation like this? And if it is not unheard-of -- how is it usually handled, given that I can't save the relevant data to the database -- that is, can I just retry getting a database connection a few times and expect to get one?
UPDATE #2
Based on what I've learned from the responses, I need to ask this question using no code from the objects defined in the code base I am working on. I'm going to accept the answer provided by Wen-Bin Luo and re-ask the question more properly.

I think you are meaning if a connection is still valid at time of checking. The isValid method returns whether or not a connection is usable. This method can be invoked at any time you like and allows you to set a timeout.
According to the official manual, the method is
particularly useful in combination with a retry mechanism.
helpful when used in conjunction with the connection timeout and connection harvesting features.
You of course can retry the method in the hope of the database issue resolves afterwards, but more generally you set a timeout and after that you may have to take appropriate action and close the connection.
try { conn = poolDataSouorce.getConnection ... } catch (SQLException sqlexc)
{
if (conn == null || !((ValidConnection) conn).isValid())
// take the appropriate action
conn.close
}
For more information, please check https://docs.oracle.com/cd/B28359_01/java.111/e10788/connect.htm#CHDIDEEB

Related

Loading com.mysql.cj.jdbc.Driver errors with com.mysql.jdbc.Driver [duplicate]

This question already has answers here:
Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver' [duplicate]
(26 answers)
Closed 2 years ago.
I've recently been have lots of db connection problems.
The current situation is, the driver has been updated to
mysql-connector-java-8.0.22.jar for linux.
The code was working and broke when I did a clean install of the OS to 20.x this caused too many problems and I reverted to 18.0.4
The class load is :
try { // The newInstance() call is a work around for some
// broken Java implementations
//Class.forName("com.mysql.jdbc.Driver").newInstance();
Class.forName("com.mysql.cj.jdbc.Driver").newInstance();
} catch (Exception ex) {
if (out != null) {
out.println("<p>Error while defining database driver: " +
"Class.forName: " + ex.getMessage());
out.println("<p>" + ex.toString() + "<p>");
// SKYPE_DEBUG.Log(DatabaseInterface.class.getName(), "mysql v[0] = "+ "\n",ex);
}
}
The actual connection code is :
query = "jdbc:mysql://localhost/bloddpressure?user=" + dbUserid + "&password=" + dbPassword;
// query = "jdbc:mysql://192.168.0.12:3307/?user=" + dbUserid + "&password=" + dbPassword;
Either of the query options give the same error.
When using:
Class.forName("com.mysql.jdbc.Driver").newInstance();
It started throwing the following error on the line
conn = DriverManager.getConnection(query);
The exception logged is:
Timestamp
Nov 12, 2020 08:38:50.273
Log Level
SEVERE
Logger
Name-Value Pairs
{levelValue=1000, timeMillis=1605170330273}
Record Number
631
Message ID
Complete Message
Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
I have changed the name of the driver to :
// Class.forName("com.mysql.jdbc.Driver").newInstance();
Class.forName("com.mysql.cj.jdbc.Driver").newInstance();
But still get the same exception as above. Either call creates the same exception.
It is not an exception. It is a warning message.
A "deprecation" warning tells you that you shouldn't be doing something like that anymore. You should read it and pay attention to what it actually says.
In this case, it is telling you that (under most circumstances) you should not use Class.forName(SOME_CLASS_NAME) to load JDBC drivers. This hasn't been necessary for a very long time.
Even you do use Class.forName, you shouldn't call newInstance() on the class. This comment:
// The newInstance() call is a work around for some
// broken Java implementations
is out of date. It might have been true 20 years ago ...
The so-called "connection code" that you showed us is not that at all. It is just generating a connection URL.
I think that the best thing you could do is to read the JDBC Basics > Establishing a Connection from the Oracle Java Tutorial which describes the 2 correct ways to get a JDBC connection. Then rewrite your code to establish JDBC connections one of those ways.
Among other things, you will get rid of fragile references to specific JDBC driver classes in your code, and get rid of the pesky deprecation message from your logs.

Oracle JDBC Driver loaded in one class not accessible in another class in same package

I am working on an EAR application(deployed on WLS 12c) that has a feature to configure and test jdbc connection for Oracle. So, in my jsp page the user selects the driver as oracle.jdbc.OracleDriver, enter the connectionurl, username and password and clicks on testConnection. And on doing this, the applicaiton gives back an error stating java.sql.SQLException: No suitable driver found for
jdbc:oracle:thin:#XXX-xx:1525/YYYY
A little brief of the architecture used in this application :
On application startup, we are referring to an xml that has all the JDBC defaults and try to load all the supported jdbc drivers that are present in that xml. And oracle.jdbc.OracleDriver is also one of them. This registering is done through a util class JDBCUtil.java which has 2 static methods : registerDriver(String driverName) and isDriverRegistered(String driverName).
So, the first time that JDBCUtil.registerDriver("oracle.jdbc.OracleDriver") is called, isDriverRegistered returns false and within registerDriver, we are making a call to class.forName(driverName) to load the driver.
The logic within isDriverRegistered gets an enumeration of all the drivers registered with DriverManager by calling DriverManager.getDrivers and checks if the driverName string passed to it equals driver.getClass.getName.
After the 1st call to JDBCUtil for OracleDriver, any subsequent calls to registerDriver for OracleDriver is expected to skip the class.forName because the isDriverRegistered is expected to return true. But upon adding logs statement and debugging, i noticed that everytime registerDriver->isDriverRegistered is called for OracleDriver, it is always returning false. The enumearation obtained by calling DriverManager getDrivers, never contains OracleDriver.
Secondly, In another class JDBCDS.java, we are calling JDBCUtil.registerDriver("oracle.jdbc.OracleDriver") and as expected, in registerDriver function exceute Class.forName("oracle.jdbc.OracleDriver") because isDriverRegistered returns false . In JDBCDS.java, after the call to registerDriver, we call DriverManager.getConnection by passing in the url,usename and password. And this call is throwing SQLException saying NoSuitableDriverFound.
So, as another debug test, i called Class.forName("oracle.jdbc.OracleDriver") in JDBCDS.java and then made a call to DriverManager.getConnection and this time i was able to get a connection successfully.
I have gone through all the question related to NoSuitableDriverFound exception on this forum, but nothing is fitting my issue.
I have tried to call create a new instanc of the driver by calling new oracle.jdbc.OracleDriver() within JDBCDS.java and see if it accepts my url and it does. Moreover, this has also ruled out any classpath problem. The jar is certainly in the classpath. Our classpath includes the following:
com.oracle.db.jdbc7-dms.jar and also weblogic.jar
com.oracle.db.jdbc7-dms.jar internally has a link to ojdbc8dms.jar and weblogic.jar internally has ojdbc8.jar included through the Class-Path attribute of its MANIFEST.MF files in both the cases.
Also, this issue is isolated to only a particular environment. In other environments with the same setup, everything works fine without issues.
From JDBCUtil.java :
static Util Method that is used to register the driver :
public static void registerDriver(String driverName)
{
// Calls isDriverRegistered,isDriverRegistered returns false, then call Class.forName(driverName)
}
static method to see if driver is alreay registered :
public static boolean isDriverRegistered(String driverName)
{
//call DriverManager.getDrivers to get all the registered drivers
if(driver.getClass().getName().equals(driverName))
{
return true;
}
return false;
}
Code in JDBCDS.java that is used to perform the test connection:
try {
JDBCUtil.registerDriver(driver);
DriverManager.setLoginTimeout(300);
conn = DriverManager.getConnection(url, username, password); //This code throws No Suitable Driver Found Exception
} catch (SQLException e) {
// tried the below as a test
try
{
Class.forName(driver);
}
catch(Exception ex)
{
Logger.log("Could not load the driver with the current classloader", 4);
throw e;
}
conn = DriverManager.getConnection(url, username, password);//At this step the connection is successfully obtained
}
Both JDBCUtil.java and JDBCDS.java are in the same package.
Can anybody point me towards what i can do further to resolve this issue?

orientdb property constraints like MANDATORY not failing on improper java api insertions

I have an orientdb setup with a class called MessageLog with a property messageId that has a constraint MANADATORY and NOT NULL. If i try to insert a record from the console with a null messageId it raises an exception telling that the property cannot be null. But when i do a simple insertion from the Java API the record is inserted with the property value null. How is that possible.
The java code is:
import com.tinkerpop.blueprints.Vertex;
import com.tinkerpop.blueprints.impls.orient.OrientGraph;
import com.tinkerpop.blueprints.impls.orient.OrientVertexType;
public class OrientDbTrials {
public static void main(String[] args) {
OrientGraph graph = new OrientGraph("remote:localhost/blah","root","*****");
System.out.println("Connected to the db.");
Vertex messageLog = graph.addVertex("class:MessageLog");
System.out.println("Created new vertex : " + messageLog.toString());
messageLog.setProperty("messageId", null);
graph.commit();
System.out.println("Successfully saved it.");
}
}
Can somebody please explain this.
From http://www.orientechnologies.com/docs/1.7.8/orientdb.wiki/Graph-Database-Tinkerpop.html
Every time the graph is modified an implicit transaction is started automatically if no previous transaction was running. Transactions are committed automatically when the graph is closed by calling the shutdown() method or by explicit commit(). To rollback changes call the rollback() method. Changes inside a transaction will be temporary till the commit or the close of the graph instance. Concurrent threads or external clients can see the changes only when the transaction has been fully committed.
Because you are not defining how to handle exceptions, I think that when one is raised the graph is being closed and thus committing the change. When I run the code you give in a try catch block, I get an exception.
OrientGraph graph = new OrientGraph("remote:localhost/blah","root","*****");
System.out.println("Connected to the db.");
try { Vertex messageLog = graph.addVertex("class:MessageLog");
System.out.println("Created new vertex : " + messageLog.toString());
messageLog.setProperty("messageId", null);
graph.commit();
} catch(Exception e) {
graph.rollback();
}
finally {
graph.shutdown();
}
That code (pardon the ugly) raises this exception
java.lang.IllegalArgumentException: Property value can not be null
And because I handle the exception with a graph.rollback() the vertex doesn't show up in the graph.
What's interesting is that just having the mandatory property doesn't seem to be enough, i.e if you never fill the field with a null in the first place it will allow the commit, not raising an exception. I haven't looked into it, but maybe it has something to do with a graph instance being mixed-schema by default or something like that? I hope someone else knows.

Huge time accessing database from Java

I'm a junior java programmer and I've finally made my first program, all by myself, apart from school :).
The basics are: you can store data on it and retrieve it anytime. The main thing is, I want to be able to run this program on another computer (as a runable .jar file).
Therefore I had to install JRE and microsoft access 2010 drivers (they both are 32 bit), and the program works perfect, but there is 1 small problem.
It takes ages (literaly, 17 seconds) to store or delete something from the database.
What is the cause of this? Can I change it?
Edit:
Here's the code to insert an object of the class Woord into the database.
public static void ToevoegenWoord(Woord woord) {
try (Connection conn = DriverManager.getConnection("jdbc:odbc:DatabaseSenne")) {
PreparedStatement addWoord =
conn.prepareStatement("INSERT INTO Woorden VALUES (?)");
addWoord.setString(1, woord.getWoord());
addWoord.executeUpdate();
} catch (SQLException ex) {
for (Throwable t : ex) {
System.out.println("Het woord kond niet worden toegevoegd aan de databank.");
t.printStackTrace();
}
}
}
Most likely creating Connection every time is slow operation in your case (especially using JDBC-ODBC bridge). To confirm this try to put print statements with timestamp before and after the line that get Connection from DriverManager. If that's the case consider not to open connection on every request but open it once and reuse, better yet use some sort of Connection Pooling, there are plenty of options available.
If that's mot the case then actual insert could be slow as well. Again simple profiling with print statements should help you to discover where your code is spending most of the time.
First of all, congrats on your first independent foray. To answer your question / elaborate on maximdim's answer, the concern is that calling:
try (Connection conn = DriverManager.getConnection("jdbc:odbc:DatabaseSenne")) {
every time you're using this function may be a major bottleneck (or perhaps another section of your code is.) Most importantly, you will want to understand the concept of using logging or even standard print statements to help diagnose where you are seeing an issue. Wrapping individual lines of code like so:
System.out.println("Before Connection retrieval: " + new Date().getTime());
try (Connection conn = DriverManager.getConnection("jdbc:odbc:DatabaseSenne")) {
System.out.println("AFTER Connection retrieval: " + new Date().getTime());
...to see how many milliseconds pass for each call can help you determine exactly where your bottleneck lies.
Advise: use another database, like Derby, hsqldb. They are not so different from MSAccess, (= can use a file based DB), but perform better (than JDBC/ODBC). And can even be embedded in the application (without extra installation of the DB).

Mock database driver

Is there some kind of JDBC driver which simply ignores database calls?
For the development I am migrating an application to a virtual machine. Here I want to work on the GUI part only. But the application makes several requests to a database which doesn't let the application even start. I don't want to change the application code at this time since the database is pretty much coupled.
So I was thinking there could be a JDBC driver which just returns empty results for queries.
I decided to write an own simple mock driver. This was pretty much straight forward and did what I want. I can switch the database driver of the application by a configuration file so I could let the application use my driver on a simple way.
Then I extended the driver to return data which it parses from CSV files. I published the code on google code maybe someone else can get use of it: dummyjdbc
There are some "void" JDBC drivers as part of Mocking framewroks, for example MockDriver from Mockrunner.
But using it requires some coding.
That's because when Java application connects to a database it provides a JDBC URL in form jdbc:mysql://localhost. The system is searching which driver is registered in it to handle this kind of URL and chooses the right driver. The info about which URL type driver supports is contained in the driver itself, and it's impossible for a mock driver to hold all known URL types in it - there's no such thing as wildcarding there and any list would not be full.
So, if you're able to call JDBCMockObjectFactory.registerMockDriver() in the application before it connects to the database - it will do the job. If not - I don't think it's possible. However, slight modification of the driver code would do it... but again - coding is required.
jOOQ ships with a MockConnection that can be provided with a MockDataProvider, which is much easier to implement than the complete JDBC API. This blog post shows how to use the MockConnection:
http://blog.jooq.org/2013/02/20/easy-mocking-of-your-database/
An example:
MockDataProvider provider = new MockDataProvider() {
// Your contract is to return execution results, given a context
// object, which contains SQL statement(s), bind values, and some
// other context values
#Override
public MockResult[] execute(MockExecuteContext context)
throws SQLException {
// Use ordinary jOOQ API to create an org.jooq.Result object.
// You can also use ordinary jOOQ API to load CSV files or
// other formats, here!
DSLContext create = DSL.using(...);
Result<MyTableRecord> result = create.newResult(MY_TABLE);
result.add(create.newRecord(MY_TABLE));
// Now, return 1-many results, depending on whether this is
// a batch/multi-result context
return new MockResult[] {
new MockResult(1, result)
};
}
};
// Put your provider into a MockConnection and use that connection
// in your application. In this case, with a jOOQ DSLContext:
Connection connection = new MockConnection(provider);
DSLContext create = DSL.using(connection, dialect);
// Done! just use regular jOOQ API. It will return the values
// that you've specified in your MockDataProvider
assertEquals(1, create.selectOne().fetch().size());
There is also the MockFileDatabase, which helps you matching dummy results with SQL strings by writing a text file like this:
# This is a sample test database for MockFileDatabase
# Its syntax is inspired from H2's test script files
# When this query is executed...
select 'A' from dual;
# ... then, return the following result
> A
> -
> A
# rows: 1
# Just list all possible query / result combinations
select 'A', 'B' from dual;
> A B
> - -
> A B
# rows: 1
select "TABLE1"."ID1", "TABLE1"."NAME1" from "TABLE1";
> ID1 NAME1
> --- -----
> 1 X
> 2 Y
# rows: 2
My framework Acolyte is a tested JDBC driver designed for such purposes (mock up, testing, ...): https://github.com/cchantep/acolyte
It already used in several open source projects, either in vanilla Java, or using its Scala DSL:
// Register prepared handler with expected ID 'my-unique-id'
acolyte.Driver.register("my-unique-id", handler);
// then ...
Connection con = DriverManager.getConnection(jdbcUrl);
// ... Connection |con| is managed through |handler|
Never heard of such a driver myself. If you don't find one, you could instead use a DB like HSQLDB. You can configure it to use in-memory tables, so nothing else gets written to disk. You would have to use a different connection string, though.
If you want to do unit tests, not an integration tests, than
you can use a very basic and simple approach, using Mockito only, like this:
public class JDBCLowLevelTest {
private TestedClass tested;
private Connection connection;
private static Driver driver;
#BeforeClass
public static void setUpClass() throws Exception {
// (Optional) Print DriverManager logs to system out
DriverManager.setLogWriter(new PrintWriter((System.out)));
// (Optional) Sometimes you need to get rid of a driver (e.g JDBC-ODBC Bridge)
Driver configuredDriver = DriverManager.getDriver("jdbc:odbc:url");
System.out.println("De-registering the configured driver: " + configuredDriver);
DriverManager.deregisterDriver(configuredDriver);
// Register the mocked driver
driver = mock(Driver.class);
System.out.println("Registering the mock driver: " + driver);
DriverManager.registerDriver(driver);
}
#AfterClass
public static void tearDown() throws Exception {
// Let's cleanup the global state
System.out.println("De-registering the mock driver: " + driver);
DriverManager.deregisterDriver(driver);
}
#Before
public void setUp() throws Exception {
// given
tested = new TestedClass();
connection = mock(Connection.class);
given(driver.acceptsURL(anyString())).willReturn(true);
given(driver.connect(anyString(), Matchers.<Properties>any()))
.willReturn(connection);
}
}
Than you can test various scenarios, like in any other Mockito test e.g.
#Test
public void shouldHandleDoubleException() throws Exception {
// given
SomeData someData = new SomeData();
given(connection.prepareCall(anyString()))
.willThrow(new SQLException("Prepare call"));
willThrow(new SQLException("Close exception")).given(connection).close();
// when
SomeResponse response = testClass.someMethod(someData);
// then
assertThat(response, is(SOME_ERROR));
}
If you're using Spring, make your own class that implements Datasource and have the methods do nothing.

Categories