NetBeansによるSpring Bootアプリケーションの開発

Gradleプラグイン

NetBeansで、[ツール]→[プラグイン]→[使用可能なプラグイン]を選択し、「Gradle Support」をインストールする。

プロジェクトの作成

http://start.spring.io にアクセスする。

1: 「Gradle Project」を選択する。

2: Groupを指定する。パッケージだと思えば良い。net.teachingprogramming.webapp

3: Artifactを指定する。アプリの名前だと思えば良い。webapp

4: 「web」と入力すると候補として「Web」が表示されるので、選択する。同様に「Thymeleaf」も選択する。

5: 「Web」と「Thymeleaf」が選択されていることを確認する。

5: クリックするとZIPファイルのダウンロードが始まる。

Spring Initializr

NetBeansで、[ファイル]→[プロジェクトをインポート]→[ZIPから]を選択し、ダウンロードしたZIPファイルを選択して[インポート]をクリックする。

NetBeans IDE 8 0 2

緑色のアイコンがついたプロジェクトが生成される。

アプリケーションの実行

webapp [root]を右クリックし、[Tasks]→[boot]→[bootRun]を選択すると、ビルド(コンパイル等)が開始され、ビルドが成功するとアプリケーションが起動する。
出力ウィンドウに次のように表示されれば起動完了。

Tomcatが8080ポートで起動しているのがわかるので、ブラウザでアクセスすると、エラーメッセージが表示される。これは、まだコンテンツを返すプログラムを作っていないために表示されるもので、正常な動作です。ブラウザのエラー(Chromeの場合は「このサイトにアクセスできません」と表示される)とは違うことを確認してください。

Localhost 8080

アプリケーションを終了するには、出力ウィンドウの左側にあるStopボタンをクリックする。

第6回: コレクション(2)

コレクションに関する Q and A

Q: プリミティブ型を格納できる?
A: できません。ラッパークラスを使いましょう。

Q: 自作クラスのインスタンスをHashSetに格納したいのだけど、「同じ」の判定がおかしい?
A: equalsメソッドとhashCodeメソッドを適切にオーバーライドしましょう。

Q: ArrayListの要素を取得するとき、範囲外のインデックスを指定して、エラーになるんだけど、エラーを出さないようにするには?
A: きちんと例外処理を行いましょう。

Q: HashSetで要素を取得するとき、格納されてないキーを指定してエラーになるんだけど、、エラーを出さないようにするには?
A: nullかどうか判定するか、例外処理を行いましょう。

Q: コレクションの要素をソートしたいんだけど?
A: 単純なソートならCollections.sortを使う。ソート順を細かく指定したいなら、Collections.sortにComparatorを渡す。常にソートした状態にする(addしたタイミングでソートする)なら、対象のクラスにComparableを実装しておく。

第5回: コレクション(1)

コレクション

コレクションとはデータの集合のこと。Javaにはコレクションを扱うライブラリが標準で含まれている。

よく使うコレクションとして、次の3つがある

java.util.ArrayList
順序の情報を保持するコレクション。同じ要素も格納できる。
java.util.HashSet
同じ要素格納できない。順序も保持できない。注: デメリットではない
java.util.HashMap
キーと値のペアで保持する。辞書のようなもの。

ArrayListの利用

ArrayListの後ろに中に格納するインスタンスの型を「<」と「>」で囲んで記述する。

addメソッドで追加する。

取り出すときはgetメソッドを使う。引数として0から始まるインデックスを指定する(配列の場合と同じ)。全てを取り出すときは、for文が使える。リストの要素数(サイズ)はsizeメソッドで取得することができる。

コレクションからデータを取り出すときには、「拡張for文」を使うと簡潔に記述できる。

自作のクラスのインスタンスを格納する場合も同様。

HashSetの利用

ArrayListと同様に利用出来るが、以下の違いがある。

  • 要素が順番に並んでるわけではないので、getメソッドが使えない。取り出すときは拡張for文で取り出す(他の方法としてIteratorを使う方法等があるが本講義では扱わない)。
  • 同じ要素は1つだけ格納される。下記のソースコードの場合、出力は3つになる。

格納する型(クラス)によっては、意図した通りにならない場合がある。例えば自作のPersonクラスの場合、「たろう」が2回、取り出されてしまう。これは、それぞれの「たろう」を表すインスタンスが「同じ」とみなされていないから、である。

「どうしたら意図したように動作するのか?」はまた後日解説するので、とりあえず、ここでは「HashSetが格納するのがStringのインスタンスなら大丈夫」と覚えておこう。

HashMapの利用

キー(key)と値(value)のペアで格納することができる。つまり辞書のようにデータを格納することができる。keyはSetで管理されるので、同じkeyを使って別の値を格納することはできないので注意。

インスンタンス化時、格納する型はkeyとvalueのそれぞれ指定する。

putメソッドで格納する。addメソッドではない点に注意。

取得はgetメソッドだが、インデックスではなく、keyを引数にする。

すでに格納されているkeyでputすると、上書きされる。

keyを取得したい場合は、keySetメソッドで、keyのSetを取得する。keyのSetから1つづつ取り出すのは、拡張for文を使う。

ソースコード全体

第4回: 復習3

クラスの基礎

コンストラクタ

インスタンス化するときに実行されるメソッドのこと[教科書P.137]。引数有りのコンストラクタを用意する(また、引数なしのコンストラクトを用意しない)ことによって、インスタンス化時にフィールドの値を設定する等の処理を行うことができる。

アクセス修飾子

メンバー(フィールド、メソッド)を、どこからアクセスできるか(読み書きできるか)を指定する[教科書P.137]。

クラスを拡張する

あるクラスを元に、新しくクラスを作る(継承する)ことができる。元になるクラスを「スーパークラス」、継承したクラスを「サブクラス」という。継承するにはextendsキーワードを使う[教科書P.126]。

オーバーライド

スーパークラスで定義されたメソッドを、サブクラスで書き換える(上書き)することができる。これをオーバーライドという[教科書P.131]。元のメソッドとオーバーライトしたメソッドを区別するにはsuperキーワードを使う[教科書P.127]。

第3回: 復習2

クラスの基礎

クラスを定義する

  • パッケージ宣言(1行目)で、クラスが所属するパッケージを指定する[教科書P.24]。
  • クラスには、メンバ(member)によって構成されている。メンバには値を保存するためのフィールド(field)と、処理をするためのメソッド(method)の2つがある[教科書P.114]。
  • メンバは、お互いに利用可能。上記の例では、メソッドbmiでフィールドwidthとheightを利用して計算している。

クラスを使う(インスタンスを作って使う)

  • クラスは「設計図」に位置づけられるので、そのままでは使えない。設計図から「実体」をつくってから利用する。実体のことをインスタンス(instance)という。クラスからインスタンスを作ることを「インスタンス化」という。
  • インスタンスの作成にはnew演算子を使う[教科書P.115]。
  • メンバを使うには、「.」を使う。
  • 同じパッケージのクラスあるいは、import宣言したクラスはパッケージを書かなくても使える(5行目と11行目を比較)。

第2回: 復習1

演習

  • ランダムに「ズン」もしくは「ドコ」を繰り返し出力しなさい。
  • 「ズン」「ズン」「ズン」「ズン」「ドコ」の順番で出力されたら、そのあとに「キ・ヨ・シ!」と出力してからプログラムを終了しなさい。

プログラム例

第1回: ガイダンス等

演習

第1回の演習、「3の倍数でアホになる」をJavaで書こう!

JPAによるリレーション: @ManyToMany

(書き途中)

マルチユーザのメモ帳。

UserとMemoを@ManyToManyでリレーション

EntityとRepository

View

Controller

URLに含まれるパラメータをコントローラで処理する

掲示板でUserとCommentのリレーションを設定したので、Userごとのコメント一覧を表示してみましょう。

まず、どのユーザを対象にするかを指定しなければなりません。これにはformのパラメータを受け取る方法がつかえますが、ここではページ遷移のしやすさを考慮してURLの一部をパラメータとして利用する方法を使ってみましょう。例えばuser1というユーザ名のコメント一覧を表示するURLを「/bbs/user/user1」のようにします。

Controller

URLの一部をパラメータとして利用するには@PathVariableアノテーションを利用します。@RequestMappingで指定するURLの一部を{}で囲むようにすると、この部分が変数として扱えるようになります。

  • 45行目: URLのうちパラメータにしたい部分を{}で囲む。囲んだ文字列は@PathVariableの引数で参照できる。
  • 46行目: URLから取得するパラメータを、メソッドの引数にし@PathVariableをつける。@PathVariableの引数はURLの{}の中の文字列にする。
  • 49行目: UserとCommentでリレーションが設定されているので、UserのインスタンスからCommentのListが取得できる。
  • 53-55行目: 48行目のgetOneでは、データが見つからない場合EntityNotFoundExceptionが発生するので、catchする。ここでは(手を抜いて)単に/bbs/にリダイレクトしている。

View

Userごとのコメントを表示するViewは次のようにします。

Usercomment

掲示板のトップページからユーザ名をクリックしてユーザごとのコメント一覧ページが見られるようにしてみましょう。

  • 11行目: もともとspanだったのをaに変更する。href属性はth:href属性で動的に設定することができる。@{}の中で変数を使うためには__${}__で囲む。

Bbs

JPAによるリレーション: @OneToManyと@ManyToOne

ユーザ認証ができたので、これまでに作った掲示板を改良してみます。名前とコメントが入力できるようになっていますが、名前をCommentのフィールドにするのではなく、CommentとUserを関連付けます。

ユーザはコメントを複数書くことができるので、UserとCommentは1対多の関係となります。ユーザごとにコメント表示できるようにし、またコメントがどのユーザのものかを表示できるようにするため、双方向の関係を持つようにします。

このような関係(リレーション)を定義するにはEntityにアノテーションをつけます。

Entity

まず、UserにCommentを格納するフィールドを追加します。Userが1でCommentが多と定義するには@OneToManyをつけます。

  • 30行目: @OneToManyアノテーションで「1対多」であることを表す。アノテーションの引数であるmappedByについては後述。
  • 31行目: 「多」となるEntityをListで格納できるようにする。

Commentは「多」なので@ManyToOneアノテーションをつけます。

  • 21行目: @MaynToOneアノテーションで「多対1」であることを表す。
  • 22行目: 「1」となるEntityを格納できるようにする。

それぞれのEntityにアノテーションを付けたので双方向の関係(UserからCommentを、CommentからUserを取得できる)になります。

これに合わせてControllerとViewも修正します。

Controller

Controllerでは、コメントのPOST時にUserを取得し、Commentにセットしてから保存します。

View

Viewでは、名前の入力欄を削除し、表示される名前はUserのusernameに変更します。

  • 10行目: commentからuserを取得しそのフィールドであるusernameを表示する。
  • 13行目: 名前の入力欄は削除し、textの入力欄だけにする。

これで、UserとCommentを関連付けられました。

データベースにはどのように保存されているのか

@OneToManyと@ManyToOneをつかって双方向の1対多のリレーションを設定した場合、RDMSではどのように保存されているのでしょうか。 H2コンソールを使って中身を覗いてみましょう。

Userが保存されるUSERSテーブルは次のようになります。

Dbuser

UserにcommentListは見当たりません。

Commentが保存されるCOMMENTテーブルは次のようになります。

Dbcomment

UserのIDが格納されるカラムができています。

このようなテーブルになっているのは@OneToManyにmappedByがついているためです。

通常、@OneToManyをつけた場合は、Listを格納するために専用のテーブル(この場合、UserのIDとCommentのIDの対応が保存されるテーブル)が作成されます。@ManyToOneの場合は上記のようにテーブルにカラムが追加されます。これだと情報を二重にもってしまうことになります。また、UserにCommentのListを保存してsaveする必要があります。これでは、コードも煩雑になり、コードにミスがあればデータベースに不整合がおこりかねません。

これを防ぐために、@OneToManyと@ManyToOneの双方向のリレーションを設定する場合は、@OneToManyがつけられたEntityにアノテーションの引数でmappedByを指定し、参照するデータをもう一方の@ManyToOneがつけられたEntityのものにします。

今回の場合は、UserのcommentListは(UserのIDとCommentのIDの対応が保存されるテーブルからデータを取得するのではなく)CommentのUserフィールドの情報から取得していることになります。