0

したがって、BalusCのこの投稿から、ステートレス セッション Bean が JSF によってアクセスされたときにデータ ストア (DB など) を継続的にスラッシングするのを防ぐ方法 (複数の呼び出しを行う可能性があります) を読んだので、コードを実装しました。 BalusCによって投稿された内容(およびこの問題に関して私が見た「ベスト プラクティス」からの他のフォーラム投稿)の精神にあると考えたいと思います。

私のステートレス セッション Bean は次のようになります。

@Stateless
@Named("productsService")
public class ProductService {

    private static boolean changed = true;

    private List<Product> products;

    private long count;

    @PersistenceContext(name = "myPU")
    private EntityManager em;

    @Inject
    private Product product;

    public ProductService() {
    }

    private void productRecordsFromDB() {
        products = em.createNamedQuery("Product.getAll", Product.class).getResultList();
        Object o = em.createNamedQuery("Product.getCount", Product.class).getSingleResult();
        count = ((Long) o).longValue();
    }

    public void addProduct() {
        synchronized (ProductService.class) {
            changed = true;
            em.persist(product);
        }
    }

    public long countProducts() {
        return count;
    }

    public void removeProduct(Product p) {
        synchronized (ProductService.class) {
            changed = true;
            em.remove(em.merge(p));
        }
    }

    public int removeAllProducts() {
        synchronized (ProductService.class) {
            changed = true;
            return em.createNamedQuery("Product.deleteAll").executeUpdate();
        }
    }

    public List<Product> getProducts() {
        synchronized (ProductService.class) {
            if (changed) {
                productRecordsFromDB();
                changed = false;
            }
        return products;
        }
    }

    public Product getProduct() {
        return product;
    }

    public void setProduct(Product p) {
        product = p;
    }
}

編集:シリアルアクセスを確実にするために、関連する部分に同期ブロックを追加しましたが、これはシングルトンのように感じ始めています。データストアへの複数の呼び出しに関連するこの問題に他の人がどのように対処したかを知りたいです。

具体的には、チェックされる「ダーティ」フラグを作成しました。DB が更新された場合、ダーティ フラグは (DB の更新によって) true に設定されます。ダーティ フラグが検出されると、フラグは false に戻されるため、DB への呼び出しは 1 回だけ行われます。したがって、DB のスラッシングは発生しません。

私の質問は次のとおりです:私がやったこと、これは解決策を解決するための適切な「ベストプラクティス」ですか、それとも私が気付いていないより賢い方法はありますか? 私は、古い「J2EE」ブルー プリント デザイン パターンと、Java EE 6 のコンテキスト内で欠落している可能性のある注釈の観点から考えています。

4

4 に答える 4

5

これは解決策を解決するための適切な「ベストプラクティス」ですか、それとも私が気付いていないより賢い方法はありますか?

セッション Bean を構築する方法は、残念ながらベスト プラクティスではありません。実際、Mikko が説明したように、セッション Bean が通常どのように動作するかは完全に反しています。したがって、あなたの明らかな努力にもかかわらず、あなたが作成したものは悪い習慣の典型的な例です。これは、ステートレス セッション Bean で行うべきではないほとんどすべてのことを行います。

BalusC が概説した問題を解決するために、ビューをサービスに直接バインドさせる代わりに、ビューをバインドする別のバッキング Bean を使用できます。このバッキングは、リクエスト中またはビュースコープの期間中に結果をキャッシュする Bean です。

例えば

サービス:

@Stateless
public class ProductService {

    @PersistenceContext(name = "myPU")
    private EntityManager em;

    public List<Product> getProducts() {
        em.createNamedQuery("Product.getAll", Product.class).getResultList();
    }

    // ...
}

次に、バッキング Bean (CDI を使用している場合は、CODI などを使用して @ViewScoped アノテーションを追加します) :

@ViewScoped
@ManagedBean
public class SomeBacking {

    private List<Product> products;

    @EJB
    ProductService productService;

    @PostConstruct
    public void init() {
        products = productService.getProducts();
    }

    public List<Product> getProducts() {
        return products;
    }
}

サービスへの呼び出しを@PostConstructここに配置しましたが、正確な要件に応じて、これはもちろん、アクション メソッドまたはゲッターの遅延読み込みパターンを介して行うこともできます。

于 2012-05-01T08:07:34.800 に答える
2

2 つの主な問題があります。

  1. ロックでガードせずにスレッド間で状態を共有する
  2. ステートレス Bean には、クライアントに見える状態があります。

変更された静的変数を介して、ProductService のすべてのインスタンス間で状態を共有します。ただし、この変数への読み取りと書き込みは同期されません。その結果、インスタンスがchangedに対して同じ値を共有するという保証はありません。ロックを介してアクセスを制御する(同じロックと同期する)か、少なくとも揮発性にする必要があります(最後に書き込まれた値が表示されることを保証するだけです)。

次の問題は、ステートレス Bean がクライアントに見える状態を持つべきではないということです。そのため、彼らはステートレスと呼ばれます。クライアントはどのインスタンスが呼び出されるかを制御できないため、どのインスタンスが呼び出しを処理するかはクライアントにとって何の違いもありません。たとえば、次のシナリオを考えてみてください (インスタンス s1 と s2、両方とも新しい状態にあると仮定)。

  1. s1.getProducts => 変更済み = false
  2. s2.getProducts => changed が false であるため、null が返されます (s2 のインスタンス変数 products に値が設定されていません)。

編集された質問とコメントへの回答: ステートレスとステートではまだ同じ問題があります。ステートレスは、状態のないソリューションが適合しない場合に使用されます。以下からわかるように、どのインスタンスがビジネス メソッド呼び出しを処理するかは依然として重要です。

  1. s1.getProducts => 変更済み = false;
  2. s2.getProducts => changed が false であるため、null が返されます

Arjan Tijmsが提供するアプローチ、または少なくとも同じ精神の何かに従ってください。

于 2012-04-30T05:06:37.883 に答える
1

これは、私が長い間見たステートレスセッションBeanの最悪の悪用です。

あなたは一種のシングルトンを作成しましたが、これにはまったく適していないBeanで、グローバル操作とローカル操作の懸念が混在し、クラスリテラルで同期することによるデッドロックのリスクがあります。

シングルトンが必要な場合は、専用の@Singleton Beanを使用する必要がありますが、リクエスト中にJSFからの繰り返し呼び出しを防ぎたい場合は、他の回答に示されているようなバッキングBeanを使用してください。

于 2012-05-01T16:33:14.977 に答える
0

シングルトンとしての EJB の具体的なアプリケーションがあります。例はこちらです。EJB 3.0 標準では、単一のセッション Bean へのタイムシェアの同期アクセスが可能です。そうは言っても、セッションキャッシングを防止/制御するには、次のオプションがあります。

1) シングルトン EJB は、EJB 3.0 タイマー Bean を使用してスケジュールに従って更新されるデータベース結果のローカル キャッシュ コピーを維持する場合があります。このシングルトンは、JSFマネージドBean、CDI Beanなど、好きなコンテキストに注入できます

2) あるいは、特定のユースケースで必要なデータの独自のローカル コピーをキャッシュすることもできます。これはBean の構築時に一度@PostConstruct読み取られますが、このパラダイムは Bean よりも寿命の長い Bean に適用でき@RequestScopedます。

3) 2) に関連して、@ApplicationScoped上記の 1) と同様に機能する Bean がありますが、さまざまなリストの (巨大な) キャッシュを維持します。

于 2012-09-05T01:03:09.353 に答える