当前位置:首页 > 全部子站 > IT > 思科认证

在WEB容器中获取持久化上下文

来源:长理培训发布时间:2017-12-19 16:30:55

 百度广告

  考试吧Java站整理收集

  在EJB容器,如果你愿意,由容器注入一个EntityManager后,你可以把一切交给容器管理.如果你愿意使用可扩展事务, 当然你已经了解如何管理EntityManager.规范在那儿定着,没有什么可多说的. 在Java SE环境下,没有容器能帮助你做任何事,一切都要由你纯手工创建,当然一切也由你负责管理,关闭,销毁.都是你的事,所以反而变得简单(是说获取持久化手段简单了,不是操作单了).

  一.容器管理持久化上下文

  而在WEB容器中,一方面容器管理的持久化事务不能象EJB容器那样可以使用扩展性事务,要想让容器管理,那么只能是 JTA事务,而且数据源也一定是JTA-DATA-SOURCE.常有人在网上找到一些persistence.xml,要么使用了本地事务,要么是非JTA的数据源,问我为什么在WEB容器中不能成功注EntityManager.

  要在WEB容器中让容器管理持久化事务,可以通过两种方式,一是容器注入,一是JNDI查找.这两种方式并不是每个人都理解的,我看到一本叫的书,在网上受到太多的人的追捧(越是这样的书受害者就越多!), 明确说"容器托管的EntityManager只能运行在EJB容器中,也就是说只有在EJB-JAR包中,才可以获得容器托管的 EntityManager对象,否则只能获得应用托管的EntityManager对象."

  事实上确实一些WEB容器不能注入EntityManager,也不能通过JNDI查找到.但是容器不支持并不是规范不支持,如果你使用glassfish,或者用resin 3.1.2以上(目前已经3.1.6)你就可以方便地获得容器管理的EntityManager.

  

  

  

  jdbc/__axman

  com.axman.CustomerEO

  

  

  

  

  

  jdbc/__axman

  com.axman.CustomerEO

  

  只要这个persistence .xml文件在类路径中,就可以正确地注入EntityManager:

  //为了阅读方便删除了无关内容和注释

  public class TestAxman extends HttpServlet {

  @PersistenceContext(name="jpaUnit")

  private EntityManager em;

  @Resource

  private UserTransaction utx;

  protected void service(HttpServletRequest request, HttpServletResponse response)

  throws ServletException, IOException {

  PrintWriter out = response.getWriter();

  CustomerEO ce = new CustomerEO();

  ce.setName("p4");

  ce.setEmail("p4@email.com");

  ce.setAsset(12.7d);

  ce.setCreateDate(new java.sql.Date(new java.util.Date().getTime()));

  utx.begin();

  em.persist(ce);

  utx.commit();

  //容器管理事务不需要自己手工回滚,只要告诉容器事务起止边界

  }

  }|||   

  public class TestAxman extends HttpServlet {

  @PersistenceContext(name="jpaUnit")

  private EntityManager em;

  @Resource

  private UserTransaction utx;

  protected void service(HttpServletRequest request, HttpServletResponse response)

  throws ServletException, IOException {

  PrintWriter out = response.getWriter();

  CustomerEO ce = new CustomerEO();

  ce.setName("p4");

  ce.setEmail("p4@email.com");

  ce.setAsset(12.7d);

  ce.setCreateDate(new java.sql.Date(new java.util.Date().getTime()));

  utx.begin();

  em.persist(ce);

  utx.commit();

  //容器管理事务不需要自己手工回滚,只要告诉容器事务起止边界

  }

  }

  public class TestAxman extends HttpServlet

  {

  @PersistenceContext(name="jpaUnit")

  private EntityManager em;

  @Resource

  private UserTransaction utx;

  protected void service(HttpServletRequest request, HttpServletResponse response)

  throws ServletException, IOException {

  PrintWriter out = response.getWriter();

  CustomerEO ce = new CustomerEO();

  ce.setName("p4");

  ce.setEmail("p4@email.com");

  ce.setAsset(12.7d);

  ce.setCreateDate(new java.sql.Date(new java.util.Date().getTime()));

  utx.begin();

  em.persist(ce);

  utx.commit();

  //容器管理事务不需要自己手工回滚,只要告诉容器事务起止边界

  }

  }

  这样注入进来的EntityManager完全由容器管理,不要做任何EntityManager相关的工作.但是一切就这样完美了吗? 当然不是,和EJB容器不同的是,相当于有状态会话BEAN的Servlet是多线程服务的,一个实例变量的EntityManager 完全可能会被多个线程同时访问而出现极大的安全性隐患.那么这样的注入是否有意义呢? 当然有意义,一种情况是你可以在ServletContextListener这样的线程安全模块中注入,另一种情况只要你能控制不让多个线程同时访问一个实例变量的EntityManager,那么你就可以享受容器管理带来的方便性.

  但是任何事物都有两面性,如果你要获取方便,就要牺牲应用范围的控制和性能,要让多个线程不同时访问一个实例变量EntityManager,最终还是要进行同步或互斥.即一个线程使用变量EntityManager时其它线程都要等待.如果能在线程的local方法中(doXXX或父类的service方法中)获取由容器管理的EntityManager, 那将会大大提高程序的性能. "容器管理"的意思,其实就是容器产生了一些对象,你只需要拿来使用,不需要负责它的产生和销毁,关键是容器产生了这样的对象后你如何"拿到".通过注入只能注入成实例字段,那么在线程方法中,可能通过JNDI 来即时获取"容器中的EntityManager".(实事上,只要改一下容器实现的源码,还可以通过在service方法中获取ServletContext对象来动态即时注入,但这对于普通程序员是做不到的).

  通过JNDI查找的jndi-ref也可以通过注释或通过web.xml配置.两种方法都可以: 注入jndi-ref:

  @PersistenceContext(name="persistence/jpaUnit",unitName="jpaUnit")

  public class TestAxman extends HttpServlet {

  protected void service(HttpServletRequest request,HttpServletResponse response)

  throws ServletException, IOException {

  Context env =(Context)newInitialContext().lookup("java:comp/env");

  EntityManager em = (EntityManager)env.lookup("persistence/jpaUnit");

  Query query = em.createQuery("SELECT c from CustomerEO c");

  List ls = query.getResultList();

  }

  }|||   @PersistenceContext(name="persistence/jpaUnit",unitName="jpaUnit")

  public class TestAxman extends HttpServlet {

  protected void service(HttpServletRequest request,HttpServletResponse response)

  throws ServletException, IOException {

  Context env =(Context)newInitialContext().lookup("java:comp/env");

  EntityManager em = (EntityManager)env.lookup("persistence/jpaUnit");

  Query query = em.createQuery("SELECT c from CustomerEO c");

  List ls = query.getResultList();

  }

  }如果你不想用注释可以在web.xml中配置:

  

  persistence/jpaUnit

  jpaUnit

  

  

  persistence/jpaUnit

  jpaUnit

  然后可以同样通过

  Context env =(Context)new InitialContext().lookup("java:comp/env");

  EntityManager em = (EntityManager)env.lookup("persistence/jpaUnit"); 查找到EntityManager.

  需要说明的是,JNDI查看本身是耗时的,特别是new InitialContext().所以应该在一个全局的位置创建一个env (相当于工场).然后在service方法中通过这个env来查找EntityManager就可以大量地提升性能,比如:

  public class TestAxman extends HttpServlet {

  Context env = null;

  protected void init(ServletConfig config) throws ServletException {

  env = (Context)newInitialContext().lookup("java:comp/env");

  }

  protected void service(HttpServletRequest request, HttpServletResponse response)

  throws ServletException, IOException {

  EntityManager em = (EntityManager)env.lookup("persistence/jpaUnit");

  Query query = em.createQuery("SELECT c from CustomerEO c");

  List ls = query.getResultList();

  }

  }

  public class TestAxman extends HttpServlet {

  Context env = null;

  protected void init(ServletConfig config) throws ServletException {

  env = (Context)newInitialContext().lookup("java:comp/env");

  }

  protected void service(HttpServletRequest request, HttpServletResponse response)

  throws ServletException, IOException {

  EntityManager em = (EntityManager)env.lookup("persistence/jpaUnit");

  Query query = em.createQuery("SELECT c from CustomerEO c");

  List ls = query.getResultList();

  }

  }

  如果你需要容器管理持久化,这种方案是最合适的方式.虽然EntityManager 每次要在service中lookup出来,但它是

  方法内的local变量,不象注入成实例变量那样存在多线程安全隐患.

  如果你需要容器管理持久化,这种方案是最合适的方式.虽然EntityManager 每次要在service中lookup出来,但它是 如果你需要容器管理持久化,这种方案是最合适的方式.虽然EntityManager 每次要在service中lookup出来,但它是

  方法内的local变量,不象注入成实例变量那样存在多线程安全隐患. 方法内的local变量,不象注入成实例变量那样存在多线程安全隐患.

  二.应用管理持久化上下文:

  应用管理持久化上下文,事实上就是EntityManager对象不是由容器负责产生和销毁.而是由应用程序来创建,当然是 由应用程序来销毁.要由应用程序来创建持久化上下文,就是要由EntityManagerFactory来进行createEntityManager. 本着谁生产谁负责的原则,当然要程序来负责销毁.所以应用管理的EntityManager一定要在finally语句中调用close() 方法.这样多少给我们使用它带来不便.但它也因为是应用程序创建,所以有着广泛的应用范围,无论是EJB容器,还是WEB 容器,或者是纯Java SE环境,都可以使用JPA功能.

  要在WEB容器获取EntityManagerFactory,同样可以通过注入和手工创建.明白"容器管理"的意义应该知道,注入 是容器已经产生了的对象,所以EntityManagerFactory如果是容器注入的,同样不需要你手工销毁,而如果是手工 创建的,则需要手工销毁.简单说EntityManagerFactory对象本身也可以容器管理的:

  public class TestServlet extends HttpServlet {

  @PersistenceUnit(unitName = "jpaUnit")

  private EntityManagerFactory emf;

  public void service(……) throws xxxExceotion{

  EntityManager em = emf. createEntityManager();

  try{

  //invoke em;

  }|||   finally{ em.close();} // EntityManager本身是应用程序创建的,

  //所以必须手工关闭.如果这里是写入操作,事务还必须在cacth块中手工回滚

  }

  }

  public class TestServlet extends HttpServlet {

  @PersistenceUnit(unitName = "jpaUnit")

  private EntityManagerFactory emf;

  public void service(……) throws xxxExceotion{

  EntityManager em = emf. createEntityManager();

  try{

  //invoke em;

  }

  finally{ em.close();} // EntityManager本身是应用程序创建的,

  //所以必须手工关闭.如果这里是写入操作,事务还必须在cacth块中手工回滚

  }

  }如果你不想通过容器注入EntityManagerFactory,只要调用

  EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpaUnit");

  就可以获取一个手工创建的EntityManagerFactory,但是要记得在创建它的对应位置手要销毁它.如:

  public class TestServlet extends HttpServlet {

  private EntityManagerFactory emf;

  void init(......){

  emf = Persistence.createEntityManagerFactory("jpaUnit");

  }

  void destory(......){

  if(emf != null) emf.close();

  }

  public void service(......) throws xxxExceotion{

  EntityManager em = emf. createEntityManager();

  try{

  //invoke em;

  }

  finally{ em.close();} // EntityManager本身是应用程序创建的,

  //所以必须手工关闭.

  }

  }

  public class TestServlet extends HttpServlet {

  private EntityManagerFactory emf;

  void init(......){

  emf = Persistence.createEntityManagerFactory("jpaUnit");

  }

  void destory(......){

  if(emf != null) emf.close();

  }

  public void service(......) throws xxxExceotion{

  EntityManager em = emf. createEntityManager();

  try{

  //invoke em;

  }

  finally{ em.close();} // EntityManager本身是应用程序创建的,

  //所以必须手工关闭.

  }

  }因为EntityManagerFactory是工场对象,所以上面的例子并不好,最好的位置是在SerlvetContextListener中注入然后放在 ServletContext中,或在ContextListener的contextInitialized中手工生成,在contextDestroyed中销毁,生成后放入 ServletContext中供全局访问.即一个应用只有一个工场,而EntityManager是在service方法中通过EntityManagerFactory 即时生成的,这样既可以提高性能,又保证了线程安全性.可以说是一个非常正确的方案.

  同样上面的那本书中,在Servlet的init方法中生成一个实例对象EntityManagerFactory后,在doPost方法中获取 EntityManager,本来是一个非常好的方案,但却说是不安全的,非要用一个辅助的类来调用ThreadLocal来在doPost方法中获取EntityManager.难道不同线程的doPost方法内的local变量也能被其它线程访问?纯是蛇足之举.

责编:罗莉

发表评论(共0条评论)
请自觉遵守互联网相关政策法规,评论内容只代表网友观点,发表审核后显示!

国家电网校园招聘考试直播课程通关班

  • 讲师:刘萍萍 / 谢楠
  • 课时:160h
  • 价格 4580

特色双名师解密新课程高频考点,送国家电网教材讲义,助力一次通关

配套通关班送国网在线题库一套

课程专业名称
讲师
课时
查看课程

国家电网招聘考试录播视频课程

  • 讲师:崔莹莹 / 刘萍萍
  • 课时:180h
  • 价格 3580

特色解密新课程高频考点,免费学习,助力一次通关

配套全套国网视频课程免费学习

课程专业名称
讲师
课时
查看课程
在线题库
面授课程更多>>
图书商城更多>>
在线报名
  • 报考专业:
    *(必填)
  • 姓名:
    *(必填)
  • 手机号码:
    *(必填)
返回顶部