Java Guice提供者<;实体管理器>;vs实体管理器
我试图让简单的webapp在Jetty上使用Guice和JPA,使用持久性和servlet Guice扩展 我编写了以下服务实现类:Java Guice提供者<;实体管理器>;vs实体管理器,java,jpa,guice,guice-servlet,guice-persist,Java,Jpa,Guice,Guice Servlet,Guice Persist,我试图让简单的webapp在Jetty上使用Guice和JPA,使用持久性和servlet Guice扩展 我编写了以下服务实现类: public class PersonServiceImpl implements PersonService { private EntityManager em; @Inject public PersonServiceImpl(EntityManager em) { this.em = em; } @Override @Transactiona
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注释):
当我运行它时,它工作了,我得到了发送给客户端的名称,但是当我查看日志时,我看到没有为插入生成DML,并且postgresql中的选择没有返回任何结果,这意味着它没有真正持久化
我对代码进行了调试,发现JpaLocalTxnInterceptor
调用了txn.commit()
然后我更改了PersonServiceImpl
,并使用了Provider
,而不仅仅是EntityManager
,它按预期工作。现在我真的不明白为什么,可能是因为我真的不明白提供商背后的想法。
上面写着:
请注意,如果您将MyService设置为@Singleton,那么您应该插入Provider
然而,我的PersonServiceImpl不是@Singleton,所以我不知道为什么它适用,也许是因为Servlet
如果您能帮我解决这个问题,我将不胜感激。您需要提供程序
,因为Guice内置的持久性和servlet扩展要求EntityManager具有请求范围。通过从单例servlet中保存的服务注入请求范围的EntityManager,您正在进行范围扩展注入,Guice将不会存储来自过时的、不匹配的EntityManager的数据
提供者
Provider是一个方法接口,它公开一个get()
方法。如果您插入一个提供程序
,然后调用get()
,它将返回一个以与直接插入Foo相同的方式创建的实例。但是,注入提供程序允许您控制创建了多少对象以及创建对象的时间
- 仅在实际需要时创建实例,特别是在创建需要大量时间或内存的情况下
- 从同一组件中创建两个或多个独立实例
- 将创建延迟到初始化方法或单独的线程
- 混合范围,如下所述
X
、提供程序
或@提供的X
,Guice将自动允许您直接插入X
或提供程序
。您可以在不调整任何绑定的情况下使用提供程序,并且提供程序可以很好地使用
作用域和作用域加宽注入
广义地说,定义对象的生存期。默认情况下,Guice为每次注入创建一个新对象;通过将对象标记为@Singleton,可以指示Guice为每次注入注入相同的实例。Guice的servlet扩展还支持@RequestScope和@SessionScoped注入,这会导致在一个请求(或会话)中一致地注入相同的对象,但为不同的请求(或会话)注入新对象。Guice还允许您定义自定义作用域,例如(每个线程一个实例,但在同一线程中跨注入的同一实例)
如果直接从@Singleton组件中注入请求范围的对象,会发生什么?创建singleton时,它会尝试注入与当前请求相关的实例。请注意,可能没有当前请求,但如果有,则实例将保存到singleton中的一个字段中。随着请求的来来去去去,单例永远不会被重新创建,字段也永远不会被重新分配——因此在第一次请求之后,组件就停止正常工作
将窄范围对象(@RequestScoped)注入宽范围(@Singleton)称为范围加宽注入。并不是所有的范围扩大注射都会立即出现症状,但所有注射都可能在以后引入挥之不去的bug
供应商如何提供帮助
PersonService没有用@Singleton注释,但因为您正在@Singleton servlet中注入并存储一个实例,所以它也可能是一个Singleton本身。这意味着出于同样的原因,EntityManager也有单例行为
根据,EntityManager是短暂的,只存在于会话或请求中。这允许Guice在会话或请求结束时自动提交事务,但重用同一个EntityManager可能会阻止在第一个会话或请求结束后的任何时间存储数据。切换到提供者允许您通过在每个请求上创建一个新的EntityManager来缩小范围
(您还可以让PersonService成为一个提供者,这也可能会解决问题,但我认为最好观察Guice的最佳实践,并让EntityManager的范围与提供者明确地缩小。)
@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());
}
@Singleton public class YourClass {
@Inject HttpServletRequest request; // BAD IDEA
}