2012-11-12 29 views
1

我在GWT框架下用Hibernate設置Spring時遇到了問題。我對GWT相當陌生。我有應用程序上下文設置和加載沒有輸出錯誤,但我目前的主要問題是,我的服務層實現(PobaseServiceImpl)需要一個DAO,我在appcontext中設置,但它不包裝DAO。當然,我的RPC試圖調用導致NullPointerException的dao方法。當我初始化時,pobaseDao沒有被TransactionProxyFactoryBean設置。Spring TransactionProxyFactoryBean不爲服務加載dao

總結: DAO應該像我其他服務一樣通過(即配置)Spring來創建。然後通過Spring注入服務。然後用DAO把它包裝在Spring事務代理(org.springframework.transaction.interceptor.TransactionProxyFactoryBean)中並給它一個Hibernate SessionFactory(org.springframework.orm.hibernate4.LocalSessionFactoryBean)。但由於某種原因,它不設置道

所以我的應用程序上下文正在通過ServletContextListener加載。 這裏是我的應用程序上下文:

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> 

    <!-- 
    Spring Framework application context definition for the POBASE Website. 
    --> 

<beans> 

    <!-- Configurer that replaces ${...} placeholders with values from a properties file --> 
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> 
    <property name="locations"> 
     <list> 
     <value>classpath:/pobase.properties</value> 
     <value>file:${user.home}/pobase.properties</value> 
     </list> 
    </property> 
    <property name="ignoreResourceNotFound" value="no"/> 
    </bean> 

    <!-- Hibernate Data Source --> 
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> 
    <property name="driverClassName" value="${pobase.database.driver}" /> 
    <property name="url" value="${pobase.database.url}" /> 
    <property name="username" value="${pobase.database.user}" /> 
    <property name="password" value="${pobase.database.password}" /> 
    </bean> 

    <!-- Hibernate SessionFactory --> 
    <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> 
    <property name="dataSource"> 
     <ref bean="dataSource" /> 
    </property> 
    <property name="packagesToScan" value="nz.co.doltech.pobase.client.entity"/> 
    <property name="hibernateProperties"> 
     <props> 
     <prop key="hibernate.dialect">${pobase.hibernate.dialect}</prop> 
     <prop key="hibernate.show_sql">${pobase.hibernate.show_sql}</prop> 
     <prop key="javax.persistence.validation.mode">none</prop> 
     </props> 
    </property> 
    </bean> 

    <!-- Transaction manager for a single Hibernate SessionFactory (alternative to JTA) --> 
    <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> 
    <property name="sessionFactory" ref="sessionFactory" /> 
    </bean> 

    <!-- Default transaction proxy, defining the transactional behaviour for 
    a typical Dao configuration --> 
    <bean id="baseDaoTransactionProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" 
    abstract="true"> 
    <property name="transactionManager" ref="transactionManager" /> 
    <property name="transactionAttributes"> 
     <value>*=PROPAGATION_MANDATORY</value> 
    </property> 
    </bean> 

    <!-- Default transaction proxy, defining the transactional behaviour for 
    a typical Service configuration --> 
    <bean id="baseServiceTransactionProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" 
    abstract="true"> 
    <property name="transactionManager" ref="transactionManager" /> 
    <property name="transactionAttributes"> 
     <value>*=PROPAGATION_REQUIRED</value> 
    </property> 
    </bean> 

    <!-- ========================= BUSINESS OBJECT DEFINITIONS ========================= --> 

    <bean id="pobaseDao" parent="baseDaoTransactionProxy"> 
    <property name="target" ref="pobaseDaoTarget" /> 
    </bean> 
    <bean id="pobaseDaoTarget" class="nz.co.doltech.pobase.server.dao.PobaseHibernateDao"> 
    <property name="sessionFactory" ref="sessionFactory" /> 
    </bean> 

    <bean id="pobaseService" parent="baseServiceTransactionProxy"> 
    <property name="target" ref="pobaseServiceTarget" /> 
    </bean> 
    <bean id="pobaseServiceTarget" class="nz.co.doltech.pobase.server.service.PobaseServiceImpl"> 
    <property name="pobaseDao" ref="pobaseDao" /> 
    <!-- property name="accessControlService" ref="accessControlService" /> 
    <property name="lookupService" ref="lookupService"/> 
    <property name="notificationService" ref="notificationService"/ --> 
    </bean> 

</beans> 

,這裏是我的RPC Servlet實現:

package nz.co.doltech.pobase.server.service; 

/** 
* Extends RSS and implements the PobaseService 
* @author Ben 
*/ 
@SuppressWarnings("serial") 
public class PobaseServiceImpl extends RemoteServiceServlet implements PobaseService { 

    @SuppressWarnings("unused") 
    private static final Logger logger = Logger.getLogger(PobaseServiceImpl.class.getName()); 

    private PobaseDao pobaseDao; 
    private final HashMap<Integer, PobaseEntity> pobaseEntities = new HashMap<Integer, PobaseEntity>(); 

    private void fetchExternPobase() 
    { 
     pobaseEntities.clear(); 
     List<PobaseEntity> pobaseList = pobaseDao.getAllPobase(); 
     for (int i = 0; i < pobaseList.size(); i++) 
     { 
      PobaseEntity en = pobaseList.get(i); 
      if(en != null) { 
       pobaseEntities.put(en.getId(), en); 
      } 
     } 
    } 

    public void setPobaseDao(PobaseDao dao) 
    { 
     this.pobaseDao = dao; 
    } 

    public PobaseDao getPobaseDao() 
    { 
     return this.pobaseDao; 
    } 

    public PobaseData addLocalPobase(PobaseData pobase) 
    { 
     PobaseEntity entity = new PobaseEntity(); 
     entity.mirrorObjectData(pobase); 

     pobase.setId(pobaseEntities.size()); 
     pobaseEntities.put(pobase.getId(), entity); 

     return entity.getDataObject(); 
    } 

    public PobaseData updateLocalPobase(PobaseData pobase) 
    { 
     PobaseEntity entity = new PobaseEntity(); 
     entity.mirrorObjectData(pobase); 

     pobaseEntities.remove(entity.getId()); 
     pobaseEntities.put(entity.getId(), entity); 

     return entity.getDataObject(); 
    } 

    public Boolean deleteLocalPobase(int id) 
    { 
     pobaseEntities.remove(id); 
     return true; 
    } 

    public ArrayList<PobaseData> deleteLocalPobases(ArrayList<Integer> ids) 
    { 
     for (int i = 0; i < ids.size(); ++i) { 
      deleteLocalPobase(ids.get(i)); 
     } 

     return getLocalPobaseData(); 
    } 

    public ArrayList<PobaseData> getLocalPobaseData() 
    { 
     ArrayList<PobaseData> pobaseList = new ArrayList<PobaseData>(); 
     Iterator<Integer> it = pobaseEntities.keySet().iterator(); 
     while(it.hasNext()) 
     { 
      PobaseData pobase = pobaseEntities.get(it.next()).getDataObject(); 
      pobaseList.add(pobase); 
     } 
     return pobaseList; 
    } 

    public PobaseData getLocalPobase(int id) 
    { 
     return pobaseEntities.get(id).getDataObject(); 
    } 

    public ArrayList<PobaseData> resyncExternPobase() 
    { 
     fetchExternPobase(); 
     return getLocalPobaseData(); 
    } 

} 

而且這裏是啓動日誌對於Web應用程序:

ServletContextListener started 
Nov 12, 2012 8:20:33 PM nz.co.doltech.pobase.SpringInitialiser initSpringContext 
INFO: Creating new Spring context. Configs are [/nz/co/doltech/pobase/appcontext.xml] 
Nov 12, 2012 8:20:33 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh 
INFO: Refreshing org[email protected]8423321: startup date [Mon Nov 12 20:20:33 NZDT 2012]; root of context hierarchy 
Nov 12, 2012 8:20:33 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 
INFO: Loading XML bean definitions from class path resource [nz/co/doltech/pobase/appcontext.xml] 
Nov 12, 2012 8:20:33 PM org.springframework.core.io.support.PropertiesLoaderSupport loadProperties 
INFO: Loading properties file from class path resource [pobase.properties] 
Nov 12, 2012 8:20:33 PM org.springframework.core.io.support.PropertiesLoaderSupport loadProperties 
INFO: Loading properties file from URL [file:/home/ben/pobase.properties] 
Nov 12, 2012 8:20:33 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons 
INFO: Pre-instantiating singletons in org.s[email protected]4c56666d: defining beans [org.springframework.beans.factory.config.PropertyPlaceholderConfigurer#0,dataSource,sessionFactory,transactionManager,baseDaoTransactionProxy,baseServiceTransactionProxy,pobaseDao,pobaseDaoTarget,pobaseService,pobaseServiceTarget]; root of factory hierarchy 
Nov 12, 2012 8:20:33 PM org.springframework.jdbc.datasource.DriverManagerDataSource setDriverClassName 
INFO: Loaded JDBC driver: org.postgresql.Driver 
Nov 12, 2012 8:20:33 PM org.hibernate.annotations.common.Version <clinit> 
INFO: HCANN000001: Hibernate Commons Annotations {4.0.1.Final} 
Nov 12, 2012 8:20:33 PM org.hibernate.Version logVersion 
INFO: HHH000412: Hibernate Core {4.1.7.Final} 
Nov 12, 2012 8:20:33 PM org.hibernate.cfg.Environment <clinit> 
INFO: HHH000206: hibernate.properties not found 
Nov 12, 2012 8:20:33 PM org.hibernate.cfg.Environment buildBytecodeProvider 
INFO: HHH000021: Bytecode provider name : javassist 
Nov 12, 2012 8:20:34 PM org.hibernate.dialect.Dialect <init> 
INFO: HHH000400: Using dialect: org.hibernate.dialect.PostgreSQLDialect 
Nov 12, 2012 8:20:34 PM org.hibernate.engine.jdbc.internal.LobCreatorBuilder useContextualLobCreation 
INFO: HHH000424: Disabling contextual LOB creation as createClob() method threw error : java.lang.reflect.InvocationTargetException 
Nov 12, 2012 8:20:34 PM org.hibernate.engine.transaction.internal.TransactionFactoryInitiator initiateService 
INFO: HHH000399: Using default transaction strategy (direct JDBC transactions) 
Nov 12, 2012 8:20:34 PM org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory <init> 
INFO: HHH000397: Using ASTQueryTranslatorFactory 
Nov 12, 2012 8:20:34 PM org.springframework.orm.hibernate4.HibernateTransactionManager afterPropertiesSet 
INFO: Using DataSource [[email protected]c0d4] of Hibernate SessionFactory for HibernateTransactionManager 
Starting Jetty on port 8888 

任何人有任何想法,爲什麼它不工作?這裏的一些規格:

  • 春3.2.0
  • Hibernate4
  • GWT SDK 2.4.0

感謝所有幫助我能!

乾杯, 本

+0

請添加堆棧跟蹤,另外您確定要加載數據並將其保存在您的服務中嗎? –

+0

是否有更傳統的方式來存儲數據?我對GWT相當陌生,所以不能100%確定最佳實踐。 (將堆棧跟蹤添加到主帖子)null異常是由於在設置之前調用了pobaseDao。但據我瞭解,在appcontext.xml中配置的事務代理和服務bean應該照顧這個? –

+0

那麼,這取決於你的方法,但是當你想要執行像緩存一樣的東西時,將數據存儲在服務實現中是合理的,否則這不是一種好的方法。 –

回答

1

PobaseServiceImpl類不是線程安全的。 在Java中,任何遠程調用服務(當然也是容器中的單例也是這樣)應該準備好,它的方法將從不同的線程中調用,並且可以在不同的線程中同時執行相同的方法。

整個班級需要詳細檢查。

首先,您應該使用ConcurrentHashMap而不是HashMap。並且所有方法都需要仔細重寫 - 例如,返回ArrayList (public ArrayList<PobaseData> resyncExternPobase()) 的方法應該製作數據的防禦副本。

正如你應該考慮讓公衆方法您的服務方法同步,但是替代方案,這將迫使該方法依序執行,如果該服務有望從不同的線程主動使用,這將削弱性能。

供參考,請考慮閱讀Brian Goetz Java Concurrency In Practice,第5章。構建基塊特別是5.1同步集合5.2。併發集合

更新:您PobaseServiceImpl似乎是由servlet容器實例化一個servlet - 來填充它與Spring bean的領域你是你應該使用Spring實用方法 - 例如WebApplicationContextUtils - 有在互聯網上有很多例子,比如http://pgt.de/2009/07/17/non-invasive-gwt-and-spring-integration-reloaded/

+0

謝謝你的回覆,我一定會研究你提到的。我對這種類型的開發相當陌生,因此學習遠程服務等方面的更好實踐非常好。基本上這是一個測試應用程序,讓我學習更多關於GWT和大型Web應用程序開發。無論如何,是你提到導致我的DAO包裝問題? –

+0

@BenDol首先用'ConcurrentHashMap'替換'HashMap',如果有變化就添加註釋。 –

+0

行爲沒有變化。我沒有真正清楚地說明我的問題在主要職位上。基本上,當初始化appcontext時,Spring DAO似乎沒有被包裝。據我瞭解,在我的appcontext.xml中,我定義了一個id爲pobaseService的bean,它指向具有類PobaseServiceImpl和屬性的目標bean(pobaseServiceTarget),它應該包裝DAO進入服務。但我明顯錯過了一些東西,不太清楚是什麼。 –