首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Guice Provider<EntityManager>诉EntityManager

Guice Provider<EntityManager>诉EntityManager
EN

Stack Overflow用户
提问于 2015-02-28 21:37:36
回答 1查看 3.4K关注 0票数 5

我试图让简单的webapp在Jetty上使用Guice和JPA,使用persistence和servlet扩展。

我编写了这个服务实现类:

代码语言:javascript
复制
public class PersonServiceImpl implements PersonService {

private EntityManager em;

@Inject
public PersonServiceImpl(EntityManager em) {
    this.em = em;
}

@Override
@Transactional
public void savePerson(Person p) {
    em.persist(p);
}

@Override
public Person findPerson(long id) {
    return em.find(Person.class, id);
}

@Override
@Transactional
public void deletePerson(Person p) {
    em.remove(p);
}

}

这是我的servlet (用@Singleton注释):

代码语言:javascript
复制
@Inject
PersonService personService;

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
    String name = req.getParameter("name");
    String password = req.getParameter("password");
    String email = req.getParameter("email");
    int age = Integer.valueOf(req.getParameter("age"));


    Person p = new Person();
    p.setAge(age);
    p.setName(name);
    p.setEmail(email);
    p.setPassword(password.toCharArray());

    logger.info("saving person");

    personService.savePerson(p);
    logger.info("saved person");

    logger.info("extracting person");
    Person person = personService.findPerson(p.getId());
    resp.getWriter().print("Hello " + person.getName());
}

当我运行它时,它会工作,并且我会将名称发送给客户机,但是当我查看日志时,我发现没有为插入生成DML,并且postgresql中的选择不返回任何结果,这意味着它并不是真正的持久化。

我在代码中进行了调试,并看到JpaLocalTxnInterceptor调用了txn.commit()

然后,我对PersonServiceImpl做了一个更改,使用了Provider<EntityManager>,而不是只使用了EntityManager,它如预期的那样工作。现在我不太明白为什么,也可能是因为我不太明白提供者背后的想法。在Guice wiki页面上写着:

请注意,如果您将MyService设置为@Singleton,则应该使用注入提供者。

但是,我的PersonServiceImpl不是@Singleton,所以我不知道它为什么应用,也许是因为Servlet?

如果你能帮我澄清这件事,我会非常感激的。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-03-01 10:22:46

您需要Provider<EntityManager>,因为Guice的内置持久性和servlet扩展都希望EntityManager是请求范围的。通过从单例servlet中保存的服务注入请求作用域的EntityManager,您将进行范围扩大的注入,并且Guice不会存储来自陈旧的、不匹配的EntityManager的数据。

提供者

提供程序是一个公开get()方法的单方法接口。如果注入一个Provider<Foo>,然后调用get(),它将返回一个与直接注入Foo相同的方法创建的实例。但是,注入提供程序允许您控制创建了多少个对象,以及创建对象的时间。这在一些情况下是有用的:

  • 只有在实际需要时才创建实例,特别是如果创建需要大量时间或内存
  • 在同一组件中创建两个或多个独立实例
  • 将创建推迟到初始化方法或单独的线程。
  • 混合作用域,如下所述

对于XProvider<X>@Provides X的绑定,Guice将自动允许直接注入XProvider<X>。您可以在不调整任何绑定的情况下使用提供程序,并且提供程序可以很好地使用绑定注释

范围和范围-扩大注射

广义地说,范围定义对象的生存期。默认情况下,Guice为每个注入创建一个新对象;通过标记一个对象@Singleton,可以指示Guice为每个注入注入相同的实例。Guice的servlet扩展还支持@RequestScoped和@SessionScoped注入,这会使同一个对象在一个请求(或会话)内一致地被注入,但是会为一个不同的请求(或会话)注入一个新的对象。Guice还允许您定义自定义作用域,例如线程范围 (每个线程有一个实例,但在同一个线程中的注入中有相同的实例)。

代码语言:javascript
复制
@Singleton public class YourClass {
  @Inject HttpServletRequest request;  // BAD IDEA
}

如果直接从@Singleton组件中注入请求作用域对象,会发生什么情况?当创建单例时,它尝试注入与当前请求相关的实例。请注意,可能没有当前请求,但如果存在请求,则实例将保存到单例中的字段中。随着请求的来来去去,单例永远不会被重新创建,而且字段也不会被重新分配--所以在第一个请求之后,组件就停止正常工作了。

将窄范围对象(@RequestScoped)注入到宽范围(@Singleton)称为范围扩大注入。并不是所有的范围扩大的注射都会立即出现症状,但所有这些都可能会在稍后引入挥之不去的bug。

提供者如何帮助

PersonService没有使用@Singleton注释,但是由于您在@Singleton servlet中注入和存储一个实例,所以它可能也是一个单独的实例。这意味着,出于同样的原因,EntityManager也有单例行为。

根据你引用的那一页的说法,EntityManager应该是短暂的,只存在于会话或请求中。这允许Guice在会话或请求结束时自动提交事务,但是重用相同的EntityManager可能会阻止在第一个会话或请求结束后的任何时候存储数据。切换到提供程序允许您通过对每个请求创建一个新的EntityManager来缩小范围。

(您还可以使PersonService成为一个提供者,这也可能解决问题,但我认为最好是观察Guice的最佳实践,并将EntityManager的范围与提供者显式缩小。)

票数 13
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/28787238

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档