How to best set up multiple data sources with Guice

568 views
Skip to first unread message

Eelco Hillenius

unread,
Sep 8, 2009, 5:01:54 AM9/8/09
to google-guice
Hi,

I have two different data sources and I want them initialized through
properties files. When using Spring I would just instantiate the same
class twice with different parameters, but with Guice it is a little
bit less straightforward - at least initially, and in fact I already
like the end result better :-)

As I'm really just starting out with Guice, I'm wondering if the way
I'm going about it is the best way. So, I'd really appreciate it if
you could look at the following code fragment, and share whether you
think that's the best way to solve it, or how I can do it better.

If this is indeed the way to work with Guice, my next step would be to
either implement javax.sql.DataSource myself and wrap e.g.
com.mchange.v2.c3p0.ComboPooledDataSource (from 3cp0) or extend the
latter class directly and throw in a few Guice annotations for good
measure. Does that sound reasonable?

Thanks!

Eelco (code follows next):


defaults.properties:

udbDataSource.driverClassName=oracle.jdbc.driver.OracleDriver
udbDataSource.url=jdbc:oracle:thin:foo/bar:1526:baz
districtDataSource.driverClassName=oracle.jdbc.driver.OracleDriver
districtDataSource.url=jdbc:oracle:thin:hoi/pipeloi@localhost:
1526:oempf

Datasource base class:

public abstract class DataRef {
private final String driverClassName, url;

public DataRef(String driverClassName, String url) {
this.driverClassName = driverClassName;
this.url = url;
}
public String getDriverClassName() { return driverClassName; }
public String getUrl() { return url; }
public String toString() { return "{driver=" + driverClassName + ",
url=" + url + "}"; }
}

Two tagging annotations to differentiate between data source
instances:

@BindingAnnotation @Target( { FIELD, PARAMETER, METHOD }) @Retention
(RUNTIME)
public @interface UdbRef { }

@BindingAnnotation @Target( { FIELD, PARAMETER, METHOD }) @Retention
(RUNTIME)
public @interface DistrictRef { }

And then the data sources:

public class UdbDataRef extends DataRef {

@Inject
public UdbDataRef(
@Named("udbDataSource.driverClassName") String driverClassName,
@Named("udbDataSource.url") String url) {
super(driverClassName, url);
}
}

public class DistrictDataRef extends DataRef {

@Inject
public DistrictDataRef(
@Named("districtDataSource.driverClassName") String
driverClassName,
@Named("districtDataSource.url") String url) {
super(driverClassName, url);
}
}

A class that uses a specific data source:

public class Foo {
private final DataRef dataRef;

@Inject
public Foo(@UdbRef DataRef dataRef) {
this.dataRef = dataRef;
}

@Override
public String toString() {
return "dataRef: " + dataRef;
}
}

And finally the configuration and test:

public class Guice1Test {

private static class Bindings extends AbstractModule {

@Override
protected void configure() {
bind(DataRef.class).annotatedWith(DistrictRef.class).to
(DistrictDataRef.class);
bind(DataRef.class).annotatedWith(UdbRef.class).to
(UdbDataRef.class);
Properties properties = new Properties();
try {
properties.load(
Guice1Test.class.getResourceAsStream("defaults.properties"));
} catch (IOException e) {
throw new RuntimeException(e);
}
Names.bindProperties(binder(), properties);
}
}

public static void main(String[] args) {
Injector injector = Guice.createInjector(new Bindings());
Foo foo = injector.getInstance(Foo.class);
System.out.println(foo);
}
}

Robbie Vanbrabant

unread,
Sep 8, 2009, 4:49:07 PM9/8/09
to google...@googlegroups.com
I think the more natural solution here would be to use provider methods. Basically you would specify all the properties as parameters, new up the object and return.
See the second example here: http://code.google.com/p/google-guice/wiki/ProvidesMethods

Robbie

Eelco Hillenius

unread,
Sep 8, 2009, 5:31:59 PM9/8/09
to google...@googlegroups.com
Oh right, that is a lot nicer (no need for subclassing, still
statically typed). Thanks!

Eelco
Reply all
Reply to author
Forward
0 new messages