原帖地址:http://blog.csdn.net/bq_cui/archive/2007/03/21/1536706.aspx
开始学习seam的时候,有两个问题:我会用Spring和Hibernate,但一点也不懂Seam 和EJB3的 EntityManager。我用了一些时间学习seam,但EntityManager 一直困扰我。
同时我也有了一些小收获,愿意跟刚刚开始学习seam的朋友分享以下。别紧张,我不敢确信我写的东西都正确 ;).
好了,关于EntityManager有何用处?它管理你的entities ;)。 那是一些简单的java对象,通过getters and
setters具备一些属性。这些属性之一是id(一般是Long数据类型),并且这些class必须以@Entity注解(annotated
)。在seam的源码中可以找到一大批这样的例子,例如booking例子。在seam中,一件很重要的事情就是,每个Entity都有一个
@Name 注解(annotation),这样,它们才能被注入到其他seam部件(component)中。
假设我们有这样一个entity class,叫做"Entity"。其生命周期内包含以下功能:
增
查
删
改
EntityManager 提供了这些功能。首先,如何把EntityManager 引入我的代码?很简单:
@PersistenceContext
private EntityManager em;
好了,我们看看一个Entity 进程如何产生:
Entity entity = new Entity();
这很简单。现在,这entity 的状态是NEW/TRANSIENT 。这意味着一个entity已经存在于你的应用程序中,但并不具有id,也不存在于你的数据库中。
由于我们要使它持久化(即它应被写入数据库),我们应把它的状态转换为MANAGED 。
em.persist(entity);
现在,此entity由EntityManager管理了。EntityManager控制entity写入数据库。这动作无须立刻发生,可能把你的entity放在cache,稍后写入数据库。你可以放心,写动作肯定会发生。
Ok, what about reading an existing entity from the database? Therefore we use:
好,如何从数据库中读出已存在的entity呢?这样:
Entity entity = em.find(Entity.class, Id);
每个entity 有一个id(我已经说过,多数情况下是Long数据类型),通过id你可存取entity。这是这里的第二个参数。第一个参数代表你要存取的Entity class的进程。find操作之后,entity的状态也是MANAGED 。
一旦entity数据有所改变,将反映到数据库中。不能确认EntityManager何时会向数据库写入更新的数据。但是这一事件肯定会发生,一般是马上,但不会晚于EntityManager消失;)。你可以控制触发更新数据库:
em.flush();
这将强制EntityManager 立刻将更新写入数据库。请记住,这条语句将影响所有状态为MANAGED的entitie,而不是当前一个。不过,一般情况下不必这么做。
如果你想反其道而行之,也就是从数据库载入一个entity (因为别人可能已经修改其数据),这么写:
em.refresh(entity);
怎样删除一个entity呢?很简单:
em.remove(entity);
现在,entity的状态成为REMOVED,表示其已经被列入删除计划。你可以用flush()语句,让删除动作马上发生,但没必要这么干。
现在来点复杂的。当注入EntityManager 的时候,有一个事务范围持久上下文( Transaction Scoped Persistence Context)。当entitie处在MANAGED状态时,其处在持久上下文(persistence context )的“容器”中。“事务范围”意味什么?首先,什么是“事务”?
EJB3Stateful 和 Stateless beans (依同名标注辨别)的每个方法调用都包装到一个事务内。(顺便说一下,事务一旦发生RuntimeException,即发生会滚,对数据的改变将撤销)。因此,持久上下文(persistence context)将在调用方式之前建立,方式调用结束后移除。然后,持久上下文中管理的所有entity的状态成为 DETACHED。
假设在你的bean里头有两个method。第一个是load(), 它调用find函数取出数据库中的一个实体。第二个是finish(),返回一个JSF输出。调用这两个函数中间修改了实体数据。这种修改持久到数据库中吗?回答是:NO。
load() 函数结束后,EntityManager'的持久上下文结束,它所有管理过的entity状态成为DETACHED。一个后果是,与新entity相反-有一个ID,但他们的状态并不是managed ,对这些detached entities 所作的更新并不影响数据库。如果你想使一个entity 更新数据库,需要将其重新attach到持久上下文。在这个例子中,在finish()方法中增加以下行:
em.merge(entity);
现在这个entity 已经合并到finish()方法的持久上下文中(记住,每个方法是一个事务,每个事物有自己的持久上下文),并且状态是又变为managed。
这样做是可以的,但有两个缺点:
-需要调用merge(->多了一些代码)
-调用find()之前,如果某个entity未初始化,而你又要存取它的某些属性,将会得到exception 错误。
有一个简单的解决方案:扩展持久上下文的生命期,这样,当调用多个事务/方法时,entity状态始终保持为managed 。因此,我们改变EntityManager的注入方式:
@PersistenceContext(type=PersistenceContextType.EXTENDED
private EntityManager em;
现在,managed entities "生活在"一个扩展持久上下文中( Extended Persistence Context )。
你甚至无须呼叫merge()方法,因为eitities从未被DETACHED。你可能会问:普通事务范围( "normal" (transaction scoped) )持久上下文的好处是什么?它一直依附于你做的事情(小崔:什么意思?)。扩展上下文需要更多的内存,因为即使你不需要,它也一直存在于内存。并且一旦entities被其他的bean改变(它们有自己的持久上下文),你需要显式地调用refresh()方法(在overviews/list页面)。当你需要普通EntityManager 时它就在那里,因其生命周期短,只能操作即时数据;)
当谈及列表:为取得不止一个实体,而是一个实体集合,使用:
List<Entity> entities = em.createQuery("from Entity").getResultList();
这不是“真正”SQL,而是类似sql的东西,称之为EJBQL。你可以用它执行某些条件查询,排序等,如
..."from Entity where lastName=".nameToSearchFor." order by firstName"
仅使用entity属性名称即可。关于EJBQL 要说的太多了,但对于本篇介绍来说这已足够。
好,基本介绍到此为止。希望对你来说,在如何使用EntityManager方面,这一个简单易懂的介绍。
我已声明过,不能保证百分百正确。并且肯定有许多英语语法错误(对不起,我是德国人,不要打我PP噢)。欢迎斧正。
2007.4.11翻译自:
http://www.jboss.com/index.html?module=bb&op=viewtopic&t=88460