OpenJPA

1 Overview
    Apache OpenJPA是JPA规范的一个实现,它既可以用于POJO的持久层,也可以被集成到EJB3.0兼容的容器中或者其它轻量级的框架中。在Apache Geronimo 2.0 版本中通过OpenEJB使用了OpenJPA。在WebLogic和WebShpere中也采用了OpenJPA。目前OpenJPA的最新版本是1.0.2。在OpenJPA中大量使用了generic和annotation,因此需要使用1.5以上版本的JDK。

    以下是JPA中使用的主要组件:

  • Persistence: javax.persistence.Persistence类包含静态方法用于获得EntityManagerFactory对象。
  • EntityManagerFactory: javax.persistence.EntityManagerFactory类是创建EntityManager的工厂类。
  • EntityManager: javax.persistence.EntityManager是应用中主要使用的接口,它主要用于管理持久对象,也用于创建Query 接口。
  • Entity。Entity用于封装持久对象。
  • EntityTransaction: EntityTransaction 用于封装事务,javax.persistence.EntityTransaction和EntityManager之间是一对一的关系。
  • Query: javax.persistence.Query接口用于持久对象的查询。它支持Java Persistence Query Language (JPQL) 和 Structured Query Language (SQL)。
  • PersistenceException: JPA异常体系的根是PersistenceException,它继承于RuntimeException。OpenJPA中抛出的异常都实现了org.apache.openjpa.util.ExceptionInfo接口,用于提供额外的信息。

   以下是使用JPA的一个例子:

Java代码  收藏代码
  1. EntityManagerFactory factory = Persistence.createEntityManagerFactory(null);  
  2. EntityManager em = factory.createEntityManager(PersistenceContextType.EXTENDED);  
  3. EntityTransaction tx = em.getTransaction();  
  4. tx.begin();  
  5. Query query = em.createQuery("select e from Employee e where e.division.name = 'Research' AND e.avgHours > 40");  
  6. List results = query.getResultList ();  
  7. for (Object res : results) {  
  8.     Employee emp = (Employee) res;  
  9.     emp.setSalary(emp.getSalary() * 1.1);  
  10. }  
  11. tx.commit();  
  12. em.close();  
  13. factory.close();  

 

2 Entity
    JPA 区分两种不同的persistent class: entity classes 和 embeddable classes. EntityManager可以通过persistent identity来查询entity 实例,或者在Query中指定条件来查询entity实例。在另一方面,embedded实例没有persistent identity,也不能直接从EntityManager或者Query的查询中得到。

2.1 Restrictions on Persistent Classes
    在编写persistent class的时候,不需要继承任何父类或者实现任何接口。 但是persistent class需要一个无参构造函数。如果需要的话,OpenJPA的enhancer也可以创建一个protected 无参构造函数,所以当使用enhancer的时候,persistent class可以不提供无参构造函数。OpenJPA支持final classes 和 final methods。
    所有的entity classes必须提供一个或者多个字段来组成一个persistent identity。你可以为persistent class提供一个version字段,用来检测并发修改,这个字段必须是整数型的(例如int,Long)或者是java.sql.Timestamp。version字段应该是不可变(immutable)的。除了version字段以外,OpenJPA也支持通过其它的方式检测并发修改。
    JPA支持persistent class的继承,但是persistent class不能继承自一些包含native实现的系统类,例如:java.net.Socket 和 java.lang.Thread。如果一个persistent class继承自non-persistent class,那么non-persistent class中的字段不能被持久化。具有继承关系的所有类必须有相同的identity type。

2.2 Entity Identity
    Java中有两种判断object identity的方式:使用==操作符判断同一个JVM中两个引用的值是否相等,或者说指向相同对象;使用equals 方法来判断两个对象是否满足定制的相等条件。JPA中引入了另外一种判断object identity的方式,称为entity identity或者 persistent identity。Entity identity被封装到持久对象的identity字段中,如果两个相同类型的entities拥有相同的identity字段,那么这两个entities代表datastore中相同的状态。Identity字段必须是以下类型:primitives、primitive wrappers、 Strings、Dates、Timestamps或者embeddable types。
    当你处理single persistence context的时候,可以不必通过比较identity字段来判断这些entities是否代表datastore中相同的状态,而是可以通过==操作符。JPA要求每一个persistence context中只能保持一个object来代表每一个datastore record,因此entity identity相当于引用相等。

 

2.2.1 Identity Class
    如果entity 中只有一个identity字段,那么可以将这个字段用作所有EntityManager APIs的identity object;否则必须提供一个identity class,它必须符合以下条件:

  • The class must be public.
  • The class must be serializable.
  • The class must have a public no-args constructor.
  • The names of the non-static fields or properties of the class must be the same as the names of the identity fields or properties of the corresponding entity class, and the types must be identical.
  • The equals and hashCode methods of the class must use the values of all fields or properties corresponding to identity fields or properties in the entity class.
  • If the class is an inner class, it must be static.
  • All entity classes related by inheritance must use the same identity class, or else each entity class must have its own identity class whose inheritance hierarchy mirrors the inheritance hierarchy of the owning entity classes.

   以下是个使用多个identity字段的entity class的例子:

Java代码  收藏代码
  1. import javax.persistence.Entity;  
  2. import javax.persistence.Id;  
  3. import javax.persistence.IdClass;  
  4.   
  5. @Entity  
  6. @IdClass(Magazine.MagazineId.class)  
  7. public class Magazine {  
  8.     @Id  
  9.     private String isbn;  
  10.       
  11.     @Id  
  12.     private String title;  
  13.   
  14.     public String toString() {  
  15.         StringBuffer sb = new StringBuffer();  
  16.         sb.append("isbn: " + isbn);  
  17.         sb.append(", title: " + title);  
  18.         return sb.toString();  
  19.     }  
  20.       
  21.     public String getIsbn() {  
  22.         return isbn;  
  23.     }  
  24.   
  25.     public void setIsbn(String isbn) {  
  26.         this.isbn = isbn;  
  27.     }  
  28.   
  29.     public String getTitle() {  
  30.         return title;  
  31.     }  
  32.   
  33.     public void setTitle(String title) {  
  34.         this.title = title;  
  35.     }  
  36.       
  37.     public static class MagazineId {  
  38.         // each identity field in the Magazine class must have a  
  39.         // corresponding field in the identity class  
  40.         private String isbn;  
  41.         private String title;  
  42.           
  43.         public MagazineId() {  
  44.         }  
  45.           
  46.         public MagazineId(String isbn, String title) {  
  47.             this.isbn = isbn;  
  48.             this.title = title;  
  49.         }  
  50.           
  51.         /** 
  52.          * Equality must be implemented in terms of identity field 
  53.          * equality, and must use instanceof rather than comparing  
  54.          * classes directly (some JPA implementations may subclass the 
  55.          * identity class). 
  56.          */  
  57.         public boolean equals(Object obj) {  
  58.             if(this == obj) {  
  59.                 return true;  
  60.             }  
  61.             if(!(obj instanceof MagazineId)) {  
  62.                 return false;  
  63.             }  
  64.               
  65.             MagazineId rhs = (MagazineId)obj;  
  66.             boolean b1 = (isbn == rhs.isbn || (isbn != null && isbn.equals(rhs.isbn)));  
  67.             boolean b2 = (title == rhs.title || (title != null && title.equals(rhs.title)));  
  68.             return b1 && b2;  
  69.         }  
  70.           
  71.         /** 
  72.          * Hashcode must also depend on identity values. 
  73.          */  
  74.         public int hashCode(){  
  75.             int h1 = (isbn == null ? 0 : isbn.hashCode());  
  76.             int h2 = (title == null ? 0 : title.hashCode());  
  77.             return h1 ^ h2;  
  78.         }  
  79.           
  80.         public String toString() {  
  81.             StringBuffer sb = new StringBuffer();  
  82.             sb.append("isbn: " + isbn);  
  83.             sb.append(", title: " + title);  
  84.             return sb.toString();  
  85.         }  
  86.     }  
  87. }  

   除了手工编写Id class之外,也可以通过ApplicationIdTool自动生成Id class,例如:

   java org.apache.openjpa.enhance.ApplicationIdTool -s Id Magazine.java
    关于其可选的命令行参数,请参考Apache OpenJPA的user guide。
    如果觉得在命令行上执行命令比较麻烦,也可以在程序里直接调用其main方法,如下:

Java代码  收藏代码
  1. String suffix = "Id";  
  2. String java = "./src/com/example/entity/Product.java";  
  3. ApplicationIdTool.main(new String[]{"-d""./src/""-s", suffix, java});  

   关于如何在类的继承体系中编写identity class,请参考Apache OpenJPA的user guide。

 

2.3 Lifecycle Callbacks
    经常需要在persistent object 生命周期的不同阶段实施不用的动作。JPA包含了多种不同的callback 方法来监控persistent object。这些callback方法可以在persistent classes 中定义,也可以在non-persistent listener classes 中定义。

2.3.1 Callback Methods
    每一个persistence event都有相关的callback方法标记,如下:

  • PrePersist: 用这个annotation 标记的方法会在对象被持久化之前调用。这个方法可以用来为持久化对象设置主键。
  • PostPersist: 用这个annotation 标记的方法会在对象被持久化之后调用。这个方法可以用来在model被保存后更新view。
  • PostLoad: 用这个annotation 标记的方法会在对象中所有eagerly fetched 字段被加载后调用。这个方法通常用来根据加载后的持久化字段的值更初始化非持久字段的值。
  • PreUpdate: 用这个annotation 标记的方法会在对象中持久字段的值被flush到datastore前被调用。 这个方法通常用来根据非持久字段的值来设置持久字段。
  • PostUpdate: 用这个annotation 标记的方法会在对象中持久字段值的改变被保存到datastore后调用。这个方法可以用来清空应用层的过期数据。
  • PreRemove: 用这个annotation 标记的方法会在对象经事务标记成已删除状态前调用。在这个方法中访问持久字段是合法的。在这个方法中,可以基于复杂的条件级联删除其它对象,或者做其它的清理工作。
  • PostRemove: 用这个annotation 标记的方法会在对象被标记成已删除状态后调用。

   除了属性存取方法外,任何没有参数的方法都可以用以上annotation标记。一个方法也可以使用多个annotation标记。以下是个persistent classes的例子

Java代码  收藏代码
  1. /** 
  2.  * Example persistent class declaring our entity listener. 
  3.  */  
  4. @Entity  
  5. public class Magazine {  
  6.   
  7.     @Transient   
  8.     private byte[][] data;  
  9.   
  10.     @ManyToMany  
  11.     private List<Photo> photos;  
  12.   
  13.     @PostLoad  
  14.     public void convertPhotos() {  
  15.         data = new byte[photos.size()][];  
  16.         for (int i = 0; i < photos.size(); i++)  
  17.             data[i] = photos.get(i).toByteArray();  
  18.     }  
  19.   
  20.     @PreDelete  
  21.     public void logMagazineDeletion() {  
  22.         getLog().debug("deleting magazine containing" + photos.size()   
  23.             + " photos.");  
  24.     }  
  25. }  

   也可以使用XML文件,如下:  

Xml代码  收藏代码
  1. <entity class="Magazine">  
  2.     <pre-remove>logMagazineDeletion</pre-remove>  
  3.     <post-load>convertPhotos</post-load>  
  4. </entity>  

 

2.3.2 Entity Listeners
    在persistent classes 中加入Lifecycle Callbacks并不总是理想,更优雅的方式是在 non-persistent listener class 中处理生命周期内的相关事件。Entity listener classes需要有一个无参的构造函数,callback 方法需要有个java.lang.Object 型的参数来指定激发事件的持久对象。Entities可以通过EntityListeners这个annotation来枚举listeners。以下是个Entity和Entity Listener的例子:

Java代码  收藏代码
  1. /** 
  2.  * Example persistent class declaring our entity listener. 
  3.  */  
  4. @Entity  
  5. @EntityListeners({ MagazineLogger.class, ... })  
  6. public class Magazine {  
  7.   
  8.     // ... //  
  9. }  
  10.   
  11.   
  12. /** 
  13.  * Example entity listener. 
  14.  */  
  15. public class MagazineLogger {  
  16.   
  17.     @PostPersist  
  18.     public void logAddition(Object pc) {  
  19.         getLog ().debug ("Added new magazine:" + ((Magazine) pc).getTitle ());  
  20.     }  
  21.   
  22.   
  23.     @PreRemove  
  24.     public void logDeletion(Object pc) {  
  25.         getLog().debug("Removing from circulation:" +   
  26.             ((Magazine) pc).getTitle());  
  27.     }  
  28. }  

   也可以在XML文件中定义Entity Listeners,如下: 

Xml代码  收藏代码
  1. <entity class="Magazine">  
  2.     <entity-listeners>  
  3.         <entity-listener class="MagazineLogger">  
  4.             <post-persist>logAddition</post-persist>  
  5.             <pre-remove>logDeletion</pre-remove>  
  6.         </entity-listener>  
  7.     </entity-listeners>  
  8. </entity>  

   关于entity listeners的调用顺序,默认listener会最先被调用;接下来,父类的listeners会先调用,然后是子类的listeners;最后,如果entity上还有某个事件的多个callback方法,那么会按照这些callback方法在entity上声明顺序来调用。

   可以使用以下两个class-level的annotation,以便在entity listeners的调用链上去掉默认listener和父类中定义的listener:

  • ExcludeDefaultListeners: 这个annotation 表明这个类及其子类上不再调用默认listeners。
  • ExcludeSuperclassListeners: 这个annotation 指示OpenJPA不调用父类上声明的任何entity listeners。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值