1

I am trying to implement a ConnectionFactory using Enum Singleton and hitting a strange problem with loading the DB driver (Class.forName). Now, I know, Singleton is a controversial pattern and not everybody likes it, and yeah, I am not a big fan too. But this is for illustration purpose only.

Here is my main code:

public class HelloWorld {

    public static void main(String[] args) {
        ConnectionFactory.getConnection();
    }
}

And the ConnectionFactory:

public enum ConnectionFactory {
    INSTANCE;

    // Database details
    private static final String JDBC_DRIVER_PARAM="org.h2.Driver"; 
    private static final String DB_URL_PARAM="jdbc:h2:file:./h2database/homedb";
    private static final String DB_USER_PARAM="admin";
    private static final String DB_PASS_PARAM="pass";

    private ConnectionFactory()  {
        try {               
            Class.forName(JDBC_DRIVER_PARAM);
            System.out.println("Driver Loaded!");

        } catch (ClassNotFoundException e) {
            System.out.println(e);        
        }
    }

    private Connection createConnection() {
        Connection conn = null;
        try{            
            conn = DriverManager.getConnection(DB_URL_PARAM, DB_USER_PARAM, DB_PASS_PARAM);
        }catch (Exception e) {
            System.out.println(e);
        }
        return conn;
    }

    //public method to obtain connection
    public static Connection getConnection() {
        return INSTANCE.createConnection();
    }
}

And here is the output:

java.lang.ClassNotFoundException: org.h2.Driver 

Though, below works fine - so it is definitely not a classpath issue. I am running both approaches from Eclipse and it is the same maven project, so no issue with dependencies.

public class HelloWorld {

    public static void main(String[] a) throws Exception {
        Class.forName("org.h2.Driver");
        Connection conn = DriverManager.getConnection("jdbc:h2:file:./h2database/homedb", "admin", "pass");
        System.out.println("Connected");
        conn.close();
    }
}

And here is the output:

Connected

The only real difference I can see between the 2 approaches is the use of Enum Singleton. So my question: Is there something specific going on when I use Class.forName() from Enum Constructor? I thought, the constructor is called when enum is loaded by class loader, and that should be able to use other classes on the classpath. Am I missing something here?

Pat
  • 2,223
  • 4
  • 19
  • 25
  • 1
    This is not a singleton - there is no benefit to what you are doing. You are creating the connection on each call to `getConnection` - this defeats the object of having a singleton. – Boris the Spider Sep 13 '14 at 10:43
  • yeah I get it, it is a (very) bad design, and my `createConnection` is still work in progress. But what about the exception that I am running into? I am not looking for the best way to create ConnectionFactory, rather I am looking for the reason why I am hitting this exception. – Pat Sep 13 '14 at 10:49
  • That, especially, is a sign of bad design. It's a [legacy hangver](http://stackoverflow.com/a/5484254/2071828) that you should not be using. – Boris the Spider Sep 13 '14 at 10:52
  • So the exception `ClassNotFoundException` is because I am creating new connections for each `getConnection`? I think not. As I mentioned, this is for pure illustration purpose. I wouldn't dare put this in a real life production system ever! – Pat Sep 13 '14 at 10:56
  • The exception is because you are using a legacy, pre-JDBC4, approach to creating connections. Read up on how _modern_ JDBC works. – Boris the Spider Sep 13 '14 at 10:57
  • @BoristheSpider I agree with you that his approach is wrong but how was he able to run his program using the second version (without using his "singleton")? – M A Sep 13 '14 at 11:01
  • @Pat Are you sure it's not a classpath issue? – M A Sep 13 '14 at 11:03
  • @BoristheSpider doesn't explain how approach 2 worked. Both are using `Class.forName()` – Pat Sep 13 '14 at 11:11
  • @manouti i did not change anything in the entire project, just replaced the main() method with approach and it worked. So doesn't seem like classpath issue. I tried approach 2 specifically to ensure that it is not a classpath issue. – Pat Sep 13 '14 at 11:11
  • You also have a typo "Class.forName("org.h2.Driver"));" not sure if it would compile that way though... – Lucas Sep 13 '14 at 11:19
  • @Lucas ah thanks for that. Must have creeped in when I was formatting the code on SO. I have updated the code now. Still the same issue. – Pat Sep 13 '14 at 11:26
  • What does `prop.getProperty(JDBC_DRIVER_PARAM)` do? What is prop? Another singleton? **That** is the first difference I can spot. In your working example you are using an inlined string as the driver name instead. – Pyranja Sep 13 '14 at 11:48
  • @Pyranja sorry, I was loading that from properties file earlier. Mut have been left by mistake. Thanks for spotting it. Updated the code use hard coded values. – Pat Sep 13 '14 at 11:50
  • Do you run both versions in the exact same way, e.g. both from the command line with the same `-cp` parameter? You can also check the [`System.getProperty("java.class.path")`](http://docs.oracle.com/javase/tutorial/essential/environment/sysprop.html) to verify the H2 .jar is available. – Pyranja Sep 13 '14 at 11:59
  • No solution, but some advice.. Dont use an enum this way, just make a regular singleton. Enums are great singletons, but you should make use of the fact that its an enum (as in, strategy pattern for example), or dont use an enum. – Pienterekaak Oct 03 '14 at 09:32

0 Answers0