Search code examples
javaguicejooqspark-java

SparkJava JOOQ dependency injection with Guice


I am writing simple CRUD application which would fetch person records from database and I'm using SparkJava framework I have working code which fetches records from database but I would want to extract JOOQ DSLContext code and inject it as a bean and initialize it in another class in order to have more cleaner code, but I'm not sure how to achieve it here's main method which currently hold everything:

 public static void main(String[] args) throws IOException {
    final BasicDataSource ds = new BasicDataSource();
    final Properties properties = new Properties();
    properties.load(BankApiApplication.class.getResourceAsStream("/application.properties"));
    ds.setDriverClassName(properties.getProperty("db.driver"));
    ds.setUrl(properties.getProperty("db.url"));
    ds.setUsername(properties.getProperty("db.username"));
    ds.setPassword(properties.getProperty("db.password"));

    final ConnectionProvider cp = new DataSourceConnectionProvider(ds);
    final Configuration configuration = new DefaultConfiguration()
            .set(cp)
            .set(SQLDialect.H2)
            .set(new ThreadLocalTransactionProvider(cp, true));
    final DSLContext ctx = DSL.using(configuration);
    final JSONFormat format = new JSONFormat().format(true).header(false).recordFormat(JSONFormat.RecordFormat.OBJECT);

    port(8080);

    get("/persons", (request, response) -> {
        return ctx.select().from(Person.PERSON).fetch().formatJSON();
    });
}

How I could extract code which initializes Datasource and configures DSLContext and instead I could just inject DSLContext or some kind of DSLContextHolder and do querying ?


Solution

  • So, in general, you want to inject the highest-level object you can. This is related to the Law of Demeter, which in short says that a component can know about its direct dependencies, but it shouldn't know about those dependencies' dependencies.

    In your case, you're really only using DSLContext (ctx). [A note here: your code has a lot of two-letter names - it's pretty hard to follow. It would be easier if you wrote out e.g. ctx -> dslContext, cp -> connectionProvider]. This means you really only want your method to know about the DSLContext, not its dependencies. Therefore, it would be good to pull the following out into a module, then inject just a DSLContext:

    1. Configuration
    2. ConnectionProvider
    3. Properties
    4. BasicDataSource

    If all these things are only used in this one main(), you can write a single Provider to return a DSLContext. If some of these are used in multiple places (for more than instantiating this main()'s DSLContext), then they can go in their own Providers. For example, here's what a Provider for a DSLContext would look like, if Configuration was placed in its own Provider:

    public class MyModule extends AbstractModule {
    
        // other providers
        // ...
    
        @Provides
        @Singleton
        public DSLContext dslContext(Configuration configuration) {
            return SL.using(configuration);
        }
    }
    

    Then, in your main(), you would write:

    Injector injector = Guice.createInjector(yourModule());
    DSLContext myContext = injector.getInstance(DSLContext.class);
    
    // ... use it