2014-08-29 58 views
0

拳頭它不是錯誤的對象,但它不是正確的參考,因爲我診斷出這個問題。GWT RequestFactory調用設置錯誤的對象

我有一個編輯器,我正在編輯一個包含更多bean的另一個列表編輯器的對象。

我有一個TagCollectionProxy,它包含一個TagProxy對象列表。當我使用附加的List對象保存TagCollectionProxy時,就會出現問題。我的實體定位器正在查找數據庫中的對象並設置該值,但是我沒有對該對象的引用。這是新的,我不知道爲什麼現在發生這種情況。

當我調用保存在對象上的調試消息。我在setValue的Tag類中放了一條消息,你可以看到我在域實體上設置了正確的值。 id匹配。問題出現在我的dao類中保存(TagCollection)的下一條消息中。我正在遍歷實體並打印該值,但該實體的值未變。

2014-08-29T15:34:15.912-0500|INFO: Setting value Tag2asdfasdfasdfasdf for id 294 Reference [email protected] 
2014-08-29T15:34:15.944-0500|INFO: Tag name => asdfasdf Tag value => Tag2 id => 294 ref => [email protected] 
2014-08-29T15:34:15.944-0500|INFO: Id of tag is 294 

在此先感謝您的幫助,我在一分鐘內放鬆一下頭髮。

這是我的活動,保存對象。請注意,我正在保存包含標籤列表的TagCollectionProxy。

public class TagCollectionEditActivity extends AbstractActivity<TagCollectionEditView> implements 
     TagCollectionEditView.Presenter { 



    private Receiver<Long> saveReceiver = new Receiver<Long>() { 
     @Override 
     public void onSuccess(Long response) { 
      logger.info("saved tagCollection with id of " + response); 
      clientFactory.getPlaceController().goTo(new TagCollectionsPlace()); 
     } 

     public void onConstraintViolation(Set<ConstraintViolation<?>> violations) { 

      for (ConstraintViolation<?> violation : violations) { 
       logger.warning(violation.getMessage()); 
      } 
      getDisplay().getEditorDriver().setConstraintViolations(violations); 
     } 

     public void onFailure(ServerFailure failure) { 
      logger.log(Level.SEVERE, "Failed to save tag collection " + failure.getMessage()); 
      getDisplay().showError(failure); 
     } 
    }; 

    private static final Logger logger = Logger.getLogger(TagCollectionEditActivity.class.getName()); 

    private IClientFactory clientFactory; 

    private TagCollectionDaoRequest editContext = null; 

    public TagCollectionEditActivity(IClientFactory clientFactory) { 
     super(new TagCollectionEditView(clientFactory.getResources(), clientFactory.getEventBus())); 
     this.clientFactory = clientFactory; 
    } 

    @Override 
    protected void bindToView() { 
     getDisplay().setPresenter(this); 
     final TagCollectionEditPlace place = (TagCollectionEditPlace) getPlace(); 
     Long tagCollectionId = place.getTagCollectionId(); 

     if (tagCollectionId == null) { 
      createTagCollection(); 
     } else { 
      findCollection(tagCollectionId); 
     } 
    } 

    private void findCollection(Long tagCollectionId) { 
     clientFactory.tagCollectionRequest().find(tagCollectionId).with(getEntityProperties()) 
       .fire(new Receiver<TagCollectionProxy>() { 

        @Override 
        public void onSuccess(TagCollectionProxy tagCollection) { 
         editContext = clientFactory.tagCollectionRequest(); 
         editContext.save(tagCollection).with(getDisplay().getEditorDriver().getPaths()).to(saveReceiver); 
         getDisplay().getEditorDriver().edit(tagCollection, editContext); 
         GWT.log("Context is " + editContext.hashCode()); 
        } 
       }); 
    } 

    private void createTagCollection() { 
     editContext = clientFactory.tagCollectionRequest(); 
     TagCollectionProxy tagCollection = editContext.create(TagCollectionProxy.class); 
     editContext.save(tagCollection).with(getDisplay().getEditorDriver().getPaths()).to(saveReceiver); 
     tagCollection.setTags(new ArrayList<TagProxy>()); 
     getDisplay().getEditorDriver().edit(tagCollection, editContext); 
    } 

    @Override 
    public void onSave() { 
     RequestContext context = getDisplay().getEditorDriver().flush(); 
     context.fire(); 
    } 

    public String[] getEntityProperties() { 
     return new String[] { "tags", "deviceFamily" }; 
    } 

    @Override 
    public void onCancel() { 
     clientFactory.getPlaceController().goTo(new TagCollectionsPlace()); 
    } 
} 

這是我的標籤集合代理,我定義我DomainEntityLocator

@ProxyFor(value = TagCollection.class, locator = DomainEntityLocator.class) 
public interface TagCollectionProxy extends DomainEntityProxy { 

    public List<TagProxy> getTags(); 

    public void setTags(List<TagProxy> tags); 

    public DeviceFamilyProxy getDeviceFamily(); 

    public void setDeviceFamily(DeviceFamilyProxy deviceFamily); 

} 

這裏是我的定位是使用數據庫來查找使用JPA的EntityManager對象。

public class DomainEntityLocator extends Locator<DomainEntity, Long> { 

    private static EntityManager em = null; 

    static { 
     Context initCtx; 
     try { 
      initCtx = new InitialContext(); 
      // perform JNDI lookup to obtain container-managed entity manager 
      em = (javax.persistence.EntityManager) initCtx.lookup("java:comp/env/persistence/DomainEntityManager"); 

     } catch (NamingException e) { 
      throw new RuntimeException("Unable to get the domain entity manager"); 
     } 
    } 

    public DomainEntityLocator() { 
    } 

    @Override 
    public DomainEntity create(Class<? extends DomainEntity> clazz) { 
     try { 
      return clazz.newInstance(); 
     } catch (InstantiationException e) { 
      throw new RuntimeException(e); 
     } catch (IllegalAccessException e) { 
      throw new RuntimeException(e); 
     } 
    } 

    @Override 
    public DomainEntity find(Class<? extends DomainEntity> clazz, Long id) { 
     return em.find(clazz, id); 
    } 

    @Override 
    public Class<DomainEntity> getDomainType() { 
     return DomainEntity.class; 
    } 

    @Override 
    public Long getId(DomainEntity domainObject) { 
     return domainObject.getId(); 
    } 

    @Override 
    public Class<Long> getIdType() { 
     return Long.class; 
    } 

    @Override 
    public Object getVersion(DomainEntity domainObject) { 
     return 0; 
    } 
} 

更新

經過一番調查後,我意識到,JPA在服務器端是責怪我的問題。我得出的結論是,DomainEntityLocator正在獲取實體,但並未將其包裝在事務中。因此,DomainEntityLocator#find()方法將檢索實體,並且ServiceLayer將修改實體上的值。我遇到的問題是find方法沒有包含在事務中,並且由於我檢索的實體管理器沒有管理,實體管理器沒有清除更改。因此我沒有看到價值的更新。

在題目問題2

有沒有添加事務管理的實體定位器,這樣要求在工廠中調用setter方法後,會持續對象的常見圖案。我曾經用一個簡單的servlet過濾器來做到這一點,但是我正在處理的項目需要java ee來維護這種功能。我無法使DomainEntityLocator成爲無狀態bean,因爲ServiceLayer會查找它。最好是擴展RequestFactoryServlet並將doPost包裝在tranasction中?這似乎是最合乎邏輯的想法。有什麼建議麼?

再次感謝托馬斯

回答

2

在服務器端,RequestFactory將通過其ID加載每個對象,使用你的定位,所以TagCollection將被加載(其的Tag列表)和各Tag將使用裝過DomainEntityLocator(假設這是他們在@ProxyFor中聲明的方式)。
設置者將被定位器返回的對象調用,而不是來自TagCollectiongetTags列表中的對象。

您必須確保它們是相同的實例。

使用JPA,這意味着使用「打開會話在視圖中」(又名「每個請求的會話」)模式。 JPA會話使用緩存,因此在整個HTTP請求中共享同一個會話將確保每次從TagCollection或直接通過ID加載時使用相同的Tag實例。

+0

托馬斯,謝謝你的幫助。經過一些調試,我得出了和你一樣的結論。我最初的困惑源自Chrome,並未在(調試工具)預覽面板中打印整個發佈消息。我不得不深入Header面板才能看到整個傳出的消息。我用一個小小的更新更新了這個問題。如果你知道一個很好的解決方案來包裝定位器,如果這是一個好主意,請告訴我。 – 2014-09-02 14:30:42

+0

在交易中包裝整個請求/響應會引發麻煩:如果您這樣做,RF語義將會改變。你需要的是「open session in view」模式(每個請求/響應一個'EntityManager'),然後將你的事務範圍擴展到你的服務方法。 – 2014-09-03 08:51:54

0

以下是我爲解決EJB和RequestFactory問題所做的工作。

在確定RequestFactory方法未包含在實體管理器的事務中後,我想到了兩個選擇。一個是我可以將UserTransaction添加到Entity定位器的find和create方法,但我決定將整個請求包裝在一個事務中,這對我的情況非常有效,因爲我的請求工廠方法幾乎總是處於事務中。

我擴展了requestfactory servlet並添加了一個UserTransaction到doPost。

@WebServlet("/rf/secure/dao") 
public class DmsRequestFactoryServlet extends RequestFactoryServlet { 

    private static final long serialVersionUID = -521670028586842819L; 

    private static final Logger logger = Logger.getLogger(DmsRequestFactoryServlet.class.getName()); 

    @Resource 
    private UserTransaction tx; 


    public static class SimpleExceptionHandler implements ExceptionHandler { 

     @Override 
     public ServerFailure createServerFailure(Throwable throwable) { 
      logger.log(Level.SEVERE, "Unable to complete request", throwable); 

      return new ServerFailure("Server Error: " + (throwable == null ? null : throwable.getMessage()), throwable 
        .getClass().getName(), ExceptionUtils.getStackTrace(throwable), true); 
     } 
    } 

    @Override 
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, 
      ServletException { 
     try { 
      tx.begin(); 
      super.doPost(request, response); 
      tx.commit(); 
     } catch (Exception e) { 
      logger.log(Level.SEVERE, "Failed request ", e); 
     } 
    } 

    public DmsRequestFactoryServlet() { 
     super(new SimpleExceptionHandler()); 
    } 
}