本文同时发表在我在google Appengine 上的搭建的博客: http://blogfor11lu.appspot.com/articleaction_view.action?article.id=agtibG9nZm9yMTFsdXIPCxIHQXJ0aWNsZRjBtQMM
之前用JDO 和 Struts2 在google Appengine 上试着写了一个简单的blog程序,但我还是希望使用Spring的依赖注入和事务管理等方面的功能,于是着手搭建环境。
这里,我使用了GAE1.3.5 Spring2.5.6 Struts2.1.8
首先是测试了一下JPA,在Eclipse开发环境下,GAE已经把相应的包都放到了Lib目录下了,只要在src/META-INF下建立persistence.xml文件就可以了,具体内容从GAE文档中复制过来就OK了。写一个简单的的类,测试一下,这个比较简单,GAE文档中都有。很快就成功了。
接下来,我把spring和struts的相关jar包拷到lib目录下,并配置好相关xml,总会出现或这或那的错误,这都是由于jar包不完整或者是jar包之间的版本不配比造成的,还再次出现了上一次集成struts和gae时提示xalan这个jar包没有的错误。后来发现是struts和spring-struts-plugin的版本不一致造成的(这个错误的出现有两种,一个是ongl没处理,一个就是版本不配比)。
经过一大堆错误,最后还是决定一个一个来集成。先集成struts,把前次的jar包拷贝过来,简单做一个action,测试一下,OK。
复制spring的jar包,根据网上的提示,不用all in on的那个,主要是因为GAE在file.io方面不能写文件,使用modules下面的jar包,凭感觉挑了一些,然后,再复制lib目录下需要使用的一些jar包,这个基本参考网上和以前SSH整合时用的jar包。不断测试,根据提示把jar包补齐。一般的,提示没找到指定的类错误,是没有相应jar包,提示没有指定的方法,是jar包的版本不一致。最后要把spring-struts-plugin2.1.8.jar复制到lib目录下。
集成测试了,在集成测试时,测试了@Controller、@Resource、@Transaction等几个注解,首先测的是@Controller,这个测试比较顺利。接着用@Resource 注入一个DAO操作的类。问题出来了:错误信息如下:
Error creating bean with name 'personAction': Injection of resource fields failed; nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'personService' must be of type [com_11lu.GAE.PersonService], but was actually of type [$Proxy17]
最开始,我并没有PersonService,而是直接使用DaoSupport,这是一个抽象类实现Dao接口,DaoSupport是从网上拷来的,当我注意到是抽象类时,把它的abstract去掉,错误依旧,再次,把PersonService继承DaoSupport,还是一样的错误。从这个错误信息我们可以看出,大致的意思的bean的type不对。于是在google上找,说到这个找,关键词很重要,最先是找Injection of resource fields failed这个,没有得到合适的内容。后来找must be of type but was actually of type,在一个外国论坛上看到了答案。问题出在@Transaction注解,在spring的文档中,实际上有说明,对@Transaction的类,要设置proxy-target-class="true" ,否则就不能将这个类注入。也就是会出现上面的信息。具体设置是:<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>。
由于我的英文实在是差,没搞明白proxy-target-class="true"设置在哪,也搞不懂注解方式这个如何设,最终是试着放在这的。我想,如果不是用注解方式,可以放在<bean/>里。
再次运行,提试CGLIB要复制到CLASSPAH下,在spring的Lib目录中找到,复制好就OK了。
下面我把用到的Jar包列出,另外我把我测试用的project也上传到 http://download.csdn.net/source/2613955,大家可以下载看看。
我写的记录了我的整个过程,这个过程,我用了好几个晚上的功夫,文字有点哆嗦。
解释一下为什么@Transaction的类,会出现上述错误,在spring中@Transaction是通过AOP实现的,而spring对AOP有两种实现方式,一种是动态代理,它是通过接口方式实现的,要求所代理的类一定是实现了某一个接口,对一般的类就无法代理,spring默认是这种;通过设置proxy-target-class="true",则是使用CGLIB实现AOP,CGLIB直接生成二进制码,使得普通类也可以实现AOP。在没有设置proxy-target-class="true"时,使用动态代理,是一个临时生成的类,如proxy17,它不是@Resource指定的类,因此出现了上述错误。
<?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:context="http://www.springframework.org/schema/context" 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/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <context:component-scan base-package="com_11lu.GAE" /> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean" lazy-init="true"> <property name="persistenceUnitName" value="transactions-optional" /> </bean> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" /> <!-- Activates @Transactional for DefaultImageDatabase --> <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/> </beans>
package com_11lu.GAE; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.reflect.Method; import java.util.LinkedHashMap; import javax.persistence.EmbeddedId; import javax.persistence.Entity; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.Query; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Transactional public abstract class DaoSupport<T> implements DAO{ @PersistenceContext protected EntityManager em; public void clear(){ em.clear(); } public <T> void delete(Class<T> entityClass,Object entityid) { delete(entityClass, new Object[]{entityid}); } public <T> void delete(Class<T> entityClass,Object[] entityids) { for(Object id : entityids){ em.remove(em.getReference(entityClass, id)); } } @Transactional(readOnly=true,propagation=Propagation.NOT_SUPPORTED) public <T> T find(Class<T> entityClass, Object entityId) { return em.find(entityClass, entityId); } public void save(Object entity) { em.persist(entity); } @Transactional(readOnly=true,propagation=Propagation.NOT_SUPPORTED) public <T> long getCount(Class<T> entityClass) { return (Long)em.createQuery("select count("+ getCountField(entityClass) +") from "+ getEntityName(entityClass)+ " o").getSingleResult(); } public void update(Object entity) { em.merge(entity); } @Transactional(readOnly=true,propagation=Propagation.NOT_SUPPORTED) public <T> QueryResult<T> getScrollData(Class<T> entityClass, int firstindex, int maxresult, LinkedHashMap<String, String> orderby) { return getScrollData(entityClass,firstindex,maxresult,null,null,orderby); } @Transactional(readOnly=true,propagation=Propagation.NOT_SUPPORTED) public <T> QueryResult<T> getScrollData(Class<T> entityClass, int firstindex, int maxresult, String wherejpql, Object[] queryParams) { return getScrollData(entityClass,firstindex,maxresult,wherejpql,queryParams,null); } @Transactional(readOnly=true,propagation=Propagation.NOT_SUPPORTED) public <T> QueryResult<T> getScrollData(Class<T> entityClass, int firstindex, int maxresult) { return getScrollData(entityClass,firstindex,maxresult,null,null,null); } @Transactional(readOnly=true,propagation=Propagation.NOT_SUPPORTED) public <T> QueryResult<T> getScrollData(Class<T> entityClass) { return getScrollData(entityClass, -1, -1); } @SuppressWarnings("unchecked") @Transactional(readOnly=true,propagation=Propagation.NOT_SUPPORTED) public <T> QueryResult<T> getScrollData(Class<T> entityClass, int firstindex, int maxresult , String wherejpql, Object[] queryParams,LinkedHashMap<String, String> orderby) { QueryResult qr = new QueryResult<T>(); String entityname = getEntityName(entityClass); Query query = em.createQuery("select o from "+ entityname+ " o "+(wherejpql==null? "": "where "+ wherejpql)+ buildOrderby(orderby)); setQueryParams(query, queryParams); if(firstindex!=-1 && maxresult!=-1) query.setFirstResult(firstindex).setMaxResults(maxresult); qr.setResultlist(query.getResultList()); query = em.createQuery("select count("+ getCountField(entityClass)+ ") from "+ entityname+ " o "+(wherejpql==null? "": "where "+ wherejpql)); setQueryParams(query, queryParams); qr.setTotalrecord((Long)query.getSingleResult()); return qr; } protected void setQueryParams(Query query, Object[] queryParams){ if(queryParams!=null && queryParams.length>0){ for(int i=0; i<queryParams.length; i++){ query.setParameter(i+1, queryParams[i]); } } } /** * 组装order by语句 * @param orderby * @return */ protected String buildOrderby(LinkedHashMap<String, String> orderby){ StringBuffer orderbyql = new StringBuffer(""); if(orderby!=null && orderby.size()>0){ orderbyql.append(" order by "); for(String key : orderby.keySet()){ orderbyql.append("o.").append(key).append(" ").append(orderby.get(key)).append(","); } orderbyql.deleteCharAt(orderbyql.length()-1); } return orderbyql.toString(); } /** * 获取实体的名称 * @param <T> * @param entityClass 实体类 * @return */ protected <T> String getEntityName(Class<T> entityClass){ String entityname = entityClass.getSimpleName(); Entity entity = entityClass.getAnnotation(Entity.class); if(entity.name()!=null && !"".equals(entity.name())){ entityname = entity.name(); } return entityname; } protected <T> String getCountField(Class<T> clazz){ String out = "o"; try { PropertyDescriptor[] propertyDescriptors = Introspector.getBeanInfo(clazz).getPropertyDescriptors(); for(PropertyDescriptor propertydesc : propertyDescriptors){ Method method = propertydesc.getReadMethod(); if(method!=null && method.isAnnotationPresent(EmbeddedId.class)){ PropertyDescriptor[] ps = Introspector.getBeanInfo(propertydesc.getPropertyType()).getPropertyDescriptors(); out = "o."+ propertydesc.getName()+ "." + (!ps[1].getName().equals("class")? ps[1].getName(): ps[0].getName()); break; } } } catch (Exception e) { e.printStackTrace(); } return out; } }
问题:
1.在一个service连对一个实例两次保存
SystemUser user = (SystemUser) getSystemUser(); initSystemDao.save(user); initSystemDao.save(user);
引起:TransactionOptions.Builder.withXGfound both Element
解决:在persistence.xml增加
<property name="datanucleus.appengine.datastoreEnableXGTransactions" value="true"/>
antlr-2.7.6.jar
aopalliance.jar
appengine-api-1.0-sdk-1.3.5.jar
appengine-api-labs-1.3.5.jar
appengine-jsr107cache-1.3.5.jar
cglib-nodep-2.1_3.jar
commons-beanutils.jar
commons-collections.jar
commons-fileupload-1.2.1.jar
commons-io-1.3.2.jar
commons-lang.jar
commons-logging-1.0.4.jar
commons-logging.jar
datanucleus-appengine-1.0.7.final.jar
datanucleus-core-1.1.5.jar
datanucleus-jpa-1.1.5.jar
freemarker-2.3.13.jar
geronimo-jpa_3.0_spec-1.1.1.jar
geronimo-jta_1.1_spec-1.1.1.jar
jdo2-api-2.3-eb.jar
jsr107cache-1.1.jar
ognl-2.6.11.jar
slf4j-api-1.5.8.jar
slf4j-simple-1.5.8.jar
spring-aop.jar
spring-beans.jar
spring-context.jar
spring-core.jar
spring-jdbc.jar
spring-orm.jar
spring-tx.jar
spring-web.jar
struts2-core-2.1.8.1.jar
struts2-gae-0.1.jar
struts2-spring-plugin-2.1.8.1.jar
xwork-core-2.1.6.jar