Reputation: 813
I'm working on a Java EE 7 application in a Wildfly 8.2 container which contains some entites that exist in two data sources. Example:
I have a jar with Setting
entity:
@Entity
public class Setting {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@NotNull
private String name;
private String value;
getters/setters...
}
and a bean which has a couple of methods for retrieving and saving this entity from database via criteria query:
@Stateless
public class SettingRepository {
@Inject
private Logger logger;
@Inject
private EntityManager entityManager;
public Setting findByName(@NotNull String name) {
logger.trace("Getting setting by name: name=" + name);
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Setting> cq = cb.createQuery(Setting.class);
Root<Setting> table = cq.from(Setting.class);
cq.where(cb.equal(table.get(Setting_.name), name));
TypedQuery<Setting> query = entityManager.createQuery(cq);
List<Setting> results = query.getResultList();
Setting setting = null;
if (results.size() > 0)
setting = results.get(0);
logger.trace("Got setting: " + setting);
return setting;
}
...
}
I wan't to provide EntityManager
and Logger
instances via @Producer in a application which includes this jar in classpath like this:
@Produces @DataSource1 @PersistenceContext(unitName = "pu1")
private EntityManager entityManager1;
@Produces @DataSource2 @PersistenceContext(unitName = "pu2")
private EntityManager entityManager2;
@Produces
private Logger produceLogger(InjectionPoint injectionPoint) {
return LogManager.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
}
Is there any way to configure SettingRepository at injection point and tell it to use specific entity manager (@DataSource1
or @DataSource2
)?
Similar to this:
@Inject @DataSource1
private SettingRepository settingRepository;
Upvotes: 2
Views: 476
Reputation: 813
The route I chose to take is this one:
I created a qualifier
@Qualifier
@Retention(RUNTIME)
@Target({ METHOD, FIELD, PARAMETER })
public @interface DataSource {
@Nonbinding DataSourceName value() default DataSourceName.D1;
}
Note the @Nonbinding
annotation which tells container I don't have to specify this parameter when annotating producer method (especially this one, sine I can implement generic producer, more on that later) or injection point.
And DataSourceName
enumeration, which simply lists all data sources:
public enum DataSourceName {
D1, D2
}
I also changed SettingRepository
implementation and added a public initialize method.
@Dependent
public class SettingRepository {
@Inject
private Logger logger;
private EntityManager entityManager;
public void initialize(EntityManager entityManager) {
this.entityManager = entityManager;
}
...
}
Note that EntityManager
is no longer injected by container.
When I wan't to inject SettingRepository
, I simply decorate it with this qualifier like so:
@Inject @DataSource(DataSourceName.D1)
private SettingRepository settingRepository;
All I need now is to define a producer for SettingRepository
:
@Stateless
public class TestResourcesForSettings {
@PersistenceContext(unitName = "pu1")
private EntityManager entityManager1;
@PersistenceContext(unitName = "pu2")
private EntityManager entityManager2;
@Inject
private SettingRepository settingRepository;
@Produces @DataSource
public SettingRepository produceSettingRepository(InjectionPoint ip) {
DataSource annotation = ip.getAnnotated().getAnnotation(DataSource.class);
if (annotation.value() == DataSourceName.D1)
settingRepository.initialize(entityManager1);
else if (annotation.value() == DataSourceName.D2)
settingRepository.initialize(entityManager2);
return settingRepository;
}
And voila, I can use two data sources with one repository implementation (note: data sources have to be XA). If anyone finds any problems with this choice please tell me.
Upvotes: 1
Reputation: 1225
Yes !
In fact you have done all the hard work already as far as producer methods and qualifiers are concerned :-) All you need to do is use your qualifier (@DataSource1 etc) on the injected EntityManager instance
@Inject @DataSource1
private EntityManager entityManager;
This will ensure that JPA entity manager attached to persistence unit 'pu1' is injected by the container.
Upvotes: 0