EJB3.0 学习教程(连载) 第三部分

 
                     第三部分 持久化
3.1    操作持久化Entity
对Entity进行操作的API都设计在javax.persistence.EntityManager接口上。EntityManager,顾名思义是管理所有EJB 3运行环境中的所有Entity。 EntityManager根据运行的环境不同分为容器管理的EntityManager和应用管理的EntityManager。Liberator EJB3是一个可以运行在J2SE环境中的 嵌入式的EJB3 Persistence实现,因此在Liberator EJB3中的 EntityManager都是指应用管理的EntityManager。
3.2    配置和获得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
每个EntityManagerFactory都必须有一个persistence.xml配置文件。下面是一个例子:
               
<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的语法。
3.3    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()方法删除,对应的纪录将会在当前事务提交的时候从数据库中删除。
3.4    持久化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.                
           
Liberator EJB3 支持Persistence by reachability。当保存一个Entity时,以该对象为根对象的整个对象图都会自动的被保存。但在EJB3中,我们仍然可以通过关系元数据(比如OneToOne,OneToMany)的cascade属性来精确定义保存的级联行为。 下面我们来看看不同的cascade属性的区别。
不配置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;
        }
    }
                 
现在来保存一个Father和Son。
               
    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();
                
现在我们配置casacde=CascadeType.ALL
               
    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;
        }
    }
                 
在代码中同样持久化Father和mySon。
               
    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();
                
由于Liberator EJB3运行环境采用了弱引用POJO管理技术,能很好的支持Persistence by reachablity而不会有其他产品有的潜在内存回收的问题, 因此建议在应用中尽可能使用cascade=CascadeType.ALL来减少持久化操作的复杂性和代码量,特别是在有复杂对象关系图的时候。
3.5    获取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 ) );
           
3.6    新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();
                
3.7    删除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();
                
3.8    脱离/附合(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的属性将无法访问。 

你可能感兴趣的:(EJB3.0 学习教程(连载) 第三部分)