Spring2.5+OpenJPA的配置

最近总想写点什么,正好研究了一下OpenJPA,大概通读了一下新出的1.2版本的官方文档,然后自己做了一个小例子。因为Spring2.5的推出,增加了许多新特性,市面上关于怎样整合Spring和OpenJPA的书、包括网上文章也特别少。这里打算分享一下自己的例子,希望不足之处大家见谅,欢迎指正切磋技术。


简单介绍一下OpenJPA,它是Apache对于Java Persistence API(即JPA)的一套实现,一个ORM思想下的优秀框架。之前一致用Hibernate,甚至还用过Apache早期对ORM的实现框架OJB,然后随着J2EE 5的规范出台,JPA也正式进入数据库映射框架的大舞台,EJB3、OpenJPA就是其倡导者。感觉今后的数据库持久化层,将很可能被Hibernate3、OpenJPA、EJB3.0瓜分。


首先我们定义实体类,使用J2EE 5推荐的Annotation方式来配置映射,相对于之前Xml方式的字段~属性映射方式来说,这种Annotation方式早期是基于JavaDoc的思想,称之为元数据。元数据的作用是创建文档、跟踪代码中的依赖性。甚至执行基本的编译时检查。最大的好处就是可以使用额外数据来分析代码,是未来编程的一个新趋势。 


工程使用Jar包:geronimo-jpa_3.0_spec.jar、jta.jar、openjpa.jar、spring.jar、spring-agent.jar、serp.jar、log4j.jar以及几个常用Apache Common工程的包。使用的数据库是Apache的文件型数据库Derby,访问方式嵌入式。首先我定义一个实体类Customer,映射数据库table是tb_customer,这里可以选择性的定义DB Schema,可以使得定义的表分属不同的table space。主键映射使用AUTO方式,uuid-hex生成器,原理可见JDK自带的java.util.UUID类。注意我使用了自定义的枚举类型来映射一些固定范围内的type值,比如性别、种类、状态相关的字段都可以使用自定义枚举类,ORDINAL表示在数据库相应字段存取的是int,或者指定SPRING表示数据相应字段存取的是枚举变量字符串,根据个人需求而定


@Entity
@Table(name="tb_customer",schema="DerbyDB")
public class Customer {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO,generator="uuid-hex")
    @Column(name="col_id",length=32)
    private String id;
    
    @Column(name="col_name",unique=true,length=32)
    private String name;
    
    @Column(name="col_address",length=128)
    private String address;
    
    @Column(name="col_email",length=64)
    private String email;
    
    @Enumerated(EnumType.ORDINAL)
    @Column(name="col_sex")
    private SexType gender;
    
    @OneToMany(fetch=FetchType.LAZY,cascade=CascadeType.ALL,mappedBy="owner")
    private Set<Good> goods = new HashSet<Good>(); 
    
    public enum SexType{MALE,FEMALE;}
    
    //All getter and setter .... ignore code
}


注意那个OneToMany,使用的是懒加载,级联操作定义为ALL,mappedBy表示在映射的另外一端由哪一个字段来维护这种OneToMany关系,owner表示在映射类的另外一端即Good类中有一个字段名字叫做owner,是Customer类型。指定mappedBy实际是想双向维护这种对应关系。至于Good类的代码我就不粘贴了,大家很容易参照着写出来,重点是Spring2.5版本的配置文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:aop="http://www.springframework.org/schema/aop"
     xmlns:tx="http://www.springframework.org/schema/tx"
     xsi:schemaLocation="
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
                
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" destroy-method="close">
        <property name="driverClassName" value="org.apache.derby.jdbc.EmbeddedDriver"/>
        <property name="url" value="jdbc:derby:DerbyDB;create=true"/>
        <property name="username" value=""/>
        <property name="password" value=""/>
    </bean>
    
    <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
    
    <bean class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor">
        <property name="transactionInterceptor" ref="transactionInterceptor"/>
    </bean>
    
    <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
        <property name="transactionManager" ref="txManager"/>
        <property name="transactionAttributeSource">
            <bean class="org.springframework.transaction.annotation.AnnotationTransactionAttributeSource"/>
        </property>
    </bean>
    
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.OpenJpaVendorAdapter">
                <property name="showSql" value="true"/>
                <property name="generateDdl" value="true"/>
                <property name="databasePlatform" value="org.apache.openjpa.jdbc.sql.DerbyDictionary"/>
            </bean>
        </property>
        
        <property name="loadTimeWeaver">
            <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
        </property>
    </bean>
    
    <tx:annotation-driven transaction-manager="txManager"/>
    
    <bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    <bean id="goodDao" class="com.openjpa.demo.dao.GoodDaoImpl">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>
    
    <bean id="customerDao" class="com.openjpa.demo.dao.CustomerDaoImpl">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>
    
    <bean id="dbService" class="com.openjpa.demo.service.DBServiceImpl">
        <property name="goodDao" ref="goodDao"/>
        <property name="customerDao" ref="customerDao"/>
    </bean>
</beans>


注意2.5版本的配置文件Root节点beans改用Xml Schema来验证格式,以往我们都习惯写上一行DTD的声明来验证。这里改成Xml Schema主要是为了引入tx标签,比如在事务管理的时候其作用的<tx:annotation-driven transaction-manager="txManager"/>,或者在插入一些声明式的AOP代码时候也会用到tx标签。


解释一下配置需要注意的细节:我采用@Transactional方式来管理事务,所以会注入PersistenceAnnotationBeanPostProcessor这个类,事务管理利用AOP拦截数据库操作代码来启动、提交、回滚事务,需要一个事务拦截器TransactionInterceptor,它里面要包含事务管理组件transactionManager和事务传播属性定义组件transactionAttributeSource。这样配置之后就可以在自定义的任何Service类或者相应的方法中启动事务管理功能(即当获得数据库连接时管理事务)。看一下我的DBServiceImpl片段就明白了:

 

@Transactional(readOnly = true)
public class DBServiceImpl implements DBService{
    private CustomerDao customerDao;
    private GoodDao goodDao;
    private TradeDao tradeDao;
    
    public void setCustomerDao(CustomerDao customerDao) {
        this.customerDao = customerDao;
    }
    
    public void setGoodDao(GoodDao goodDao) {
        this.goodDao = goodDao;
    }
    
    @Override
    @Transactional(readOnly = false, propagation=Propagation.REQUIRES_NEW)
    public void addUser(Customer user) throws Exception{
        customerDao.create(user);
    }
        @Override
    public List<Customer> getAllUser() throws Exception{
        return customerDao.findAll();
    }
    
    //....ignore other code
}


我使用的@Transactional标签并且定义了默认readOnly属性,这对于提高只读查询功能的效率是很有帮助的。只是在非只读操作上我需要把readOnly开关给关闭并且定义事务传播属性(之前这一些列的配置都是需要在Xml文件中指定),现在都可以使用元数据来完成。只不过这里一致怀疑Xml配置的好处是修改不需要重新编译,那么元数据的指定一旦要发生修改的话,必然需要重新编译替换class文件,这样做一定是最好的么?


最后需要说明的是JPA配置的核心组件类entityManager,这里注意我对entityManager注入InstrumentationLoadTimeWeaver,这是先说明JDK5.0之后引入的一个接口java.lang.instrument,允许提供Java代理检测运行在JVM上程序的服务,检测机制是对方法的字节码进行修改。而JPA允许通过LoadTimeWeaver属性注入一个JPA ClassTransformer实例到特定环境中。注入的这个代理挂钩通过修改方法字节码来协助JPA容器管理实体类和数据库的同步,检测实体类的各种状态,比如detach、managed、persistence等。典型的应用是在配置懒加载的时候对实体类的get方法做一些手脚。


还有一点是JPA规范要求配置JPA应用的时候必须在classpath能找到persistence.xml或者META-INF/persistence.xml,在里面配置JPA容器的各种属性,这里由于整合Spring而使得大部分的属性都可以移植到Spring的配置文件中。使得我的persistence.xml就是一个简单的声明而已,当然用到更多专属OpenJPA特性的时候,Spring不一定能全部整合配置,毕竟Spring支持的是J2EE 5推崇的JPA,而不是特指OpenJPA。

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                version="1.0">
    <persistence-unit name="derbyDemo"  transaction-type="RESOURCE_LOCAL">
    </persistence-unit>
</persistence>

今天大概就说道这里吧,总的体会感觉OpenJPA提供的功能比JPA规范中定义的要多的多,很强大,值得好好研究一番。



你可能感兴趣的:(Spring2.5+OpenJPA的配置)