I have a library method that can be used to connect to a database and then build a document using data from the database.
When they run this app with no parameters, I want to list out all available sql vendor connections. Based on this question I'm guessing its done using ServiceLoader but it's not clear to me exactly how to do this.
And critical to this is I'd like to get the class "com.mysql.jdbc.Driver", and I must get the "jdbc:mysql:" start of the connection string syntax.
So, how can I get the class (optional) and connection string start (necessary) of all JDBC connectors in the classpath?
There is no way defined in JDBC to automatically discover the JDBC URL format of a driver.
You will need to keep a registry of JDBC URL formats yourself (eg linked to one or more drivers), and then using the ServiceLoader or DriverManager, discover the available drivers and use that to determine which URL formats you can use.
Be aware that JDBC allows multiple drivers to use the same JDBC URL format (the first driver to successfully connect 'wins'), and a single driver can have more than one JDBC URL format.
To discover the JDBC drivers on the classpath, you can use
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
for (Driver driver : loadedDrivers) {
// do something with Driver
}
Be aware that using for-each might not be the best solution. Explicitly using the iterator might be better as then you can explicitly handle the ServiceConfigurationError thrown if a specific driver fails to load.
Alternatively, you can use
Enumeration<Driver> drivers = DriverManager.getDrivers();
And let DriverManager take care of discovering the drivers.
Maybe you can use a framework, which abstracts the layer of data access i.e. all the talking to the database?
I could recommend you Spring Data JDBC. https://spring.io/projects/spring-data-jdbc
But there are more frameworks that you could find as OR-Mapper which could help you.
Related
The DriverManager.getConnection makes call to driver.connect() internally to get database connection.
Are there any pitfalls of using driver.connect() directly?
java.sql.Driver is an interface, you need an actual implementation (eg org.firebirdsql.jdbc.FBDriver for Firebird) to actually do anything with it. On the other hand, DriverManager is a concrete class that abstracts (hides) driver loading, selection and usage for you.
Normally you should not use a Driver implementation directly. Stick with either using DriverManager, or otherwise use a DataSource implementation (preferably through configuration).
Using DriverManager decouples your code from the actual driver used. That means you do not have a compile time dependency on a specific driver. Changing drivers or even database system (assuming the SQL is compatible or generated compatibly) is then just a manner of adding/replacing a driver jar on the class path and specifying the right JDBC url in the configuration. That is assuming JDBC 4 automatic driver loading, otherwise adding the specific driver name to the config and using Class.forName(<value from config>) is also necessary.
For example, for SQL Server you have the driver from Microsoft or the jTDS driver (+ other third party drivers). Switching between those is - basically - a matter of adding the right driver to the class path and changing the URL in the configuration.
In the past, Microsoft even had a breaking change in their JDBC driver, changing both the class name of the driver and the URL. If you had hard coded a dependency on the Driver implementation, your code would break if you tried to upgrade the driver. If you had just used DriverManager (and had a config option for the URL, and - this was before automatic driver loading - the driver class name for loading), things would just have continued to work after a configuration change.
There are exceptions where you would use a Driver implementation directly (usually through reflection and still not hard coded), but those situations are rare, and unless you have a very good reason (and know what you're doing) then just don't do that.
I understand that class loading is useful for load the class at runtime with its class name.
However while using JDBC in our project we know which driver we are going to use and mostly driver manager string is hard coded.
My question is: Why are we loading driver using Class.forName("JDBC_DRIVER") here?
Why can't we go ahead adding the driver in class path? since we know which driver jar we are going to use.
I believe Class.forName(JDBC_DRIVER) will load the Driver into DriverManager. Is it the only reason?
Edit 1:
The DriverManager API doc states that
As part of its(DriverManager) initialization, the DriverManager class will attempt to load the driver classes referenced in the "jdbc.drivers" system property.
Applications no longer need to explictly load JDBC drivers using Class.forName(). Existing programs which currently load JDBC drivers using Class.forName() will continue to work without modification.
Then when I use other than oracle driver; do I need to change the driver name string in system property?
First of: with modern JDBC drivers and a current JDK (at least Java 6) the call to Class.forName() is no longer necessary. JDBC driver classes are now located using the service provider mechanism. You should be able to simply remove that call and leave the rest of the code unchanged and it should continue to work.
If you're not using a current JDK (or if you have a JDBC driver that does not have the appropriate files set up to use that mechanism) then the driver needs to be registered with the DriverManager using registerDriver. That method is usually called from the static initializer block of the actual driver class, which gets triggered when the class is first loaded, so issuing the Class.forName() ensures that the driver registers itself (if it wasn't already done).
And no matter if you use Class.forName() or the new service provider mechanism, you will always need the JDBC driver on the classpath (or available via some ClassLoader at runtime, at least).
tl;dr: yes, the only use of that Class.forName() call is to ensure the driver is registered. If you use a current JDK and current JDBC drivers, then this call should no longer be necesary.
The Class.forName(JDBC_DRIVER) call will register your JDBC driver in the DriverManager, so you can address it by url, such as "jdbc:odbc:Database" and so on...
Usually the driver class has static initialization code like this, which is invoked on Class.forName():
public class Driver implements java.sql.Driver {
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
}
You still have to put JDBC driver jar into the classpath.
As an alternative, you can use database specific DataSource, then you can declaratively specify the datasource type, for example in Spring context or in your Web server JNDI. Here is an example.
Putting a class in the classpath is not sufficient to have it loaded by the class loader. And the driver class must be loaded to ensure that it's registered to the JDBC API. BTW, for Class.forName to work, the driver class must be in the classpath.
In many industrial applications, we would like to abstract the data access layer from the rest of code, by pulling such information in a form of a property file/configuration file. In those cases, we might need to use something as you have done.
For e.g. we can use the JDBC interface classes to write the code to access the database, where you could configure the properties by selecting which technology you want to use as a database (e.g. ojdbc driver, or mysql jdbc driver, etc.) In those cases you can load the class using such method.
Is it possible, to register some custom JDBC driver in SQL-Anywhere,
so that it is possible to embed a table as remote table so that
statements on that table are redirected to that driver? For example
if a select gets executed, i want to query data from a webservice and
return the fir the resut-set. The same things for inserts and so on.
Thank you very much!
From the Javadoc of DriverManager:
JDBC 4.0 Drivers must include the file META-INF/services/java.sql.Driver. This file contains the name of the JDBC drivers implementation of java.sql.Driver. For example, to load the my.sql.Driver class, the META-INF/services/java.sql.Driver file would contain the entry:
my.sql.Driver
So there's your way of defining custom JDBC drivers. As you want to not use the sql anywhere driver in your application (because you'll have your own driver inside the remote part of your setup), there's no need to integrate with sql anywhere there - that's something you have to take care of in your webservices where you'll transform the networked SQL calls to SQL Anywhere calls.
Check out VJDBC - that already does something similar to what you want to do.
Good Luck
I understand that class loading is useful for load the class at runtime with its class name.
However while using JDBC in our project we know which driver we are going to use and mostly driver manager string is hard coded.
My question is: Why are we loading driver using Class.forName("JDBC_DRIVER") here?
Why can't we go ahead adding the driver in class path? since we know which driver jar we are going to use.
I believe Class.forName(JDBC_DRIVER) will load the Driver into DriverManager. Is it the only reason?
Edit 1:
The DriverManager API doc states that
As part of its(DriverManager) initialization, the DriverManager class will attempt to load the driver classes referenced in the "jdbc.drivers" system property.
Applications no longer need to explictly load JDBC drivers using Class.forName(). Existing programs which currently load JDBC drivers using Class.forName() will continue to work without modification.
Then when I use other than oracle driver; do I need to change the driver name string in system property?
First of: with modern JDBC drivers and a current JDK (at least Java 6) the call to Class.forName() is no longer necessary. JDBC driver classes are now located using the service provider mechanism. You should be able to simply remove that call and leave the rest of the code unchanged and it should continue to work.
If you're not using a current JDK (or if you have a JDBC driver that does not have the appropriate files set up to use that mechanism) then the driver needs to be registered with the DriverManager using registerDriver. That method is usually called from the static initializer block of the actual driver class, which gets triggered when the class is first loaded, so issuing the Class.forName() ensures that the driver registers itself (if it wasn't already done).
And no matter if you use Class.forName() or the new service provider mechanism, you will always need the JDBC driver on the classpath (or available via some ClassLoader at runtime, at least).
tl;dr: yes, the only use of that Class.forName() call is to ensure the driver is registered. If you use a current JDK and current JDBC drivers, then this call should no longer be necesary.
The Class.forName(JDBC_DRIVER) call will register your JDBC driver in the DriverManager, so you can address it by url, such as "jdbc:odbc:Database" and so on...
Usually the driver class has static initialization code like this, which is invoked on Class.forName():
public class Driver implements java.sql.Driver {
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
}
You still have to put JDBC driver jar into the classpath.
As an alternative, you can use database specific DataSource, then you can declaratively specify the datasource type, for example in Spring context or in your Web server JNDI. Here is an example.
Putting a class in the classpath is not sufficient to have it loaded by the class loader. And the driver class must be loaded to ensure that it's registered to the JDBC API. BTW, for Class.forName to work, the driver class must be in the classpath.
In many industrial applications, we would like to abstract the data access layer from the rest of code, by pulling such information in a form of a property file/configuration file. In those cases, we might need to use something as you have done.
For e.g. we can use the JDBC interface classes to write the code to access the database, where you could configure the properties by selecting which technology you want to use as a database (e.g. ojdbc driver, or mysql jdbc driver, etc.) In those cases you can load the class using such method.
I'm writing an application which has to be configurable to connect to Oracle, SQL Server and MySQL depending on client whim.
Up till now I'd been planning on using the JDBC-ODBC bridge and just connecting to the databases using different connection strings.
I'm told this is not very efficient.
Is there a pattern or best practice for connecting to multiple database systems? Or for selecting which driver to use?
Should I have it configurable? but include all three drivers or build three separate clients?
I'm not doing anything complex just pumping (inserting) data into the database from an event stream.
I would suggest that you make it configurable and include the three drivers. You can use a pattern like this: Create a super class (lets call it DAO) that provides the functionality of connecting to the database. This could be abstract.
Create a concrete sub class for each type of database that you wish to connect to. So you may end up with MySQLDAO, MSSQLDAO, and OracleDAO. each one will load the respective driver and use its respective connection string.
Create another class (lets call it DAOFactory) with a method getDAO(DB) that will create an instance of the DAO depending on the value of DB.
So for instance(in Pseudocode):
if(DB.equals("MySQL")){
DAO = new MySQLDAO();
}
return DAO;
So any code that needs to connect to the database will call the DAOFactory and ask for a DAO instance. You may store the DB value in an external file (like a properties file) so that you do not have to modify code to change the type of database.
this way your code does not need to know which type of database it is connecting to, and if you decide to support a fourth type of database later you will have to add one more class and modify the DAOFactory, not the rest of your code.
If you need anything complex, Hibernate is a good choice.
otherwise, what I would do is store your connection details in a properties file (or some other form of configuration) - namely: driver classname, JDBC url, username and password.
Then, all you need to do is load up the connection details from your properties file and include the correct JAR file on your classpath and you're done.
You could use a library such as Commons-DBCP if you wanted it to be a little easier to configure but other than that it's all you need to do (provided your SQL statements work on every database, of course).
If you're careful (and you test), you can do this with straight JDBC and just vary the driver class and connection information. You definitely want to stay away from the JDBC-ODBC bridge as it's generally slow and unreliable. The bridge is more likely to behave differently across dbs than JDBC is.
I think the DAO path is overkill if your requirements are as simple as listed.
If you're doing a lot of inserts, you might want to investigate prepared statements and batched updates as they are far more efficient. This might end up being less portable - hard to say without testing.
Take a look at Datasource. This is the preferred mechanism for obtaining a database connection.
IMO this provides an adminstrator the greatest flexibility for choosing database, connection pooling, and transaction strategies.
If you're using tomcat, then see here for how to register a Datasource with tomcat's JNDI.
If you're using Spring, then you can obtain a Datasource using jee:jndi-lookup.
If you're using Spring, but don't want to use JNDI, take a look at DriverManagerDataSource for a discussion of how to obtain a pooled Datasource (DBCP or C3P0).