Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Use Case]: use CDI qualifier annotations to select a data source #476

Open
1 of 4 tasks
gavinking opened this issue Feb 22, 2024 · 2 comments
Open
1 of 4 tasks

[Use Case]: use CDI qualifier annotations to select a data source #476

gavinking opened this issue Feb 22, 2024 · 2 comments
Labels
enhancement New feature or request

Comments

@gavinking
Copy link
Contributor

As a ...

  • Application user/user of the configuration itself
  • API user (application developer)
  • SPI user (container or runtime developer)
  • Specification implementer

I need to be able to ...

specify the data source that backs a repository via CDI qualifier annotations.

Which enables me to ...

avoid the use of stringly-typed names.

Additional information

Currently, a datasource, persistence unit, etc, that backs a repository implementation must be specified using @Repository(dataStore=".....") which works, but is not properly integrated with the CDI bean manager.

It would be nice to be able to disambiguate the data source or persistence unit or whatever via the placement of CDI qualifier annotations on the repository interface. Unfortunately, there's no obvious completely natural place to put them.

  • Interfaces don't have constructors, so we can't put them on a constructor parameter.
  • Interfaces don't have non-static fields, so we can't put them there either.
  • A qualifier annotation on the interface itself is most naturally interpreted as specifying the CDI qualifiers of the repository itself, not of the datasource backing it.

So, the only options I can really imagine are the following.

Use of method injection

The data source could be viewed as being injected via a method, which is a concept which does already exist in CDI.

@Repository
interface DocumentRepo {

    @Inject 
    void init(@DocumentDatabase DataSource ds);

     ...

}

The problem with this is that it exposes to clients the possibility of manipulating the state of the repository. (And interface methods are always public.)

So this is no good. I would say we definitely don't want to do it this way.

Annotate the resource access method

I actually think this option is pretty good and natural. Probably most repositories are going to want to have a resource accessor method, and so that's a place we can put these annotations.

@Repository
interface DocumentRepo {

    @DocumentDatabase 
    DataSource datasource();

     ...

}

It's true that this is a non-standard place to place qualifier annotations in the sense that it's not contemplated by the CDI spec, but to me that's completely fine. The repository implementation can easily obtain the qualifiers from this method declaration and use them to look up the data source at runtime. Alternatively, an annotation processor can copy them onto an injection point of the repository implementation.

@gavinking
Copy link
Contributor Author

Note that a proposed implementation of this idea is in #474.

@gavinking
Copy link
Contributor Author

gavinking commented Feb 25, 2024

So in implementing the spec, I really struggled a bit to know what to do with the dataSource in the @Repository annotation. I really didn't want to map it to the old legacy JNDI stuff.

In the end what I ended up with was this, which is I believe right for Quarkus, and almost correct for a "regular" Jakarta EE server:

@TransactionScoped
@Generated("org.hibernate.processor.HibernateProcessor")
public class Repo_ implements Repo {

	private @Nonnull StatelessSession session;
	
	// constructor for testing
	public Repo_(@Nonnull StatelessSession session) {
		this.session = session;
	}
	
	// resource accessor method
	@Override
	public @Nonnull StatelessSession session() {
		return session;
	}
	
	// injection of the persistence unit
	@Inject
	@PersistenceUnit(unitName="myds")
	private EntityManagerFactory sessionFactory;
	
	// constructor for CDI
	@Inject
	Repo_() {
	}
	
	// create a session after injection (ugh)
	@PostConstruct
	private void openSession() {
		session = sessionFactory.unwrap(SessionFactory.class).openStatelessSession();
	}
	
	// clean up the session
	@PreDestroy
	private void closeSession() {
		session.close();
	}

	...

}

This is OK, but definitely isn't as clean as I would have preferred.

The critical bit here is this:

@Inject
@PersistenceUnit(unitName="my-persistence-unit")
private EntityManagerFactory sessionFactory;

The dataSource is interpreted as a JPA persistence unit name and maps to the unitName of @PersistenceUnit.

The actual code is a bit nasty because @PersistenceUnit was defined well before CDI existed and doesn't allow constructor injection.

Now, if the persistence unit were identified by CDI qualifier annotations, I could just replace the @PersistenceUnit with the qualifiers in this generated code, or I could do even better and clean up this mess to just use constructor injection.

@gavinking gavinking added this to the Jakarta Data Future milestone Mar 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant