本文将简单谈谈我对 EJB 3.0 的两种 Persistence Context 和 Seam-managed Persistence Context 的不同点的理解、所要解决的问题和我自己所疑惑的问题。
EJB 3.0 (JPA) 的 Persistence Context
大家在使用 EJB 3.0 的时候会注意到 EJB 3.0 中的容器管理 Persistence Context 有两种类型,一种是 Transaction,另一种是 Extended。这是一个较 Hibernate 的 Session 所没有的概念,Session 没有两种不同的类型,而且最重要的是 Session 不是容器管理的,这里的容器指的是 App server 容器。这里暂时不谈论 Persistence Context 与 Session 之间的异同,主要谈谈两种 Persistence Context 之间的不同。学过 ORM 的同学都知道,当 Persistence Context 是打开状态的时候,Model 就处于被管理的状态中;当 Persistence Context 关闭之后,Model 就处于了 Detached 状态。
上面这些特性对于 Transaction 或 Extended 的 Persistence Context 都是一样的,不同的地方在于 Persistence Context 何时被打开关闭。由于绝大多数情况下 Persistence Context 是被容器管理的(如果你不嫌累也可以自己控制 Persistence Context),所以在 EJB 3.0 应用中看不到打开或关闭 Persistence Context 的代码(Spring + Hibernate 的应用也同样如此,Hibernate Session 的管理工作可以交给 Spring 来做)。
其实,Transaction 和 Extended Persistence Context 的不同之处也就在于容器何时打开或关闭 Persistence Context。Transaction 类型的 Persistence Context 的打开和关闭是和事务的打开和关闭是同步的。也就是说在一个事务开始之后,Persistence Context 才会开始;在事务关闭的时候,相应的 Persistence Context 也会被关闭。
Extended 类型的 Persistence Context 的打开和关闭是和 Stateful Session Bean 的生命周期同步的,是跨越事务的。也就是说,从 SFSB 的初始化开始,直到销毁,Persistence Context 都是存在的。你可以在事务之外执行写操作,但是这是并不会执行真正的数据库操作,写操作只是放入了队列,直到下一个事务,写操作才会真正地被执行。两者的不同简单说来就是 Extended Persistence Context 存在的时间更长。那为什么要有两种不同的 Persistence Context 呢?
当一个 Web 请求到来时,服务器会打开一个线程,这个线程可能会调用一个事务方法,这是一个事务便开始了,当这个请求结束时,线程关闭,事务也随之结束。由于 Transaction 类型的 Persistence Context 的生存周期是在事务范围之内的,所以一个 Web 请求的结束也意味着相应的 Persistence Context 的关闭。由于多数 Web 应用在一次 Web 请求内即可完成一个独立的操作,所以大部分情况下 Transaction 的 Persistence Context 是适用的。但是对于一些复杂的应用,一次操作需要跨越多次请求。这种情况下,如果依旧使用 Transcation 的 Persistence Context,由于每次请求结束后,相应的 Persistence Context 都被关闭,相应的 Model 也就变为 Detached 状态。如果接下来的请求仍然需要这些已经变为 Detached 状态的 Model 就需要重新 load,使用 merge() 方法来持久化。稍有不适就会产生 LazyInitializationException 和 NonUniqueObjectException。同时,这也提高了操作的复杂程度。
如果使用 Extended Persistence Context 就能解决这些问题。由于 Extended Persistence Context 的生命周期是与 SFSB 的生命周期同步的,所以只要多次请求调用的都是同一个 SFSB 中的方法,有多少次的请求,Persistence Context 总是同一个,其中的 Model 也始终是被管理的。很好地解决了 Persistence Context 在线程之间传递的问题,也不会有 LazyInitializationException 和 NonUniqueObjectException 问题的发生。