I was going through JDBC API's (mainly java.sql package) after writing some simple JDBC programs.
For example, in java.sql, the below is the declaration:
public interface Connection extends Wrapper, AutoCloseable
So, as per my understanding, these specifications have to be implemented by database vendors, in the form of JDBC drivers.
In my sample program i used H2 db, so i downloaded the JDBC driver.
Now, this jar should have implementation of java.sql.Connection, and this is what i saw in the .jar (jdbc driver) for this (under package --> org.h2.jdbc):
public class org.h2.jdbc.JdbcConnection extends org.h2.message.TraceObject implements java.sql.Connection {
The jdbc driver jar does implement java.sql.Connection, as expected; however where does it get java.sql.Connection from? (it simply implements java.sql.Connection), where is the definition of java.sql.Connection coming from?
Any pointers to clear this doubt would be helpful.
It's in the JDK, since you were able to look at its documentation in the JDK javadoc.
Related
Is it correct that when we dynamically load a .class class file, we need to use reflection to access the methods defined in the class?
When we use a JDBC driver in our Java program,
if I am correct, the JDBC driver is also dynamically loaded behind the scene.
But we never need to use reflection to access the methods defined in the JDBC driver. Instead we import java.sql and access the classes and methods defined in the java.sql.
Under the hood, is there reflection undergoing? If not, how does it work without reflection?
In general, when we dynamically load a .java class file, how can we eliminate or hide the use of reflection to access the methods defined in the class, just like a JDBC driver?
By the way, does a JDBC driver define exactly a class?
Thanks.
JDBC driver needs to implement java.sql.Driver interface (e.g. oracle.jdbc.OracleDriver). Apart from that the JDBC driver code is just plain Java code. It can use reflection or any other Java language feature. It's up to the driver vendor to decide if it's worth it e.g. using classes introduced in Java 8 will make it compatible only with Java 8+.
What is and isn't dynamically loaded depends on the JVM and the implementation of ClassLoader. Certain technologies like OSGI give more flexibility by implementing a ClassLoader that allows for unloading parts of the application.
Loading the JDBC driver class should happen so infrequently, and be cached by JVM, that cost of this one reflective call to load the driver should be negligible. A single SELECT 1 query will be orders of magnitude more expensive than loading the driver bytecode.
Starting with JDBC 4.0 it is no longer necessary to do any reflection-related calls to work with JDBC drivers, as any driver discovered on your class path is loaded automatically.
If your code must support pre-JDBC 4 code, or if your JDBC driver is not located on your class path, you must call Class.forName to load the driver.
Under the hood, is there reflection undergoing?
JDBC 4.0 uses Service Provider mechanism to locate JDBC drivers without doing reflection directly. At some point, however, JDBC driver class needs to be loaded, so a reflection call must be performed.
What is the actual use of Class.forName("oracle.jdbc.driver.OracleDriver") while connecting to a database? Why cant we just import the same class, instead why we are loading it.
The basic idea behind using Class.forName() is to load a JDBC driver implementation. A (normal) JDBC driver must contain a static initializer that registers an instance of the driver implementation with java.sql.DriverManager:
JDBC drivers must implement the Driver interface, and the implementation must contain a static initializer that will be called when the driver is loaded. This initializer registers a new instance of itself with the DriverManager
(from JDBC 4.1, section 9.2)
Since JDBC 4.0 however there is a new way to register drivers: the jar of a JDBC driver needs to include a file /META-INF/services/java.sql.Driver which contains the name(s) of the java.sql.Driver implementations in that jar. When you create a connection using the DriverManager, it will use java.util.ServiceLoader to enumerate all /META-INF/services/java.sql.Driver files in the classpath and load all drivers so they get registered.
The DriverManager.getConnection method has been enhanced to support the Java Standard Edition Service Provider mechanism. JDBC 4.0 Drivers must include the file META-INF/services/java.sql.Driver. This file contains the name of the JDBC driver’s implementation of java.sql.Driver.
(from JDBC 4.1, section 9.2.1)
The reasons drivers are loaded this way, is that it allows you to decouple an application from the driver (and database) it uses. This means that you can write, compile and even distribute an application without any drivers, you only need to use the interfaces provided in the java.sql (and javax.sql) package - which is part of Java - without needing to access the implementation directly.
The user of the application then adds a valid JDBC driver to the classpath (and configuring things like a connection string) so the application can actually to connect to a database. Before JDBC 4.0, the user would have to specify the driver name so that the application could load it using Class.forName, with a JDBC 4.0 compliant driver and Java 6 or higher this discovery is automatic.
When you load a driver literally with Class.forName("oracle.jdbc.driver.OracleDriver") it might feel like overkill, but if you keep in mind that it could also be a string pulled from a config file (or from user input) you might start to understand why it is so powerful.
Of course this driver independence is not 100%, especially not if your application uses vendor specific SQL. But the theory is that your application can be database independent. JDBC also provides some additional mechanisms to address this, eg JDBC escapes to provide a common syntax that the driver translates to the specific syntax, and DatabaseMetaData which allows you to discover features, reserved words etc which allow you to create or generate compatible queries.
A couple reasons to use Class.forName("") instead of just referencing the class directly:
Using Class.forName("") gives you more obvious control over where exactly the first attempt to load the specified class will be made in your code. This makes it more obvious where the code will fail (throw an exception) if that class is not present in the classpath when that code runs.
If you simply import the class and then reference it in your code, it becomes slightly less obvious where the code will throw an exception if the class is not present.
Also, using Class.forName("") is a way to get around potential compile-time restrictions. If, for example, the person compiling the code does not (for, let's say, licensing or intellectual property reasons) have access to the class oracle.jdbc.driver.OracleDriver, they may find it easier to compile code which references the class by Class.forName("") rather than directly.
If you do not need to use any methods, fields, or inner classes of the specified class, then Class.forName("") may be the clearest way to express that the only thing desired is to load the class (and have its static initializers run), and nothing else.
I don't think Class.forName exhibits any different functional behavior than referencing the class directly. It uses the calling class' classloader by default, which should be the same classloader that is used when referencing the class directly. There are some overloads to Class.forName("") that let you customize the class loading behavior a bit more.
It`s a legacy way to do so. Importing class you will have extra dependency
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.)
Sometimes it is required to load a class during the run time. i.e, any class can be loaded into the memory location dynamically while executing the java application. The Class.forName is used to load any given class (within double quotes as String) at run time. For example, when we use IDE, we see there will be a GUI builder which allows us to drag and drop the buttons, text fields, etc. This drag and drop mechanism internally requires certain classes to be loaded at run time.
In the Class.forName (sun.jdbc.odbc.JdbcOdbcDriver), the Class belongs to the package java.lang.Class and the forName() is a static method of the java.lang.Class. The JDBC Drivers (String) will be loaded into the class dynamically at run time and forName method contains static block which creates the Driver class object and register with the DriverManager Service automatically. Since the forName() is static, we call it using the class name (Class).
When we want to execute static block of a class, without creating its object then we can use class.forName(). Most of the work that Driver class do, exists in its static block.
Now what we require in our JDBC connectivity is to get the driver registered with DriverManager and to obtain connections with it, so this can be achieved simply by getting static block executed and there is no requirement to create object of that class. This approach will give a better performance.
I have a c3p0.ComboPooledDataSource, managed by Spring, managing my database connections. When I grab a PreparedStatement instance from a connection, I find that C3P0 is actually returning me an instance of the NewProxyPreparedStatement class which implements the PreparedStatement Interface. Fine.
However, when I call some of the methods defined as part of this contract I get the following RuntimeException java.lang.AbstractMethodError: com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.isClosed()Z
Looking at the source code, I can see that this class does not provide a number of methods which are outlined as part of the Statement and PreparedStatement contract. This code shouldn't compile. I feel like I am missing something important, but quite advanced here. How is it possible that this is shipped as part of a library?
I was hoping to use the PreparedStatement.isClosed method, is the actual underlying problem that I should just be leaving the management of these Objects to Spring?
Upgrade to the latest pre-release of c3p0-0.9.5, which supports all JDBC4 api. The version you are using supports only JDBC3. If you like managed dependencies, use groupId: com.mchange, artifactId: c3p0, version: 0.9.5-pre5. Otherwise binary distributions are on sourceforge.
As for JDBC4 it is required that the JDBC driver loads automatically. It is made by adding a static block where the driver registers in DriverManager. However when I'm writing such a block in my class, it is executed only when I create the object of this class. Otherwise the code is not run.
My question is: how do the drivers initilize itself, when they are not being created from the application code, however they are registered in DriverManager when I use it for getting db connection?
The paragraph 9.2 of the JDBC4 specification, states that the Driver implementation must register itself to the DriverManager upon class loading, so that when the Driver implementation is loaded the static initializer will automatically
register an instance of the driver.
So, simply loading the Driver implementation by (Class.forName("driverClassName")), will register the driver with the DriverManager.
Alternatively, the specification provides a mean to externally specify the drivers to be loaded (and thus registered) by the DriverManager, through the system property jdbc.drivers (see paragraph 9.2.1):
java -Djdbc.drivers=com.acme.jdbc.AcmeJdbcDriver Test
These methods of registration are available also in old JDBC3 implementations.
JDBC4 introdces a new method of registration, leveraging the Service Provider Mechanism:
every compliant driver must provide a jar including the META-INF/services/java.sql.Driver file.
The DriverManager (on DriverManager.getConnection() calls ) uses the java.sql.Driver service provider and loads the classes there specified, thus automatically registering the driver. This removes the need to invoke Class.forName (see paragraph 9.2.1 and paragraph 3.1, first bullet).
You can use the ServiceLoader facility of Java to automatically load services.
You need to put a file in the JAR that lists your class, then Java will automatically load it when the service is needed. Check the documentation for ServiceLoader for more details.
The answer to this similar question actually says that this is the way that JDBC uses.
In previous versions of JDBC when using a DataSource you will often see a call to Class.forName("driver.class")
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.
The documentation further states:
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 I assume the classpath is being scanned for any classes implementing the driver interface.
Usually you use Class.forName to initialize the class.
In my OSGi environment I am trying to preload a database driver for further usage. Normally, this can be done like that:
Class.forName("com.mysql.jdbc.Driver");
After that, a connection can be created. However, if I use that in OSGi under Felix, he says that the class cannot be found (ClassNotFoundException) and the connection cannot be created. But when I do something like that (try-catch is omitted):
com.mysql.jdbc.Driver d = new com.mysql.jdbcDriver
Class.forName("com.mysql.jdbc.Driver");
Then everything works fine and the connection is created. However, this is not very pretty because the driver class cannot be exchanged.
Is there a way to load the class with the first method? I assume that I have to provide the correct class loader. But where do I get that from?
The MySQL driver is provided as an OSGi wrapper bundle.
How exactly do you create your bundle manifest? If you use tools to automatically resolve the OSGi import statements of your bundle, they will fail on the first method since they do not recognize a simple string as a package dependency. The second method expresses the dependency as a hard Java dependency, so it's recognized by the tooling which adds the required OSGi import statement (and thus by the OSGi runtime to the classpath of your bundle).
So for your first method to work you must add the dependency to the package com.mysql.jdbc to the OSGi import statements of your bundle. How this is achieved is tool specific, Bnd uses an Import-Statement configuration parameter.
Everything #Heri said in his answer was correct. However if you want to introduce more flexibility into this system, use OSGi Services.
You want to make a database connection but you don't want to tightly couple your code to the specific database or JDBC driver. Why not write a small JDBC wrapper bundle that publishes a javax.sql.DataSource service? Your logic bundle can then bind to the service when it wants to query the database, and it needn't know anything about the physical database connection.
Note that the JDBC wrapper bundle would need to know about a specific JDBC driver, however it would be an extremely thin bundle and you could produce alternative wrappers for each of the drivers that you might wish to use.