Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Guice: how to get more than one @Provides for a type?

I'm working on a project and trying to bring two different 'data service' modules together into a web app (currently, the app is a desktop Swing app).

Each module has its own Guice (private) module. Each Guice module contains:

@Provides
@Inject
protected JPQLQuery provideJPQLQuery(EntityManager entityManager) {
    return new JPAQuery(entityManager);
}

This is used later in the constructor of the classes that look things up from the db:

@Inject
public SomeClassThatLooksObjectsUpFromDatabase(Provider<JPQLQuery> queryProvider) {
    this.queryProvider = queryProvider;
}

The 'queryProvider' is then able to execute queries.

Now, this works fine when only one of the Guice modules is installed, but once both are installed, I (predictably) get this error:

Unable to create binding for com.mysema.query.jpa.JPQLQuery.  It was already configured on one or more child injectors or private modules
bound at ServiceOneGuiceModule.provideJPQLQuery()
bound at ServiceTwoGuiceModule.provideJPQLQuery()

Now, I understand why this is broken -- I'm saying that there's two Providers for the type JPQLQuery and Guice doesn't know which one to use.

Is there any way I can get Guice to separate these Providers? I would like to do this because each module has its own properly-configured Hibernate entities, and each has its own unique datasource (multiple databases in this project).

Ideally, it would involve something like somehow naming these providers and injecting them by their name (e.g. I could separately inject "ServiceOneJPQLQueryProvider" and "ServiceTwoJPQLQueryProvider"), but I haven't found any way of achieving anything like this.

(I suppose an alternative is to somehow configure Hibernate so it has all the different datasources it needs and then I'd only need one Provider for my queries, possibly, but that seems like a lot more work than what I'm describing above)

like image 779
user3183922 Avatar asked Jan 11 '14 01:01

user3183922


1 Answers

Have a look binding annotations, they're used to solve just the problem you've got.

They're recommended over using @Named because they are type-safe and will produce compilation errors and not runtime errors if you misspell them.

In short:

ServiceOne.java:

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

ServiceTwo.java:

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

ServiceOneModule.java:

@Provides
@ServiceOne
@Inject
protected JPQLQuery provideJPQLQuery(EntityManager entityManager) {
    return new JPAQuery(entityManager);
}

SomeClass.java:

@Inject
public SomeClassThatLooksObjectsUpFromDatabase(@ServiceOne Provider<JPQLQuery> queryProvider) {
    this.queryProvider = queryProvider;
}
like image 176
Raniz Avatar answered Oct 29 '22 16:10

Raniz