将 CDI 限定符转移到注入字段
Transferring CDI qualifier to injected field
我正在 Wildfly 8.2 容器中开发 Java EE 7 应用程序,其中包含一些存在于两个数据源中的实体。示例:
我有一个带有 Setting
实体的罐子:
@Entity
public class Setting {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@NotNull
private String name;
private String value;
getters/setters...
}
还有一个 bean,它有几种方法可以通过标准查询从数据库中检索和保存该实体:
@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;
}
...
}
我不想在类路径中包含此 jar 的应用程序中通过 @Producer 提供 EntityManager
和 Logger
实例,如下所示:
@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());
}
有没有办法在注入点配置 SettingRepository 并告诉它使用特定的实体管理器(@DataSource1
或 @DataSource2
)?
与此类似:
@Inject @DataSource1
private SettingRepository settingRepository;
是的!
事实上,就生产者方法和限定符而言,您已经完成了所有艰苦的工作:-)您需要做的就是在注入的 EntityManager 实例上使用限定符(@DataSource1 等)
@Inject @DataSource1
private EntityManager entityManager;
这将确保附加到持久性单元 'pu1' 的 JPA 实体管理器由容器注入。
我选择的路线是这条:
我创建了一个预选赛
@Qualifier
@Retention(RUNTIME)
@Target({ METHOD, FIELD, PARAMETER })
public @interface DataSource {
@Nonbinding DataSourceName value() default DataSourceName.D1;
}
请注意 @Nonbinding
注释,它告诉容器我在注释生产者方法时不必指定此参数(尤其是这个方法,因为我可以实现通用生产者,稍后会详细介绍)或注入点。
和DataSourceName
枚举,简单列出所有数据源:
public enum DataSourceName {
D1, D2
}
我还更改了 SettingRepository
实现并添加了 public 初始化方法。
@Dependent
public class SettingRepository {
@Inject
private Logger logger;
private EntityManager entityManager;
public void initialize(EntityManager entityManager) {
this.entityManager = entityManager;
}
...
}
注意EntityManager
不再被容器注入
当我不想注入 SettingRepository
时,我只是用这个限定符装饰它,就像这样:
@Inject @DataSource(DataSourceName.D1)
private SettingRepository settingRepository;
我现在需要的是为 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;
}
瞧,我可以在一个存储库实现中使用两个数据源(注意:数据源必须是 XA)。 如果有人发现这个选择有任何问题,请告诉我。
我正在 Wildfly 8.2 容器中开发 Java EE 7 应用程序,其中包含一些存在于两个数据源中的实体。示例:
我有一个带有 Setting
实体的罐子:
@Entity
public class Setting {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@NotNull
private String name;
private String value;
getters/setters...
}
还有一个 bean,它有几种方法可以通过标准查询从数据库中检索和保存该实体:
@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;
}
...
}
我不想在类路径中包含此 jar 的应用程序中通过 @Producer 提供 EntityManager
和 Logger
实例,如下所示:
@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());
}
有没有办法在注入点配置 SettingRepository 并告诉它使用特定的实体管理器(@DataSource1
或 @DataSource2
)?
与此类似:
@Inject @DataSource1
private SettingRepository settingRepository;
是的!
事实上,就生产者方法和限定符而言,您已经完成了所有艰苦的工作:-)您需要做的就是在注入的 EntityManager 实例上使用限定符(@DataSource1 等)
@Inject @DataSource1
private EntityManager entityManager;
这将确保附加到持久性单元 'pu1' 的 JPA 实体管理器由容器注入。
我选择的路线是这条:
我创建了一个预选赛
@Qualifier
@Retention(RUNTIME)
@Target({ METHOD, FIELD, PARAMETER })
public @interface DataSource {
@Nonbinding DataSourceName value() default DataSourceName.D1;
}
请注意 @Nonbinding
注释,它告诉容器我在注释生产者方法时不必指定此参数(尤其是这个方法,因为我可以实现通用生产者,稍后会详细介绍)或注入点。
和DataSourceName
枚举,简单列出所有数据源:
public enum DataSourceName {
D1, D2
}
我还更改了 SettingRepository
实现并添加了 public 初始化方法。
@Dependent
public class SettingRepository {
@Inject
private Logger logger;
private EntityManager entityManager;
public void initialize(EntityManager entityManager) {
this.entityManager = entityManager;
}
...
}
注意EntityManager
不再被容器注入
当我不想注入 SettingRepository
时,我只是用这个限定符装饰它,就像这样:
@Inject @DataSource(DataSourceName.D1)
private SettingRepository settingRepository;
我现在需要的是为 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;
}
瞧,我可以在一个存储库实现中使用两个数据源(注意:数据源必须是 XA)。 如果有人发现这个选择有任何问题,请告诉我。