I am wondering how JDBC knows which database driver class it should use.
Example:
Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
Connection verbindung = DriverManager.getConnection("jdbc:derby:d:/memory/onlineshop;create=true");
The first line takes Care, that a driver (EmbeddedDriver) will be loaded into to class loader (and so be available, e.g. for using with reflections, right?).
So, the next line is my connection string. It starts with:
jdbc:derby:...
I expected something like this instead:
jdbc:ConcreteDriverClassForInit
As you can see i am missing a link between the class loaded in the class loader and the connection string call of that class.
I searched in the derby archive for a class named "Derby.Class" - but there is no such class.
Even when I try sth. like this, JDBC still knows, what to do:
Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
Class.forName("org.something.anyotherDBDriver1");
Class.forName("org.something.anyotherDBDriver2");
Connection verbindung = DriverManager.getConnection("jdbc:derby:d:/memory/onlineshop;create=true");
But why?
Thanks for your help!
That EmbeddedDriver class has a static block executed when you load the class that adds an handler for the specific JDBC type:
static {
EmbeddedDriver.boot();
}
Check the code of the boot method here, and you'll see where the protocol is registered:
new JDBCBoot().boot(Attribute.PROTOCOL, ps);
That specific string is located in org.apache.derby.iapi.reference:
String PROTOCOL = "jdbc:derby:";
That's a common pattern that is followed by all JDBC drivers, i don't particularly like the code of this driver, if you want a cleaner example look at the SQLite driver, way more straightforward implementation:
static {
try {
DriverManager.registerDriver(new JDBC());
}
catch (SQLException e) {
e.printStackTrace();
}
}
org.sqlite.JDBC will register itself to the java.sql.DriverManager that will invoke JDBC.isValidURL(String url) to know if this class is a valid driver for a specific JDBC url, the SQLite driver will return true only if the url contains the PREFIX jdbc:sqlite:.
In previous versions of JDBC, to obtain a connection, you first need to load your JDBC driver by calling the method Class.forName().
Currently any JDBC 4.0 drivers that are found in your class path are automatically loaded. So, there is not even the need for Class.forName().
The gist of it is to be found in the documentation for java.sql.Driver and java.sql.DriverManager.
Basically, starting with JDBC 4, all you have to do is create a META-INF/services/java.sql.Driver file for your SQL driver implementation and the JRE will load it automatically. Which means that you can directly try and do:
DriverManager.getConnection("yourUrlHere")
If one driver recognizes the URL, it will be used automatically.
Related
I'm trying to make a program that connects to an Oracle database for the ultimate purpose of creating a few tables and running commands on them for a course I am taking. I'm currently trying to make the example given in class work but I can't get that to work. The code that generates the error "java.lang.ClassNotFoundException" is when my main hits the code:
Class.forName("oracle.jdbc.OracleDriver");
or
Class.forName("oracle.jdbc.driver.OracleDriver");
I have manually added the ojdbc6.jar, ojdbc8.jar and ojdbc14.jar files to each folder in my "PATH" system variable and I'm getting the exception:
java.lang.ClassNotFoundException: oracle.jdbc.OracleDriver
or
java.lang.ClassNotFoundException: oracle.jdbc.driver.OracleDriver
I have no idea how to fix this or what to modify. It is a course example after all...
Any/All suggestions are welcome.
Thank you for your time
I hope this might help?
I don't think you need Class.forName with latest version.
I would have done something like below and set connection,url,user and password as global final variables to avoid repeating yourself and call the method where you need it.
public Connection getConnection() throws SQLException {
return connection = DriverManager.getConnection(url, user, password);
}
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?
What will the command
Class.forName("oracle.jdbc.driver.OracleDriver")
exactly do while connecting to a Oracle database? Is there an alternate way of doing the same thing?
It obtains a reference to the class object with the FQCN (fully qualified class name) oracle.jdbc.driver.OracleDriver.
It doesn't "do" anything in terms of connecting to a database, aside from ensure that the specified class is loaded by the current classloader. There is no fundamental difference between writing
Class<?> driverClass = Class.forName("oracle.jdbc.driver.OracleDriver");
// and
Class<?> stringClass = Class.forName("java.lang.String");
Class.forName("com.example.some.jdbc.driver") calls show up in legacy code that uses JDBC because that is the legacy way of loading a JDBC driver.
From The Java Tutorial:
In previous versions of JDBC, to obtain a connection, you first had to initialize your JDBC driver by calling the method Class.forName. This methods required an object of type java.sql.Driver. Each JDBC driver contains one or more classes that implements the interface java.sql.Driver.
...
Any JDBC 4.0 drivers that are found in your class path are automatically loaded. (However, you must manually load any drivers prior to JDBC 4.0 with the method Class.forName.)
Further reading (read: questions this is a dup of)
What purpose does Class.forName() serve if you don't use the return value?
How does Class.forName() work?
What does 'Class.forName("org.sqlite.JDBC");' do?
What is the purpose of 'Class.forName("MY_JDBC_DRIVER")'?
Loading JDBC driver
It registers the driver; something of the form:
public class SomeDriver implements Driver {
static {
try {
DriverManager.registerDriver(new SomeDriver());
} catch (SQLException e) {
// TODO Auto-generated catch block
}
}
//etc: implemented methods
}
From the Java JDBC tutorial:
In previous versions of JDBC, to obtain a connection, you first had to initialize your JDBC driver by calling the method Class.forName.
Any JDBC 4.0 drivers that are found in your class path are automatically loaded. (However, you must manually load any drivers prior to JDBC 4.0 with the method Class.forName.)
So, if you're using the Oracle 11g (11.1) driver with Java 1.6, you don't need to call Class.forName. Otherwise, you need to call it to initialise the driver.
Pre Java 6 the DriverManager class wouldn't have known which JDBC driver you wanted to use. Class.forName("...") was a way on pre-loading the driver classes.
If you are using Java 6 you no longer need to do this.
This command loads class of Oracle jdbc driver to be available for DriverManager instance. After the class is loaded system can connect to Oracle using it. As an alternative you can use registerDriver method of DriverManager and pass it with instance of JDBC driver you need.
An alternative would to use the jdbc.drivers System property to specify your required drivers(s) on the command line when you start the JVM.
Use oracle.jdbc.OracleDriver, not oracle.jdbc.driver.OracleDriver. You do not need to register it if the driver jar file is in the "WEB-INF\lib" directory, if you are using Tomcat. Save this as test.jsp and put it in your web directory, and redeploy your web app folder in Tomcat manager:
<%# page import="java.sql.*" %>
<HTML>
<HEAD>
<TITLE>Simple JSP Oracle Test</TITLE>
</HEAD><BODY>
<%
Connection conn = null;
try {
Class.forName("oracle.jdbc.OracleDriver");
conn = DriverManager.getConnection("jdbc:oracle:thin:#XXX.XXX.XXX.XXX:XXXX:dbName", "user", "password");
Statement stmt = conn.createStatement();
out.println("Connection established!");
}
catch (Exception ex)
{
out.println("Exception: " + ex.getMessage() + "");
}
finally
{
if (conn != null) {
try {
conn.close();
}
catch (Exception ignored) {
// ignore
}
}
}
%>
So, I have a node.js project that I am working on for a class. One of the features our group is trying to build, requires calling a Java class method that connects to a MySQL database, does some computation and returns the result to the node server. Here is what we have so far:
Using the node-java library, we are adding the required jars to classpath:
var java = require('java');
var path = require('path');
java.classpath.push(path.join(__dirname, "../java/mysql-connector-java.5.1.38-bin.jar"));
java.classpath.push(path.join(__dirname, "../java/BayesianCurve.jar"));
java.classpath.push(path.join(__dirname, "../java/commons-math3-3.6.jar"));
Then, the class is instantiated and a method is called:
var test = java.newInstanceSync('BayesianCurve');
test.getPrediction(param, function(err, data){
if(err)
{
console.log(err);
return;
}
else
res.send(JSON.stringify(data));
});
So far, so good. The Java class method then has to create a connection to a MySQL database and this is where the problems start:
Class.forName("com.mysql.jdbc.Driver");
//System.setProperty("jdbc.drivers","com.mysql.jdbc.Driver");
//DriverManager.registerDriver(new com.mysql.jdbc.Driver());
System.out.println("Here");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/schema","root","root");
The java code, standalone, is able to connect to the database and execute queries. However, when used through the node-java module, it throws:
ClassNotFoundException: com.mysql.jdbc.Driver
One reason we thought about was that this library is linked dynamically. Somehow, the java environment in node.js is not able to figure this out. However, we are not really sure how to resolve this. Any help in this regard is appreciated.
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.