EJB3.0之操作Entity
操作持久化Entity
对Entity进行操作的API都设计在javax.persistence.EntityManager接口上。EntityManager,顾名思义是管理所有EJB 3运行环境中的所有Entity。 EntityManager根据运行的环境不同分为容器管理的EntityManager和应用管理的EntityManager。Liberator EJB3是一个可以运行在J2SE环境中的 嵌入式的EJB3 Persistence实现,因此在Liberator EJB3中的 EntityManager都是指应用管理的EntityManager。
配置和获得EntityManager
在J2SE环境中,EJB3定义了一个javax.persistence.Persistence类用于启动EJB3运行环境。要获得EntityManager,首先需要通过javax.persistence.Persistence获得EntityManagerFactory,然后调用EntityManagerFactory.createEntityManager()方法获得。// 获得默认当前的EntityManagerFactory final EntityManagerFactory emf = Persistence.createEntityManagerFactory(); final EntityManager entityManager = emf.createEntityManager();
当调用Persistence.createEntityManagerFactory()的时候,Persistence会做以下的步骤:
- 搜索当前jar包的META-INFO/persistence.xml配置文件
- 如果没有在META-INFO下找到persistence.xml,搜索当前线程的ContextClassLoader中的persistence.xml
- 根据获得的persistence.xml初始化EntityManagerFactory
<entity-manager> <name>myEntityManager</name> <provider>com.redsoft.ejb3.PersistenceProviderImpl</provider> <class>com.redsoft.samples.HelloEntityBean</class> <properties> <property name="ConnectionDriverName" value="com.mysql.jdbc.Driver"/> <property name="ConnectionURL" value="jdbc:mysql://localhost/EJB3Test"/> <property name="ConnectionUserName" value="ejb3"/> <property name="ConnectionPassword" value="ejb3"/> <property name="RetainValues" value="true"/> <property name="RestoreValues" value="false"/> <property name="IgnoreCache" value="false"/> <property name="NontransactionalRead" value="true"/> <property name="NontransactionalWrite" value="false"/> <property name="Multithreaded" value="false"/> <property name="Liberator.option.action" value="create"/> <property name="Liberator.option.cache" value="com.redsoft.jdo.enterprise.OscacheAdapter"/> <property name="Liberator.option.JMXEnabled" value="false"/> <property name="Liberator.option.NamingPort" value="3012"/> <property name="Liberator.option.JMXPort" value="3011"/> <property name="Liberator.option.JDBC.scrollable" value="false"/> <property name="Liberator.option.JDBC.fetchSize" value="500"/> <property name="cache.algorithm" value="LRUCache"/> <property name="cache.capacity" value="5000"/> <property name="log4j.logger.com.redsoft" value="FATAL"/> </properties> </entity-manager>
<name></name> 定义一个EntityManagerFactory的名字,这样当应用中需要多个EntityManagerFactory的时候可以区别。通常情况下,一个EntityManagerFactory对应一个数据源。
<provider></provider> 定义采用的什么EJB3 Persistence运行环境(也就是EJB3 Persistence产品),例子中采用的的是Liberator EJB3的运行环境。正如前面说的,在EJB3规范中, EJB3 Persistence运行环境( Runtime )是作为一个独立的嵌入式模块,你可以随意的更换不同的EJB3 Persistence运行环境而不会影响你的应用程序。甚至在J2EE服务器内运行你也可以不用服务器本身提供的EJB3 Persistence运行环境,而采用第三方的EJB3 Persistence运行环境。
<class></class> 定义哪些类是Entity持久化类。如果是测试环境或者你不采用ear/war的方式发布你的应用,而只是把编译后的类直接拷贝到web容器中,在添加一个新的Entity类时,需要在这里添加一行。要求这个步骤是因为在J2EE 5架构中EJB3运行环境的嵌入式设计。采用这种松耦合设计使EJB3 Persistence运行环境不能控制持久化类的加载(由J2EE服务器负责加载), 这样EJB3 Persistence运行环境就无法分辨jvm中哪个类是持久化Entity类,哪个不是。因此需要一个额外的信息来让EJB3 Persistence知道那些是持久化Entity。
<property></property> 定义了了EJB3 Persistence运行环境需要的其他参数,这些参数不是标准的,而是由EJB3 Persistence运行环境的厂商自行定义。
详细的配置方法参看persistence.xml的语法。Entity的生命周期和状态
在EJB3中定义了四种Entity的状态:- 新实体(new)。Entity由应用产生,和EJB3 Persistence运行环境没有联系,也没有唯一的标示符(Identity)。
- 持久化实体(managed)。新实体和EJB3 Persistence运行环境产生关联(通过persist(), merge()等方法),在EJB3 Persistence运行环境中存在和被管理,标志是在EJB3 Persistence运行环境中有一个唯一的标示(Identity)。
- 分离的实体(detached)。Entity有唯一标示符,但它的标示符不被EJB3 Persistence运行环境管理, 同样的该Entity也不被EJB3 Persistence运行环境管理。
- 删除的实体(removed)。Entity被remove()方法删除,对应的纪录将会在当前事务提交的时候从数据库中删除。
持久化Entity(Persist)
final EntityManagerFactory emf = Persistence.createEntityManagerFactory(); final EntityManager entityManager = emf.createEntityManager(); final HelloEntityBean hello = new HelloEntityBean( 1, "foo" ); EntityTransaction trans = entityManager.getTransaction(); trans.begin(); // 持久化hello,在此操作之前hello的状态为new entityManager.persist( hello ); // 这时hello的状态变为managed trans.commit(); entityManager.close(); // 这时hellow的状态变为detached.
不配置cascade的情况下,EJB3 Persistence运行环境默认不会采用Persistence by reachability。
public class Father{ @Id int id String name; // OneToOne没有配置cascade属性,因此默认不会使用Persistence by reachablity @OneToOne Son mySon public Father( int id, String name, Son mySon ){ this.id = id; this.name = name; this.mySon = mySon; } }
final EntityManager manager = emf.createEntityManager(); manager.getTransaction().begin; Son mySon = new Son(); Father = new Father( 1, "father" mySon ); // 保存Father manager.persist( father ); // 由于OneToOne关系中没有配置casacade属性,father 关联的mySon不会被自动保存,需要分别保存 manager.persist( mySon ); manager.getTransaction().commit(); manager.close();
public class Father{ @Id int id String name; // OneToOne配置cascade=CascadeType.ALL,配置cascade=CascadeType.PERSIT也对persist操作也可以获得同样的效果。 // CascadeType.ALL包含CascadeType.PERSIST。 @OneToOne(cascade=CascadeType.ALL) Son mySon public Father( int id, String name, Son mySon ){ this.id = id; this.mySon = mySon; this.name = name; } }
final EntityManager manager = emf.createEntityManager(); manager.getTransaction().begin; Son mySon = new Son(); Father = new Father( 1, mySon ); // 保存Father。由于OneToOne关系中配置casacade=CascadeType.ALL属性,关联的mySon会自动地被持久化 manager.persist( father ); manager.getTransaction().commit(); manager.close();
获取Entity
如果知道Entity的唯一标示符,我们可以用find()方法来获得Entity。Father father = manager.find( Father.class, new Integer( 1 ) ); // 由于JDK1.5支持自动转型,也可以如下使用 Father father = manager.find( Father.class, 1 ); /* * 或者,可以用Entity名字作为查找。但无法利用JDK 1.5的自动转型功能, * 需要使用对象作为查找主键,并需要对获得Entity进行转型 */ Father father = (Father)manager.find( "com.redsoft.samples.Father", new Integer( 1 ) );
更新Entity
对Entity的更新必须在事物内完成。和persist中一样,关系元数据的cascade属性对是否集联删除有影响。transaction.begin(); Father father = manager.find( Father.class, 1 ); // 更新原始数据类型 father.setName( "newName" ); // 更新对象引用 Son newSon = new Son(); father.setSon( newSon ); // 提交事务,刚才的更新同步到数据库 transaction.commit();
删除Entity
对Entity的删除必须在事物内完成。transaction.begin(); Father father = manager.find( Father.class, 1 ); // 如果father/son的@OneToOne的cascade=CascadeType.ALL,在删除father时候,也会把son删除。 // 把cascade属性设为cascade=CascadeType.REMOVE有同样的效果。 manager.remove( father ); // 提交事务,刚才的更新同步到数据库 transaction.commit();
脱离/附合(Detach/Merge)
在三层或者分布式应用中,我们很多时候需要Entity能脱离EntityManager,避免长时间保持EntityManager打开占用资源和可以在不同的JVM之间传递Entity。
在脱离EJB3 Persistence Runtime(EntityManager)的管理后,我们仍然可以读取或者修改Entity中的内容。而在稍后的时间,我们又可以将Entity重新和原有或者新的EntityManager附合,如果附合前Entity被改动过,更改的数据可以自动的被发现并和数据库同步。
EntityManager entityManager = emf.createEntityManager(); // 这时Father还是被EntityManager管理的 Father father = manager.find( Father.class, 1 ); // 当entityManger关闭的时候,当前被entityManager管理的Entity都会自动的脱离EntityManager,状态转变为detached entityManager.close(); // 脱离EntityManager后,我们仍然可以修改Father的属性 father.setName( "newName" ); // 在稍后的,我们可以将father重新附和到一个新的或者原来的EntityManager中 EntityManager newEntityManager = emf.createEntityManager(); // 附合( merge )需要在事务中进行 newEntityManager.getTransaction().begin(); newEntityManager.merge( father ); // commit后father中的被修改的内容会同步到数据库。 newEntityManager.getTransaction().commit();
Liberator EJB3 Persistnece运行环境由于支持Persistence-by-reachablity。我们建议采用cascade=CascadeType.ALL,这样当需要附合的是一个对象图的时候,我们只需要merge根对象即可,整个对象图,对象图中被修改过的对象都会被自动识别和同步。
需要注意的是在脱离EJB3 Persistence Runtime的管理后,如果对象中有定义为lazy-load的属性将无法访问。