Search code examples
javajdbcderby

Apache Derby via Gradle: No suitable driver found for jdbc:derby


I have worked with Apache Derby before, but I have never used it in a Gradle Project. I have created a simple Derby Database in the root of my project using ij Derby tool by running the following commands:

connect 'jdbc:derby:MyDbTest;create=true';
CREATE TABLE Hashes (
string_id int,
RandString varchar(255)
);
INSERT INTO Hashes (string_id, RandString) values (1, 'sdfdsfsw');
SELECT * FROM Hashes;

After this, my Java Code in Main.java looks like:

package com.company;

import java.io.File;
import java.sql.*;

public class Main {
    public static void main(String[] args) {
        System.out.println("Current working directory : "+ new File(".").getAbsolutePath());
        try {
            Class.forName("org.apache.derby.jdbc.ClientDriver").newInstance();
        } catch (Exception e) {
            e.printStackTrace(); //Does not get executed!
        }
        Connection conn = null;
        try
        {
            conn = DriverManager.getConnection("jdbc:derby:MyDbTest"); //Exception here!!
            System.out.println("Connected to database MyDbTest");
            Statement s = conn.createStatement();
            ResultSet rs = s.executeQuery(
                    "select string_id, RandString from Hashes");
            while(rs.next()) {
                int string_id = rs.getInt("string_id");
                String randString = rs.getString("RandString");
                System.out.printf("string_id: %s, RandString: %s\n", string_id, randString);
            }
        }
        catch (SQLException sqle)
        {
            printSQLException(sqle);
        } finally {
            try {
                if (conn != null) {
                    conn.close();
                    DriverManager.getConnection("jdbc:derby:;shutdown=true;deregister=false");
                }
            } catch (SQLException sqle) {
                printSQLException(sqle);
            }
        }
    }

    public static void printSQLException(SQLException e)
    {
        //According to this page:
        //https://db.apache.org/derby/papers/DerbyTut/embedded_intro.html
        //" A clean shutdown always throws SQL exception XJ015, which can be ignored."
        if(e.getSQLState().equals("XJ015")) return; //Ignore

        // Unwraps the entire exception chain to unveil the real cause of the
        // Exception.
        while (e != null)
        {
            System.err.println("\n----- SQLException -----");
            System.err.println("  SQL State:  " + e.getSQLState());
            System.err.println("  Error Code: " + e.getErrorCode());
            System.err.println("  Message:    " + e.getMessage());
            // for stack traces, refer to derby.log or uncomment this:
            e.printStackTrace(System.err);
            e = e.getNextException();
        }
    }
}

I seem to be able to load the ClientDriver initially, but then when I try to get a connection to the Database I get the Exception. The first line ensures the Current Working Directory is the root of my project where the MyDbTest database is located.

My build.gradle file looks like:

apply plugin: 'java'
apply plugin:'application'
mainClassName = "com.company.Main"

repositories {
    mavenLocal()
    mavenCentral()
}
dependencies {
    compile group: 'org.apache.derby', name: 'derbyclient', version: '10.14.1.0'
    compile group: 'org.apache.derby', name: 'derbytools', version: '10.14.1.0'
    runtime group: 'org.apache.derby', name: 'derbyclient', version: '10.14.1.0'
    runtime group: 'org.apache.derby', name: 'derbytools', version: '10.14.1.0'
}

A gradle run gives me:

Current working directory : /Users/<myname>/Documents/sources/tempFive/.

----- SQLException -----
  SQL State:  08001
  Error Code: 0
  Message:    No suitable driver found for jdbc:derby:MyDbTest
java.sql.SQLException: No suitable driver found for jdbc:derby:MyDbTest
    at java.sql.DriverManager.getConnection(DriverManager.java:689)
    at java.sql.DriverManager.getConnection(DriverManager.java:270)
    at com.company.Main.main(Main.java:25)

I have made sure that my Derby Client and my version of the Derby Database is the same. I'm on Mac OSX 10.12 using IntelliJ 2017.3 and Java 8 update 151 if that helps.


EDIT: Why this question is different

I have worked with Apache Derby before and I can get this code to work without Gradle. The two causes of this exception mentioned in the answer to the other question are:

  • The driver is not loaded. . - If this is true, then why do I not get an exception at the top where I try to load the ClientDriver (Or should I try to load some other class to exclude this possibility.)
  • The JDBC URL is malformed. - I don't have a problem with this JDBC URL in a non-Gradle Project, so I don't think my URL is wrong. Please correct me if this is incorrect though.

EDIT: Solution (Thanks to Mark Rotteveel)

I was apparently unaware that I was using the Embedded Driver and not the Client Driver. When I used this without Gradle, I used to put all the jars in the entire Derby Lib directory in the classpath without bothering about what was used and what wasn't. My bad. So the first thing to change would be delete the try catch in my main() method:

try {
       Class.forName("org.apache.derby.jdbc.ClientDriver").newInstance();
    } catch (Exception e) {
       e.printStackTrace(); //Does not get executed!
    }

The ClientDriver is never used, so there is no point in loading it. Next, as Mark Rotteveel pointed out I only need to depend on org.apache.derby:derby. This was my real mistake, so I put below the complete corrected build.gradle file:

apply plugin: 'java'
apply plugin:'application'
mainClassName = "com.company.Main"

repositories {
    mavenLocal()
    mavenCentral()
}
dependencies {
    compile group: 'org.apache.derby', name: 'derby', version: '10.14.1.0'
}

This is all I needed.


Solution

  • You are using the wrong Derby driver, by using derbyclient, you specify that you want to use the Derby (network) client driver, which supports the URL:

    jdbc:derby://server[:port]/databaseName[;URLAttributes=value[;...]]
    

    Your URL doesn't match, so it is rejected by the driver, and eventually java.sql.DriverManager will report that it didn't find a suitable driver.

    Instead you need to use the dependency org.apache.derby:derby, as you seem to want to use the Derby Embedded driver. This driver is included in the full derby package, which also includes the Derby database itself, but not in derbyclient. This driver supports URL format:

    jdbc:derby:databaseName;URLAttributes
    

    Alternatively you do want to connect to an externally running Derby server, and in that case you are using the wrong URL.

    See also Libraries provided by Derby and Database connection URL.