Java 将DB会话注入服务的调度程序的CDI作用域 上下文
我正在开发一个web应用程序,其中我尝试集成运行在TomEE容器中的Vaadin、DB4O和CDI。为了拥有数据库事务,我创建了ServletFilter,它截取所有请求并在请求结束时提交或回滚Java 将DB会话注入服务的调度程序的CDI作用域 上下文,java,cdi,Java,Cdi,我正在开发一个web应用程序,其中我尝试集成运行在TomEE容器中的Vaadin、DB4O和CDI。为了拥有数据库事务,我创建了ServletFilter,它截取所有请求并在请求结束时提交或回滚 @WebFilter(“/*”) 公共类DBTransactionHandler实现过滤器{ @注入 SessionImpl SessionImpl; @凌驾 公共空间销毁(){ //TODO自动生成的方法存根 } @凌驾 public void doFilter(ServletRequest请求、Se
@WebFilter(“/*”)
公共类DBTransactionHandler实现过滤器{
@注入
SessionImpl SessionImpl;
@凌驾
公共空间销毁(){
//TODO自动生成的方法存根
}
@凌驾
public void doFilter(ServletRequest请求、ServletResponse响应、FilterChain链)抛出IOException,
ServletException{
布尔hadException=false;
试一试{
链式过滤器(请求、响应);
}捕获(RuntimeException ex){
hadException=true;
掷骰子;
}最后{
if(sessionImpl!=null){
if(hadException | | sessionImpl.isRollbackOnly()){
sessionImpl.rollback();
}否则{
提交();
}
sessionImpl.close();
}
}
}
@凌驾
public void init(FilterConfig arg0)抛出ServletException{
//TODO自动生成的方法存根
}
}
我有一个SessionImpl,它被设置为@requestscope
。通过这种方式,我试图实现在处理HTTP请求时需要使用数据库的所有服务都应该获得相同的实例,因此它将在相同的数据库事务中执行
/**
* http://community.versant.com/documentation/reference/db4o-8.0/java/reference/Content/platform_specific_issues/web/servlets.htm
*/
@请求范围
公共类SessionImpl实现会话{
@注入
数据库连接工厂连接工厂;
私有布尔回滚;
私有对象容器委托;
@施工后
公共void init(){
委托=connectionFactory.getConnection().ext().openSession();
}
//…许多其他与数据库相关的方法
}
我的所有服务都是从AbstractService
派生的,因此他们立即有一个工作会话
公共类抽象服务{
@注入
受保护的BeanManager BeanManager;
@注入
受保护会话数据库;
}
这就是我目前的情况,我的问题来了:
问题
在我的web应用程序中,我需要创建一个调度程序组件。计划的作业将使用与我已有的相同的服务。由于SessionImpl
是@requestscope
的,并且我在计划作业中没有HTTP请求,因此无法注入SessionImpl
- 我可以从调度程序线程以某种方式激活RequestScope上下文吗
@SchedulerScoped
。这将在调度程序开始执行作业之前激活。这种方法的问题是,当我向SessionImpl
添加第二个作用域时,我的应用程序不再部署:
SEVERE: CDI Beans module deployment failed
org.apache.webbeans.exception.WebBeansConfigurationException: Managed Bean implementation class : org.reluxa.db.SessionImplstereotypes must declare the same @Scope annotations.
at org.apache.webbeans.config.DefinitionUtil.defineScopeType(DefinitionUtil.java:390)
at org.apache.webbeans.component.creation.AbstractBeanCreator.defineScopeType(AbstractBeanCreator.java:145)
at org.apache.webbeans.util.WebBeansUtil.defineManagedBean(WebBeansUtil.java:2548)
at org.apache.openejb.cdi.BeansDeployer.defineManagedBean(BeansDeployer.java:552)
at org.apache.openejb.cdi.OpenEJBLifecycle.deployManagedBeans(OpenEJBLifecycle.java:407)
这是一个有趣的问题。我将提出以下建议,作为我实际上没有尝试过的解决方案的概要,因此需要进行一些调整:
- 使
非CDI:删除SessionImpl
,@RequestScoped
@Inject
- 使用producer方法创建CDIBean,该方法创建会话,如下所示:
到目前为止,您的web配置应该和以前一样工作。根据需要进行测试和调整。(请测试每个线程只生成一个会话。)// I believe it should be @ApplicationScoped public class SessionProducer { private ThreadLocal<Session> currentSession; @Produces Session makeSession() { return currentSession.get(); } public ThreadLocal<Session> getCurrentSessionThreadLocal() { return currentSession; } }
- 现在,您必须在调度程序中重复创建和销毁仪式。这样,服务仍将看到有效的
,而不需要请求范围。这种“仪式”可能是可以考虑的(例如,执行这些操作的调度器的CDI或EJB拦截器是可重用的,并且不会用这样的系统逻辑污染组件的业务逻辑)会话
@WebFilter("/*")
public class DBTransactionHandler implements Filter {
@Inject
DBConnectionFactory connectionFactory;
@Inject
SessionProducer sessionProducer;
...
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
boolean hadException = false;
try {
SessionImpl sessionImpl = new SessionImpl();
sessionImpl.setConnectionFactory(connectionFactory);
sessionProducer.getCurrentSessionThreadLocal().set(sessionImpl);
// here you probably want to call sessionImpl.init();
chain.doFilter(request, response);
} catch (RuntimeException ex) {
hadException = true;
throw ex;
} finally {
if (sessionImpl != null) {
if (hadException || sessionImpl.isRollbackOnly()) {
sessionImpl.rollback();
} else {
sessionImpl.commit();
}
sessionImpl.close();
sessionProducer.getCurrentSessionThreadLocal().set(null);
}
}
}
...
}