4

Webサイトのリボン/実績システムをコーディングしていますが、システム内のリボンごとにロジックを作成する必要があります。たとえば、最初の2,000人がWebサイトに登録している場合、または1,000人がフォーラムに投稿した後は、リボンを獲得できます。アイデアは、実際には、stackoverflowのバッジと非常によく似ています。

したがって、すべてのリボンは明らかにデータベースにありますが、ユーザーがいつリボンを獲得したかを判断するためのロジックも必要です。

私がコーディングした方法で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;
    }
}

問題は、メソッドuserHasRibbonDao内でnullであるため、aがスローされることです。isEarned()NullPointerException

DAOをドメインオブジェクトに自動配線するのは間違っていると思いましたが、このトピックでは、DAOが正しいアプローチ(ドメイン駆動設計)であると教えてくれました。

GitHubで機能しない非常に単純な例を共有しました:https ://github.com/MintTwist/TestApp (/WEB-INF/properties/jdbc.propertiesの接続の詳細を変更し、test_app.sqlスクリプトをインポートすることを忘れないでください)

どんな助けでも大歓迎です。

ありがとうございました!

更新-最初の答えを読んで、私のアプローチは完全に間違っているようです。50〜70の異なるリボンが存在する可能性がある場合、理想的にはコードをどのように構成しますか?ありがとう

4

8 に答える 8

6

ドメインインスタンスにDAOを注入することに同意すると言っているわけではありません....しかし

DAO をドメイン オブジェクトに接続できます。ただし、Spring アプリケーション コンテキストでドメイン オブジェクトを宣言し、Spring から新しいインスタンスを取得する必要がありますnew。必ずプロトタイプ スコープを使用してください。毎回同じシングルトン インスタンスを取得する必要はありません。

実際に実装したいこのロジックは、必要な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;
  }  
于 2012-06-15T19:47:40.677 に答える
4

マークを付けます@Configurable- @Configurableアノテーションにより、Bean が Spring の外部で作成された場合でも、依存関係が確実に注入されます。

<context:spring-configured/>また、コンテキストに追加する必要があります。

于 2012-06-15T20:40:57.257 に答える
2

答えが 1 つ欠けていて、きれいではありませんが、うまくいきます。Dao を配線する代わりに、WebApplicationContext から検索できます。

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

これは、依存性注入が表すすべてのものの反対です (私はこのパターンを「制御の反転の反転」と呼んでいます :-)) が、ドメイン オブジェクトへのサービスの注入も同様です。

于 2012-06-24T11:02:10.283 に答える
0

デザインを微調整する必要があると思います。私が最初に質問したのは、「リボンクラスに、どのユーザーがそれを持っているかを確認する機能があるのはなぜですか」というものでした。これは、キッチンテーブルに。というメソッドが必要だと言っているようなものboolean doesThisKitchenHaveMe(Kitchen k)です。

リボンをユーザーにマップする3番目のロケーターサービスが必要であることが私にはより論理的に思えます

于 2012-06-22T16:05:05.377 に答える
0

DomainObjectでDAOを使用する理由 (IMHO)メソッドisEarned(User user)はFirst2000UsersRibbonに関連しないため、DAOとDomainObjectを分離することをお勧めします。

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;}
}
于 2012-07-12T03:50:45.333 に答える
0

私が見ることができることから、あなたの休止状態のクラスのデザイン、および獲得したリボンの永続性は問題ありません。問題は、ユーザーが新しいリボンを獲得したかどうかをいつどのように決定するかということだと思います。

ログインしたユーザーから新しいリクエストが届いたとします。UserオブジェクトはHibernateによって作成および設定され、userHasRibbonSetでそのユーザーがすでに獲得したすべてのリボンがわかります。Userにはおそらく次のようなメソッドが必要です。

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

(これは、リボン自体をセットにキャッシュし、一定時間のルックアップを実行することでおそらく最適化できますが、ここでは重要ではありません)

リクエストが処理され、発生した内容を反映するようにUserオブジェクトが更新されます。次に、途中で、ユーザーが現在獲得しているリボンを確認します。次のようになります。

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));
                }
            }
        }
    }
}

この正確なパターンを使用する場合は、このインターセプターが、リボンに関連する方法でユーザーを更新するインターセプターの後に、トランザクションを閉じるインターセプターの前にあることを確認してください(リクエストごとのトランザクションモデルを使用している場合)。次に、Hibernateセッションをフラッシュすると、UserHasRibbonテーブルが自動的に更新されるため、専用のDAOは実際には必要ありません。

これは単純なアプローチであり、明らかに改良することができます。明らかな改善は、チェックしているリボンをより選択的にすることです。おそらく、各コントローラーメソッドは、関連するリボンが現在適用可能かどうかを確認することで終了する可能性があります。コントローラーは、アクションの後にどのリボンが授与されるかを知る必要があります。

お役に立てば幸いです。ポイントを完全に逃した場合はお知らせください。もう一度やり直します。

于 2012-06-20T20:26:45.120 に答える
0

First2000UsersRibbonクラス@Componentで @Entity アノテーションと一緒に宣言するアノテーションを使用することもできます。そして、このクラスを持つパッケージが にあることを確認してください。これに伴い、このクラスのオブジェクトが演算子を使用して作成されていないことを確認する必要があります。<context:component-scan base-package="" />new

これがお役に立てば幸いです。乾杯。

于 2012-06-19T07:32:09.723 に答える
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
    }
}

サービスでは、システム内のすべてのリボンのキャッシュ コレクションを保持できます (動的でない限り)。イベント トリガー (新しいユーザー、回答、投票など) によってリボンを分類することもお勧めします。現在のユーザーで該当するリボン リストを反復処理することにより、(すべてではなく) 適切なリボンのサービスのみをチェックインします。

于 2012-06-20T02:07:13.423 に答える