Eclipse+JSF+JPAで作るアプリ(17)―Primefaces Autocompleteを使う。

今回は、映画の検索画面を作成します。
技術的な内容は、以下の通りです。

  • Primefacesのp:autocompleteの使い方。
  • JPAのdistinct句
  • Java SDK8.0のLambda式

これらを組み合わせて使っているので、実装自体はすっきりとします。

実装すると次のような画面が表示されます。
f:id:tshix:20150803042540p:plain

なお、MovieクラスのCRUD操作は、前回までのUserクラスのCRUD操作と同様なので割愛しています。
いつものように画面のxhtml(searchMovie.xhtml)→ManagedBean(SearchMovieView.java)→DBアクセス(MovieManager.java)という順で実装します。
eclipseを使っていると、仮のメソッドシグニチャを書く→プロポーザルからメソッドの実装という流れになりやすいです。

p:autocompleteタグの使用方法

autocompleteは以下のように記述します。

	<p:autoComplete id="title" value="#{searchMovieView.title}" 
	   completeMethod="#{searchMovieView.completeTitle}"/>

	<p:autoComplete id="category" value="#{searchMovieView.category}"
	   completeMethod="#{searchMovieView.completeCategory}"
	   dropdown="true"/>
  • completeMethodで、フィルタするためのメソッドを呼び出します。
  • dropdown="true"にすると、テキストボックスの横に、ドロップダウンボタンが表示されます。

検索画面は、上記の画像の通り、タイトル、カテゴリ、あらすじの3つのフィールドを持つため、
以下のようなxhtmlになります。

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:p="http://primefaces.org/ui"
      >
<h2>映画の検索</h2>
<p:growl id="growl" showDetail="true" autoUpdate="true"/>

<h:form id="searchMovieForm">
	<p:panelGrid columns="2" >
		<p:outputLabel value="タイトル:" />
		<p:autoComplete id="title" value="#{searchMovieView.title}" 
		completeMethod="#{searchMovieView.completeTitle}"/>
		
		<p:outputLabel value="カテゴリ:" />
		<p:autoComplete id="category" value="#{searchMovieView.category}"
		completeMethod="#{searchMovieView.completeCategory}"
		dropdown="true"/>
		
		<p:outputLabel value="あらすじ:" />
		<p:autoComplete id="outline" value="#{searchMovieView.outline}" 
		completeMethod="#{searchMovieView.completeOutline}"/>
	</p:panelGrid>
	<p:messages />
	<p:commandButton value="映画の検索" actionListener="#{searchMovieView.searchMovie}" 
		update=":movieListForm:movieList" />
</h:form>

ManagedBeanでのcompleteMethodの実装

ManagedBeanのcompleteXxxメソッドの実装です。
SearchMovieView.java

	private List<String> enteredTitles;
	…

	@PostConstruct
	public void init()
	{
		movies = new ArrayList<Movie>();
		movieModel = new IdEntityListDataModel<Movie>(movies);
		enteredTitles = MovieManager.getEnteredTitles();
		…
	}
	…
	public List<String> completeTitle(String input)
	{
		return enteredTitles
				.stream()
				.filter(e -> e.contains(input))
				.collect(Collectors.toList());	
	}

	public List<String> completeCategory(String input)
	…

	public List<String> completeOutline(String input)
	…
  • List<String> enteredTitlesでDBに入力されている映画のタイトルを定義しています。
  • @PostConstructのinit()メソッドでdistinctした値をMovieManagerから取得しています。(後述)
  • completeTitleで、入力された値(input)をフィルタしたList<String>を返します。
  • Java SDK 8.0から導入されたLambda式を使用しています。
Lambda式の利用

ラムダ式については、こちらの説明が全体像の把握には、分かりやすかったです。
http://www.ne.jp/asahi/hishidama/home/tech/java/lambda.html

上記処理では

  • stream()でストリームAPIを取得し、
  • 中間操作 filter()で、要素eがinputを含むものを取り出し、
  • 終端操作 collect() でリストにして返しています。

フィルタ処理もラムダ式では1行で書けるのですっきりします。

DBアクセスクラス MovieManager.javaの実装

	public static List<String> getEnteredTitles()
	{
		EntityManager em = getEm();
		TypedQuery<String> q = em.createQuery("select distinct m.title from Movie m order by m.title asc", String.class);
		List<String> result = q.getResultList();
		em.close();
		return result;
	}
  • select distinctで、Movie.titleを一意なリストとして取得しています。

以上です。
JPAのdistinct、Lambda式を使えば、簡単にautocompleteを実装できます。

次回は、ひさしぶりに、JPAのCriteria APIについて取り上げます。