2012-06-15 55 views
2

我正在爲網站編寫功能區/成就係統,我必須爲系統中的每個功能區編寫一些邏輯。例如,如果您在前2000人註冊到網站或在論壇中發佈了1000個帖子後,就可以獲得功能區。這個想法非常類似於stackoverflow的徽章。將DAO自動裝入域對象

因此,每個功能區顯然都在數據庫中,但他們也需要一些邏輯來確定用戶何時贏得功能區。

在我編寫的方式,Ribbon是一個簡單的抽象類:

@Entity 
@Table(name = "ribbon") 
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) 
@DiscriminatorColumn(name = "ribbon_type") 
public abstract class Ribbon 
{ 
    @Id 
    @Column(name = "id", nullable = false, length = 8) 
    private int id; 

    @Column(name = "title", nullable = false, length = 64) 
    private String title; 

    public Ribbon() 
    { 
    } 

    public abstract boolean isEarned(User user); 

    // ... getters/setters... 
} 

你可以看到我定義繼承策略爲SINGLE_TABLE(因爲我有如下代碼50個絲帶,我不需要其中任何一個的附加列)。

現在,一個特定的帶將被這樣實現,例如:

@Entity 
public class First2000UsersRibbon extends Ribbon 
{ 
    @Autowired 
    @Transient 
    private UserHasRibbonDao userHasRibbonDao; 

    public First2000UsersRibbon() 
    { 
     super.setId(1); 
     super.setTitle("Between the first 2,000 users who registered to the website"); 
    } 

    @Override 
    public boolean isEarned(User user) 
    { 
     if(!userHasRibbonDao.userHasRibbon(user, this)) 
     { 
      // TODO 
      // All the logic to determine whether the user earned the ribbon 
      // i.e. check whether the user is between the first 2000 users who registered to the website 
      // Other autowired DAOs are needed 
     } 
     else 
     { 
      return true; 
     } 

     return false; 
    } 
} 

的問題是userHasRibbonDaoisEarned()方法內空,所以NullPointerException被拋出。

我認爲DAO自動裝入域對象是錯誤的,但在this topic他們告訴我,這是正確的方法(域驅動設計)。

我共享的非工作GitHub上很簡單的例子:https://github.com/MintTwist/TestApp(記得要改變連接細節/WEB-INF/properties/jdbc.properties並導入test_app.sql腳本)

任何幫助非常感激。

謝謝!

更新 - 閱讀第一個答案,似乎我的方法是完全錯誤的。考慮到可能存在50-70個不同的色帶,您如何理想地構建代碼?由於

+2

> _

+1

這裏應該有一些github網址嗎? – madth3

+0

謝謝@NathanHughes,我剛剛發佈了他們在另一個問題上告訴我的內容。 – satoshi

回答

6

我不是說我與注射的DAO到域情況下同意....但

您可以連接你的DAO到你的域對象。但是您必須在Spring應用程序上下文中聲明域對象,並使用new從Spring NOT獲取新實例。確保你使用原型範圍!你不想每次都得到相同的單例實例!

真的這個你想實現的邏輯屬於一個注入了它需要的DAO的服務。

也許你可以有一個像服務:

@Service 
public class RibbonServiceImpl implements RibbonService 

    @Autowired 
    private RibbonDAO ribbonDAO; 

    public boolean isEarned(Ribbon ribbon, User user) { 
    if(!userHasRibbonDao.userHasRibbon(user, this)) 
     { 
      // TODO 
      // All the logic to determine whether the user earned the ribbon 
      // i.e. check whether the user is between the first 2000 users who registered to the website 
      // Other autowired DAOs are needed 
     } 
     else 
     { 
      return true; 
     } 

     return false; 
    } 
+0

謝謝你,@亞歷克斯。所以顯然他們在另一個SO問題中告訴我的是錯誤的......但是,如果總共可以有50-70條帶,那麼您將如何構造服務和代碼?謝謝! – satoshi

+0

感謝您的編輯,@Alex。問題是每個功能區都有不同的邏輯,所以我能看到你的代碼工作的唯一方法是通過放置一些'instanceof'來檢測我應該使用哪個功能區邏輯。我知道必須使用'instanceof'(在equals()'之類的方法之外)是由不好的軟件設計引起的,所以最好的解決方案是什麼?謝謝。 – satoshi

+0

嗯好吧,所以也許你可以製作你的RibbonService摘要並創建一個抽象的isEarned方法。然後,您可以使用http://static.springsource.org/spring/docs/3.1.0.RC1/spring-framework-reference/html/beans.html#beans-factory根據RibbonType查找服務的正確實例-lookup-method-injection –

4

將其標記爲@Configurable - @Configurable annotion將確保即使豆春季之外創建,依賴注入

您還需要在您的上下文中添加<context:spring-configured/>

+3

+1但注意:這需要AspectJ編譯或加載時間編織! –

+0

+1 @satoshi:看看Spring Roo,看看它如何在實踐中發揮作用 – Ralph

0

您還可以嘗試使用@Component註釋以及First2000UsersRibbon類中的@Entity註釋。並確保該課程包含<context:component-scan base-package="" />。此外,您需要確保該類的對象不是使用new運算符創建的。

希望這可以幫助你。乾杯。

0

正如Alex已經提到的,在您的上下文中將應用程序實體作爲bean並不是一個好習慣。有許多繁瑣的事情可能發生,而且看起來不是一個好的設計。

該代碼將是這個樣子:

public abstract class Ribbon{ 

    public abstract boolean checkUser(User user); 
} 

public class NewUserRibbon extends Ribbon{ 

    @Override 
    public boolean checkUser(User user){ 
     // your logic here 
    } 
} 

在你的服務,你可以擁有所有的色帶的高速緩存集合中的系統(除非它們是動態的),我會建議甚至到了絲帶分類通過事件觸發器(新用戶,答案,投票等),因此您可以通過向當前用戶遍歷適用的功能區列表來檢查您的服務是否適用於相應的功能區(而不是所有功能區)。

0

從我所看到的您的hibernate類的設計以及獲得的色帶的持久性來看,沒有問題。我認爲問題在於何時以及如何決定用戶是否獲得了新的功能區。

可以說一個新的請求來自登錄的用戶。 User對象由Hibernate創建和填充,我們現在知道該用戶在userHasRibbonSet中已獲得的所有Ribbon。我們可能需要在用戶這樣的方法:

public boolean hasEarnedRibbon(Ribbon ribbon) { 
    for (UserHasRibbon userHasRibbon : userHasRibbonSet) { 
     if (userHasRibbon.getRibbon().equals(ribbon) { 
      return true; 
     } 
    } 
    return false; 
} 

(這或許可以通過緩存帶自己一套,做一個恆定的時間查找進行優化,但這裏沒有密鑰)

處理請求,更新用戶對象以反映發生了什麼。然後,對出路,你檢查什麼色帶的用戶現在已經賺了,是這樣的:

public class RibbonAwardingInterceptor extends HandlerInterceptorAdapter { 

    @Resource 
    private SessionFactory sessionFactory; 
    @Resource // assuming it's a request-scoped bean; you can inject it one way or another 
    private User user; 

    public void postHandle(HttpServletRequest request, HttpServletResponse response, 
     Object handler, ModelAndView modelAndView) throws Exception { 

     List<Ribbon> allRibbons = sessionFactory.getCurrentSession().createQuery("from Ribbon").list(); 

     for (Ribbon ribbon : allRibbons() { 
      if (!user.hasEarnedRibbon(ribbon)) { 
       // The user has not previously earned this ribbon - lets see if they have now 
       if (ribbon.isEarned(user)) { 
        user.getUserHasRibbonSet().add(new UserHasRibbon(user, ribbon)); 
       } 
      } 
     } 
    } 
} 

如果你想使用這個精確的模式,確保在更新用戶的任何攔截後,該攔截器去一種與ribbons相關的方式,但是在關閉事務的攔截器(假設您使用每個請求事務模型)之前。然後刷新休眠會話將自動更新UserHasRibbon表,因此不需要專門的DAO。

這是一個簡單的方法,顯然可以進行細化。一個明顯的改進就是對你所檢查的色帶更具選擇性。也許每個控制器方法都可以通過檢查是否有相關的色帶現在適用來完成 - 控制器應該知道哪些色帶可以在其動作後被授予。

希望有幫助,請讓我知道如果我完全錯過了這一點,我會再試一次。

0

我認爲你需要調整你的設計。我遇到的第一個問題是「你的功能區班級如何能夠檢查哪個用戶擁有它?」這就像說廚房的桌子應該有一個叫boolean doesThisKitchenHaveMe(Kitchen k)的方法。

這似乎更符合邏輯,我認爲你需要一個帶狀映射到用戶

1

答案之一是缺少一個第三定位服務,它不漂亮,但它的作品。相反接線道的,你可以看看它從WebApplicationContext中了:

RibbonDao dao = ContextLoader.getCurrentWebApplicationContext.getBean(RibbonDao.class); 

這是一切的對面依賴注入代表(我喜歡把這種模式「控制反轉反轉」 :-)) ,但是:那麼將服務注入到域對象中也是如此。

0

爲什麼在DomainObject中使用DAO?我建議去耦合DAO和DomainObject,因爲(恕我直言)方法isEarned(用戶用戶)與First2000UsersRibbon無關。

class UserHasRibbonDao { 
    public boolean isEarned(User user){ 
     if(!userHasRibbonDao.userHasRibbon(user, this)) { 
     // TODO 
     // All the logic to determine whether the user earned the ribbon 
     // i.e. check whether the user is between the first 2000 users who registered to the website 
     // Other autowired DAOs are needed 
     } else { 
      return true; 
     } 

     return false;} 
}