Reputation: 707
I'm trying to figure out how to do with with CDI. I'm working on an application where users are able to connect to multiple databases. The Application asks which they want to connect to and they can also disconnect and connect to a different database. This is a Java SE application using Seam for CDI and Hibernate for JPA. What I'm trying to figure out is how to leverage CDI to wire up @PersistenceContext but also make it dynamic so different databases can be accessed. I'm just not sure the pattern or technique I would use to do this with CDI. Thoughts?
Upvotes: 2
Views: 540
Reputation: 707
So I think I figured out what I wanted to do by using javax.enterprise.inject.Instance
. First define a simple bean:
@Alternative
public class Foo {
private int value;
public void setValue(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
It is defined as an @Alternative
so CDI doesn't get confused between this and the producer method (shown next). A better solution would be too define Foo
as an interface then FooImpl
would be annotated with @Typed
so CDI thinks it's only a FooImpl
bean type. Anyway, next is a producer class.
@ApplicationScoped
public class FooProducer {
private int value;
public FooProducer() {
value = -1;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
@Produces
public Foo getFoo() {
Foo p = new Foo();
p.setValue(getValue());
return p;
}
}
The getFoo()
method produces a new Foo
object with different values. The value can be changed by the setValue(int)
method. Next I use the javax.enterprise.inject.Instance
to inject Foo
.
@Inject
Instance<Foo> fooInstance;
@Inject
FooProducer fooProducer;
....
fooProducer.setValue(10);
Foo foo = fooInstance.get();
System.out.printf("foo = %s, %d\n", foo.getValue());
fooProducer.setValue(10000);
foo = fooInstance.get();
System.out.printf("foo = %s, %d\n", foo.getValue());
Here I use the injected fooProducer.setValue()
method to change how the producer will produce Foo
. When fooInstance.get()
is called the first time Foo
will contain the value 10 and the second time it will have the value 10000.
Given this simple example, it is easy to apply to getting EntityManager
instances to different databases at runtime. There is a bit more to the code which produces the EntityManager
but not too much.
Upvotes: 2
Reputation: 3533
PersistenceContext is handled by an EJB container. Since you are not in an EJB container, forget the persistence context and consider using CDI producers.
@ApplicationScoped
public class EntityManagerFactoryProvider {
private EntityManagerFactory emf;
public void voidApplicationEMF() {
//load the EMF here based on dynamic properties
this.emf = Persistence.createEntityManagerFactpry(Map of loaded properties);
}
@Produces
public EntityManager em() {
return emf.createEntityManager();
}
}
Then somewhere in your code
@SessionScoped
public class MyEntityController {
@Inject
private EntityManager emf;
}
There are still issues to deal with transactions etc.
In a JSE environment, you must explicitly start and commit/rollback transactions. Since you are using CDI, you can use Interceptors to start-of/check for transaction status and rollback/commit as necessary.
@Interceptor
public class MyInterceptor {
@Inject
private EntityManager em;
@AroundInvoke
public Object intercept(InvocationContext ic) {
try {
//initiate transaction
em.getTransaction().start ....
return ic.proceed();
}catch(Exception ex) { //rollback if necessary }
finally {//commit transaction if necessary }
}
}
Upvotes: 0