Spring2.5+OpenJPA的配置

来源:互联网 发布:淘宝 强光手电筒 编辑:程序博客网 时间:2024/05/20 04:51

最近总想写点什么,正好研究了一下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规范中定义的要多的多,很强大,值得好好研究一番。



原创粉丝点击