web-dev-qa-db-ja.com

エンティティのマスター/詳細ページの作成、それらをリンクする方法、および選択するBeanスコープ

私はJSFの学習を始めましたが、残念ながら、そこにあるほとんどのチュートリアルには、ログインまたは登録セクションしかありません。

もう少し詳細な例を教えていただけますか?私が興味を持っているのは、製品のリストを表示するページです。私はページhomeにいて、ページproductsを押して、最新のproductsが追加されたことを確認します。また、このページにアクセスするたびに、データベース内の最新のエントリから製品リストが作成されます。どうすればこれを処理できますか?

これを解決する1つの方法は、他のマネージドBeanを介して更新されたさまざまなエンティティを配置するセッションスコープのマネージドBeanを作成することです。いくつかのチュートリアルでこの種のアプローチを見つけましたが、それは非常に難しく、不器用なようです。

このようなことを解決するための最良のアプローチはどれですか? 2ページのマスター/詳細ユーザーインターフェイスでのセッションスコープの正しい使用法は何ですか?

27
TGM

セッションスコープの正しい使用法は何ですか

セッションスコープのデータにのみ使用し、他には使用しないでください。たとえば、ログインしたユーザー、その設定、選択した言語などです。

参照:


そして、このページにアクセスするたびに、データベースの最新のエントリから製品リストが作成されます。どうすればこれを処理できますか?

通常、リクエストまたはビュースコープを使用します。リストのロードは、@PostConstructメソッドで実行する必要があります。ページに<h:form>が含まれていない場合、リクエストスコープは問題ありません。とにかく<h:form>がない場合、ビュースコープのBeanはスコープのリクエストのように動作します。

すべての「製品の表示」および「製品の編集」リンク/ボタンは、情報の取得(つまりべき等)であり、エンティティ識別子を渡すだけの単純なGET <h:link>/<h:button>になります。 <f:param>によるリクエストパラメータとして。

情報の操作(つまり非べき等)を実行するすべての「製品の削除」および「製品の保存」リンク/ボタンはPOST by <h:commandLink>/<h:commandButton>(ブックマーク可能/ searchbot-indexableにしたくない!)これには<h:form>が必要です。検証とajaxリクエストのためにデータを保持するために(そうしないように)リクエストごとにエンティティをリロード/事前初期化する必要があります)、Beanはビュースコープであることが望ましいです。

基本的に、ビューごとに個別のBeanを用意する必要があることに注意してください。また、これらのBeanは必ずしも相互に参照する必要はないことに注意してください。

したがって、この「製品」エンティティを考えると、次のようになります。

@Entity
public class Product {

    @Id
    private Long id;
    private String name;
    private String description;

    // ...
}

そして、この「製品サービス」EJB:

@Stateless
public class ProductService {

    @PersistenceContext
    private EntityManager em;

    public Product find(Long id) {
        return em.find(Product.class, id);
    }

    public List<Product> list() {
        return em.createQuery("SELECT p FROM Product p", Product.class).getResultList();
    }

    public void create(Product product) {
        em.persist(product);
    }

    public void update(Product product) {
        em.merge(product);
    }

    public void delete(Product product) {
        em.remove(em.contains(product) ? product : em.merge(product));
    }

    // ...
}

この「製品の表示」は/products.xhtmlで行うことができます。

<h:dataTable value="#{viewProducts.products}" var="product">
    <h:column>#{product.id}</h:column>
    <h:column>#{product.name}</h:column>
    <h:column>#{product.description}</h:column>
    <h:column>
        <h:link value="Edit" outcome="/products/edit">
            <f:param name="id" value="#{product.id}" />
        </h:link>
    </h:column>
</h:dataTable>
@Named
@RequestScoped
public class ViewProducts {

    private List<Product> products; // +getter

    @EJB
    private ProductService productService;

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

    // ...
}

そして、この「製品の編集」を/products/edit.xhtmlで行うことができます。

<f:metadata>
    <f:viewParam name="id" value="#{editProduct.product}" 
        converter="#{productConverter}" converterMessage="Unknown product, please use a link from within the system."
        required="true" requiredMessage="Bad request, please use a link from within the system."
    />
</f:metadata>

<h:messages />

<h:form rendered="#{not empty editProduct.product}>
    <h:inputText value="#{editProduct.product.name}" />
    <h:inputTextarea value="#{editProduct.product.description}" />
    ...
    <h:commandButton value="save" action="#{editProduct.save}" />
</h:form>
@Named
@ViewScoped
public class EditProduct {

    private Product product; // +getter +setter

    @EJB
    private ProductService productService;

    public String save() {
        productService.save(product);
        return "/products?faces-redirect=true";
    }

    // ...
}

そして、「製品の編集」の<f:viewParam>用のこのコンバーター:

@Named
@RequestScoped
public class ProductConverter implements Converter {

    @EJB
    private ProductService productService;

    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String value) {
        if (value == null || value.isEmpty()) {
            return null;
        }

        try {
            Long id = Long.valueOf(value);
            return productService.find(id);
        } catch (NumberFormatException e) {
            throw new ConverterException("The value is not a valid Product ID: " + value, e);
        }
    }

    @Override    
    public String getAsString(FacesContext context, UIComponent component, Object value) {        
        if (value == null) {
            return "";
        }

        if (value instanceof Product) {
            Long id = ((Product) value).getId();
            return (id != null) ? String.valueOf(id) : null;
        } else {
            throw new ConverterException("The value is not a valid Product instance: " + value);
        }
    }

}

ジェネリックコンバーターを使用することもできます。これについては、 Javaジェネリックス のエンティティーにコンバーターを実装する)で説明されています。

参照:

52
BalusC

BalusCが推奨するものの小さな改善として、「詳細」画面の<f:viewParam>からrequired/requiredMessageの部分を削除し、代わりに編集の条件付きレンダリングを使用できる場合があります。 「リスト/マスター」画面に特定のリンクを推奨するための逆条件を使用したフォーム(BalusCが行ったように)、またはパラメーターをテストしてそのリストへのリダイレクトを強制するviewActionを使用することもできます。

0