1. ホーム
  2. database

h:selectOneMenuのオプションをデータベースから入力する方法は?

2023-09-01 20:46:55

質問

DBからオブジェクト/エンティティのリストを読み込んで、JSFに入力する必要があるWebアプリケーションを作成しています。 <h:selectOneMenu> . 私はこれをコード化することができません。誰かがそれを行う方法を私に示すことができますか?

私はどのように List<User> をDBから取得する方法は知っています。私が知る必要があるのは、このリストを <h:selectOneMenu> .

<h:selectOneMenu value="#{bean.name}">
    ...?
</h:selectOneMenu>

どのように解決するのですか?

質問履歴からすると、JSF2.xをお使いのようですので、JSF2.xをターゲットにした回答です。JSF 1.xでは、項目の値/ラベルを醜くラップすることを余儀なくされるでしょう。 SelectItem インスタンスで包まざるを得ませんでした。これは幸いなことに、JSF 2.xではもう必要ありません。


基本的な例

質問に直接答えるには、単に <f:selectItems> その value を指し、その List<T> プロパティを指し、これはビーンの (後) 構築時に DB から保存します。以下は、基本的なキックオフの例で、以下を仮定しています。 T が実際に String .

<h:selectOneMenu value="#{bean.name}">
    <f:selectItems value="#{bean.names}" />
</h:selectOneMenu>

@ManagedBean
@RequestScoped
public class Bean {

    private String name;
    private List<String> names; 

    @EJB
    private NameService nameService;

    @PostConstruct
    public void init() {
        names = nameService.list();
    }

    // ... (getters, setters, etc)
}

というように単純です。実は、この T 's toString() は、ドロップダウン項目のラベルと値の両方を表すために使用されます。ですから、あなたが代わりに List<String> のような複雑なオブジェクトのリストを使って List<SomeEntity> のような複雑なオブジェクトのリストを使っていて、 クラスの toString() メソッドをオーバーライドしていない場合、次のように表示されます。 com.example.SomeEntity@hashcode と表示されます。この問題を解決するには、次のセクションを参照してください。

のビーンも <f:selectItems> のビーンと同じビーンである必要はありません。 <h:selectOneMenu> の値と同じビーンである必要はありません。これは、値が実際にはアプリケーション全体の定数であり、 アプリケーションの起動時に一度だけロードする必要がある場合に便利です。そして、それをアプリケーションスコープドビーンのプロパティにすることができます。

<h:selectOneMenu value="#{bean.name}">
    <f:selectItems value="#{data.names}" />
</h:selectOneMenu>


利用可能なアイテムとしての複雑なオブジェクト

いつでも T のような複雑なオブジェクト (javabean) に関係します。 User のような複雑なオブジェクトに関係します。 String のプロパティは name を使用する場合、その後に var 属性を使って反復変数を取得し、それを itemValue または itemLabel 属性(もし itemLabel を省略した場合、ラベルは値と同じになる)。

例1.

<h:selectOneMenu value="#{bean.userName}">
    <f:selectItems value="#{bean.users}" var="user" itemValue="#{user.name}" />
</h:selectOneMenu>

private String userName;
private List<User> users;

@EJB
private UserService userService;

@PostConstruct
public void init() {
    users = userService.list();
}

// ... (getters, setters, etc)

を持つ場合、あるいは Long プロパティ id というように、項目の値として設定したいものを指定します。

例2:

<h:selectOneMenu value="#{bean.userId}">
    <f:selectItems value="#{bean.users}" var="user" itemValue="#{user.id}" itemLabel="#{user.name}" />
</h:selectOneMenu>

private Long userId;
private List<User> users;

// ... (the same as in previous bean example)


複合オブジェクトを選択アイテムにする

に設定したいときは、いつでも T プロパティと同様にビーン内の TUser を表すのであれば、カスタム Converter の間を変換する User と一意な文字列表現 (これは id プロパティにすることができます)。注意すべきは itemValue は複合オブジェクトそのものを表し、まさに選択コンポーネントの value .

<h:selectOneMenu value="#{bean.user}" converter="#{userConverter}">
    <f:selectItems value="#{bean.users}" var="user" itemValue="#{user}" itemLabel="#{user.name}" />
</h:selectOneMenu>

private User user;
private List<User> users;

// ... (the same as in previous bean example)

@ManagedBean
@RequestScoped
public class UserConverter implements Converter {

    @EJB
    private UserService userService;

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

        try {
            return userService.find(Long.valueOf(submittedValue));
        } catch (NumberFormatException e) {
            throw new ConverterException(new FacesMessage(String.format("%s is not a valid User ID", submittedValue)), e);
        }
    }

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

        if (modelValue instanceof User) {
            return String.valueOf(((User) modelValue).getId());
        } else {
            throw new ConverterException(new FacesMessage(String.format("%s is not a valid User", modelValue)), e);
        }
    }

}

(なお Converter を注入できるようにするために、少しハチャメチャになっています。 @EJB を注入することができるようにするために、ちょっとハックしています。 @FacesConverter(forClass=User.class) , というアノテーションをつけるでしょうが、残念ながらそれは @EJB インジェクション )

複雑なオブジェクトクラスには、必ず equals()hashCode() を適切に実装した を実装していなければ、JSFはレンダリング中にあらかじめ選択された項目を表示することに失敗し、あなたは投稿時に バリデーションエラー。値が有効ではありません .

public class User {

    private Long id;

    @Override
    public boolean equals(Object other) {
        return (other != null && getClass() == other.getClass() && id != null)
            ? id.equals(((User) other).id)
            : (other == this);
    }

    @Override
    public int hashCode() {
        return (id != null) 
            ? (getClass().hashCode() + id.hashCode())
            : super.hashCode();
    }

}


汎用コンバータで複雑なオブジェクトを扱う

この答えに向かう。 Java Genericsでエンティティのコンバータを実装する .


カスタムコンバータを使用しない複雑なオブジェクト

JSFユーティリティ・ライブラリ オムニフェーズ は特別なコンバータを提供し、複雑なオブジェクトを <h:selectOneMenu> のように、カスタムコンバータを作成する必要がありません。その SelectItemsConverter にある利用しやすい項目に基づいて変換を行います。 <f:selectItem(s)> .

<h:selectOneMenu value="#{bean.user}" converter="omnifaces.SelectItemsConverter">
    <f:selectItems value="#{bean.users}" var="user" itemValue="#{user}" itemLabel="#{user.name}" />
</h:selectOneMenu>


こちらもご覧ください。