Tramoriesその1 開発環境構築(SpringBoot, MySQL)

前回の記事で個人開発でWebサービスを作るのを目標にすると書いて、1ヶ月以上経ってしまったorz
というわけでまずは環境構築(Macを前提にしています)。まずはサーバーサイドから。

1. MySQL
まだインストールしていない方はこちらが参考になると思います。
自分はすでにインストールしていましたが、下記のようなエラーが出るので、こちらを参考に解決を図る。

すると次は下記のエラーになったので、こちらを参考にしてみる。

そうしたら、解決!記事を書いてくれた方に感謝!m(__)m

その後は下記のSQLでユーザー、データベース、権限を作成。

CREATE DATABASE tramories;
CREATE USER tramories IDENTIFIED BY ‘*******’;
GRANT ALL ON tramories.* TO 'tramories'@'localhost' IDENTIFIED BY ‘*******’;

2. Spring Boot
データベースを作った次は、Spring Bootの環境構築。
Spring Bootの環境構築にはSpring Initializrというとても便利なサイトがあるので、これを使います。
自分はProject : Maven, Language : Java, Spring Boot : 2.5.0で作りました。
DependenciesはSpring Boot DevTools, Spring Web, Lombok(自動的にゲッターとセッターを作ってくれるので、すごく便利!), Thymeleaf, Spring Security, OAuth2 Client, Spring Data JPA, Flyway Migration, MySQL Driver, Spring Batchを入れました(他にも何か入れたかも)。

その後は大雑把に以下の流れ。
1. application.propertiesに必要な設定を追加
2. ログインユーザー用のサービス、エンティティ、リポジトリを作成し、Flywayでユーザーテーブルとレコードを作成
3. 簡単なログインページを作成し、ログインできるところまで確認

詳細は後日GitHubにアップするので、そちらを見ていただければと。

次回はフロントエンド(React)の環境構築をやります。

2021個人開発Webサービス案

明けましておめでとうございます。2021年もよろしくお願いします。

さて、今年の目標の1つとして、今年こそ個人のWebサービスを開発しようと思っています。
開発しようと思っているサービスと、使う予定の技術はざっと以下のような感じです。

サービス名
 Tramories トラモリーズ(travel + memories

サービス概要
旅の写真(昔の写真も含む)を共有し合うサービス

サービス詳細
投稿機能(写真+簡単な説明)、タグ、コメント、いいね、ユーザーフォロー
 OAuth2認証(検討中)オススメ機能(検討中)

一般的なミニSNSっぽい感じですが、

使用技術
 サーバーサイド
  JavaSpring Boot(機械学習を入れる場合はPythonFlask
Laravelを使うかかなり迷ったけど、今仕事で使っていることだし、Spring Bootにしようかと。
機械学習でおすすめ機能とかスパムフィルターとか取り入れるんだったら、Flaskで機械学習API作るのもありかなと思ったり。

 フロントエンド
  TypeScriptReact
フロントエンドのフレームワークはこれかなと。JavaScriptで行くかTypeScriptにするかは若干迷っているけど。

 データベース
  MySQL
ここはこうなるか。PostgreSQLは一度も触ったことがないんで。最近流行りのNoSQLも今回はいいかなと。

 Webサーバー
  Apache
ここもこれしかないか。Nginxも最近は増えてきているようだけど、Apacheで。

 インフラ
  Microsoft AzureHerokuなども検討中)
ここが一番の悩みどころ。以前AzureにWordpress入れたら1ヶ月で6000円とかしてすごい焦ったもんなあ。
誰かWebサービスを安くデプロイする方法を知っている方教えていただけるとありがたいです。

ドキュメント、その他
 画面仕様書、テーブル定義書、機能設計書(処理フローなど)、単体テスト仕様書、結合テスト仕様書
個人サービス作る場合ってドキュメントとか公開するものなのかなあ?公開しないにしても仕事の練習になるだろうし、作ろうとは思っています。

少しづつ作って行って、何とか年内には公開したいなあ。

Javaの例外処理

今回はQiitaのアドベントカレンダーの投稿日ということもあって、Javaでの例外処理のやり方について、さらっと紹介していこうと思います。

1. プログラムにおける例外とは

そもそもプログラムにおける例外は何かというと、一言で言うと「文法としては正しいにも関わらず、実行すると想定外の状況が発生し、プログラムが実行不能になること」と言うことになります。
まず厄介なのが、「文法としては正しい」ので、コンパイルが通ってしまうことです。なので、「よっしゃ、コンパイルが通ったぞ!」と思って実行したら、「あれ!?」と言うことになるわけです。
なので、どのような値が来るとどうなって、実際に起こり得るけど起こったらまずい状況とはどう言う場合かを考えて、それに応じて然るべき対応ができるようにする必要があるわけです。
次に、具体的に「どう言う状況が例外に当たるか」の例をいくつかあげて見ます。
・nullの領域にアクセスして何かやろうとした
・配列やリストなどの要素数より大きい数字のインデックスにアクセスした
・0で割り算した
・許可されていない場所にアクセスした
・ファイルに対して何かやってたらファイルが途中で消えた

2.例外処理の基本
Javaにおける例外処理の基本形は、こんな感じです。

try {
  // ここで何らかの処理
} catch (Exception e) {
  // 例外が発生したらここに来る
}

ただ、Exceptionクラスだと、nullアクセスも0割りも、その他諸々含めて一緒くたに扱われるので、一般的にはExceptionクラスのサブクラスを使って、起きた例外に応じた処理を行います。
例として、nullが入ったString変数に対して文字列の長さを取得しようとした場合を見てみます。

try {
  String str = null;
  System.out.println(str.length());
} catch (NullPointerException e) {
  System.err.println(e.getMessage());
  e.printStackTrace();
}

これで、例外の中でもNullにアクセスした場合の例外のみに対して例外処理が行われます。ちなみに、printStackTraceメソッドは例外が起きたところからのメソッド呼び出しの階層一覧を出力してくれます。結構便利なので、デバッグでよく使います。
どんな例外クラスがあるかは、Javaの公式ドキュメントを見てください。

3. 独自の例外定義とthrow、throws
Javaでは(大体の言語でそうだと思うけど)あらかじめ用意されている例外以外に、独自の例外クラスを作ることができます。
例えば、温度が100度を超えたらエラーにするとかを考えて見ます。

// このクラスで100度以上になった場合の例外を定義
class TooHotException extends Exception {
  public TooHotException(String s) {
    super(s);
  }
}
   try {
      int temperature = getTemperature();
      if (temperature > 100) {
        // 温度が100度以上だったら先ほど定義した例外クラスに投げる
        throw new TooHotException(“Too hot!”);
      }
    } catch (TooHotException e) {
      System.err.println(e.getMessage());
    }

こんな感じです。なお、ここで出てきたthrowは、呼び出すことで対応するcatch節に処理を移すのに使います。

もう1つ、throwに似た名前のthrowsという構文がありますが、これはある例外が発生したら呼び出し元に処理を任せるのに使うものです。
使い方はこんな感じです。


public void newFunc() throws NumberFormatException {
  // このメソッド内でNumberFormatException例外が発生したら、呼び出し元で例外処理を行う
}

ちなみに、NumberFormatExceptionは数値が入るべきところに数値以外のものが来た場合の例外です。

4. 例外が起きようと起きまいと共通の終了処理を行う場合
例外が発生して、何らかのエラーメッセージを出すものの、最後に何らかの共通処理を行いたい場合は、finallyを使います。

try {
  // ここで何らかの処理
} catch (Exception e) {
  // 例外が発生したらここに来る
} finally {
  // 例外が発生してもしなくても最後にここに来る
}

ファイルバッファを最後に閉じる例です。

  BufferedReader br = new BufferedReader(new FileReader(path));
  try {
    br.readLine();
  } catch (IOException e) {
    System.err.println(e.getMessage());
  } finally {
    if (br != null) br.close();
  }

ただ、Java7以降でtry-with-resources文を用いることによって、finallyを使わなくても使い終わったものを終了することができるので、紹介しておきます(こっちが使えるならこっちの方がいいです)。

  BufferedReader br = new BufferedReader(new FileReader(path));
  BufferedReader br = new BufferedReader(new FileReader(path));
  try (BufferedReader br = new BufferedReader(new FileReader(path));) {
    br.readLine();
  } catch (IOException e) {
    System.err.println(e.getMessage());
  }

5.その他注意点いくつか
例外処理におけるいわゆる禁じ手をいくつか紹介しておきます。
・例外を握り潰さない


try {
  // 何らかの処理
} catch (Exception e) {
}

これだと、例外が発生した場合でも握りつぶして次に進んでしまいますが、このようなことはせず、ちゃんと例外に応じた処理(エラーログを出して処理を終了するとか)を行いましょう。
・例外処理は本当の例外に対してのみ行う
ごく稀に、例外処理を分岐みたいな使い方をする人がいますが、このようなことをやってはいけません。分岐はちゃんとif文とかを使い、プログラムとかシステムとかで通常は起こりえないイレギュラーな事態だけcatchされるようにしましょう。

ここまでざっと例外処理について書いて来ましたが、やはり実際に色々プログラムを書いて見て慣れるのが一番です。
これを読んだみなさんも是非例外処理を使いこなせるように頑張りましょう!

Javaの文字列入門

ここのところあまり更新できていなかったので、久々の投稿、今回はJavaの文字列についての扱いを簡単にまとめて見ます。

Javaでは文字列は標準クラス(Javaの言語機構に標準で含まれているクラス)のStringを使います。

String str = new String("Hello world");

こんな感じです。
あるいは、下記のような書き方でも宣言できます。

String str = "Hello world";

ただし、newを使うやり方だと無駄なインスタンスを使うことになってしまうので、通常はString str = "Hello world";のようにします。

文字列の扱いは、文字列用に用意されたStringクラスのメソッドを使うことで便利になります。

// length 文字列の長さを取得
String str1 = "apple";
int strlen = str1.length(); // 5

// substring 文字列の部分文字列を取得
// 1番目の引数:始まりの位置(0から始まる)
// 2番目の引数:終わりの位置(省略すると最後まで)
String str2 = "orange";
String str3 = str2.substring(2, 4); // ang
String str4 = str2.substring(0, 3); // oran
String str5 = str2.substring(3); // nge

// indexOf 指定された文字列が最初に現れる位置を返す(0が起点)
// 存在しない場合は-1が返される
String str6 = "banana";
int index1 = str6.indexOf("nan"); // 2
int index2 = str6.indexOf("non"); // -1

他にも文字列を扱うメソッドはたくさんあるので、詳しくは公式ドキュメント(Java8)を見てください。

後、文字列の比較はequalsというメソッドを用います。

String str7 = "Tokyo";
String str8 = "Tokyo";
String str9 = "Osaka";
boolean check1 = str7.equals(str8); // true
boolean check2 = str7.equals(str9); // false

こんな感じです。イコール(=)で比較するとメモリ領域を比較することになるので、必ずしも期待した結果にならないので気をつけてください。

文字列の結合はプラス(+)を使います。

String str10 = "Super";
String str11 = "Star";
String str12 = str10 + str11; // "SuperStar"

こんな感じです。ただし、何回も繰り返すとパフォーマンスが悪くなるので、このような場合はStringBuilderクラスを使って以下のようにします。

StringBuilder builder = new StringBuilder();
List<String> data = getData(); // getDataは文字列のリストを取得するメソッドとします
int dataSize = data.size();
for (int i = 0; i < dataSize; i++) {
    builder.append(data.get(i));
}
return builder.toString();

こんな感じで、いくつか気をつけるところがあったりしますが、うまく文字列処理ができるようになりたいものです。
文字列処理は正規表現を使うともっと複雑なことができたりしますが、それについては別の機会に紹介します。

令和2年秋 データベーススペシャリスト受験記

10月18日日曜日、4月に行われるはずだったのが延期になっていたデータベーススペシャリストの試験を受けて来た。会場は沖縄県立那覇工業高校。

午前1はこの時のおかげで免除で、午前2から。
自分の受けた教室は受験人数は20人より少し少ないくらいか。

午前2
問題はこちら。いきなり問1にCAP定理が出て、焦る。昨年CAP定理が出たから今年は出ないだろうと思っていたけど、違う切り口で出てきた。これはやはりCAP定理についてちゃんと理解しておく必要があるということか。
問2も新作問題のNoSQLについてだが、参考書で別のノードに時間差で反映されるのがあった気がして、イにマークする。
問3からは過去問通りの問題が出て来て、ペースが上がってくる。
最後の方に電気泳動型電子ペーパーとか知らない問題も出て来たが、終わってみるとまあ大丈夫かなという感触。
解答はこちら。自己採点で18/25。一安心。

午後1
問題はこちら
予定では問1+問2か問3のどちらかなので、まずは問1を解いていく。
問題の意味はまあわかるのだが、45分(90分の半分)でやるにはどうにも量が多い。最後の方は結構慌てて書いたので、リレーションをいくつか書き損ねてしまう。
問1が終わったところで47〜8分くらい経過。問2と問3をざっと見て、問2の方が記述の量が少なそうなので問2に行くが、これが失敗だった。
設問1と設問2の最初はまあわかったのだが、そこから先がさっぱりわからず、これはまずいと思い問2を退散し、問3に。10分くらいタイムロスしてしまったので30分で問3を解かなければならない。
しかし、問3はやってみると3問の中では一番簡単で、販売額がNULLになるケースと一番最後のテーブル構造以外はそんなに悩まず解くことができた。
試験後のツイッターとか5chとかを見た感じだと、やはりみんな問1が時間かかり過ぎという声が多かった。

午後2
問題はこちら
こちらは物理設計が苦手なのでお絵かきの問2と最初から決めており、問1には目もくれず問2にかかる。
こちらは時間的には午前1よりは十分にあるのだが、ルート巡回とかがいまいちイメージしきれず、リレーションの線を引く箇所も結構迷うところが多く、なかなかしんどかった。
特に設問2(2)の6箇所中5箇所が同じ回答になってしまい、本当かよと思いつつ、他に回答が思い浮かばず6箇所中5箇所が同じ回答のまま進める。
どうにか回答を埋めきったけど、全然答えに自信が持てないorz
去年のパン屋の問題がかなり難しかった反動で易化を期待したけど、残念ながらそうはならなかったか。

その他
試験勉強はこの本を主に使いました。
午前2は何回か過去問をやって大丈夫そうだったので、午後にやや重きを置いて勉強しました。
勉強の仕方としては、問題と解答をよく読んで解き方を理解して行くというものでした。ただ、今になって思えばもう少し手を動かせばよかったかなと。

合格発表は12月25日のクリスマス。去年のアーキテクトも含めて高度試験は3回午後2で沈没しているので、そろそろ受かりたいけど、どうなるか。。。

合格発表は12月25日のクリスマス。去年のアーキテクトも含めて高度試験は3回午後2で沈没しているので、そろそろ受かりたいけど、どうなるか。。。

2020/12/25追記

何とか合格しました!!
午前1:免除、午前2:72点、午後1:69点、午後2:62点でした。
午後2は本当ギリギリだったので(配点がわからないので何とも言えないけど)、リレーションをあと数本引き忘れていたら落ちていたと思うと、本当ゾッとする(汗)
勝因としては、最後まで諦めずに粘ったことと、午後1で手が動かなくなった時点で問2を退却して問3に切り替えたことか。
とは言え、現在業務でテーブル設計とかやっているけど、実務の点ではまだまだだと痛感するので、これから勉強を積んで本当の意味でデータベースのプロになれるよう研鑽を積んでいきたい。

次は何を受けるかを考えると、ネットワーク(に限らずインフラ全般)は苦手だし、エンベデッドは業務と結びつかないし、セキュリティ改め情報処理安全確保支援士は資格を維持するのにかなりお金がかかるみたいなので、長文を書くのは苦手だけど、論文系の資格(システムアーキテクトorプロジェクトマネージャ)を狙うしかないなあ。

TypeScript入門 その3

TypeScript入門3回目の今回は、データ型の中でも、前回紹介できなかった列挙型(enum)、タプルや関数オブジェクトあたりについて説明しようと思います。

1. 列挙型(enum)
列挙型(enum)とは、あらかじめ決められた値のみを取ることができるデータ型です。例えば、こんな感じです。

enum Signal {
 Red, Yellow, Blue }; let red = Signal.Red; let yellow = Signal.Yellow; let blue = Signal.Blue; console.log(red); // 0 console.log(yellow); // 1 console.log(blue); // 2 // let green = Signal.Green // コンパイルエラー!

このようにして定義された変数signalは、Red, Yellow, Blueのいずれかしか代入することが出来なくなっています。
また、enumはデフォルトでは内部では0から始まる整数を取るようになっていますが、

enum Signal {
  Red = 2,
  Yellow = 3,
  Blue = 4
};

というように、定義するときに取る値を変更することもできます(数値だけでなく、文字列を入れたりもできます)。
ところで、最近ではenumを使わない方がいいという意見もあるようです。
enumを使わない書き方としては、こんな感じです。

const Signal = { 
  Red: 0,
  Yellow: 1,
  Blue: 2
} as const;
type Signal = typeof Signal[keyof typeof Signal];
let red = Signal.Red;
console.log(red);  // 0

このような有限集合のデータ型をUnion型と言います。
TypeScriptの経験が浅い自分はenumのデメリットを把握しきれてないのですが、enumはJavaScriptとの互換性がないというのは確かにありますね(コンパイル後のソースを見ると一目瞭然)。

2. タプル
次は、複数の型の値をまとめて持つことができるタプルというものを紹介します。
配列のように複数のデータ型を宣言することで定義できます。
例えば、こんな感じです。

let person: [string, number, string];
person = ["Taro", 26, "Tokyo"];
console.log(person[0]); // Taro
console.log(person[1]); // 26
console.log(person[2]); // Tokyo
person[1] = 30; // OK
person[1] = "30"; // コンパイルエラー

とはいえ、この書き方だと何を表しているかわからなくなりがちなので、下記のような使い方の方がわかりやすいかもです。後、先ほどのenumももちろん要素にすることができます。

type name = string;
type age = number;
type address = string;
enum gender {male, female};

let person: [name, age, address, gender];
person = ["Taro", 26, "Tokyo", gender.male];

3. 関数オブジェクト

TypeScriptでも、JavaScriptと同じように関数の定義を行えますが、やはりそこはTypeScript。引数と戻り値に型を指定することができます。

function multiple(a:number, b:number):number {
  return a * b;
}
multiple(5, 3); // 15
multiple(5, "3"); // コンパイルエラー

こんな感じです。
また、TypeScriptでは下記のようにオプション引数を使うことができます。

function multiple(a:number, b:number, c?:number):number {
  if (c) {
    return a * b * c;
  }
  return a * b;
}
multiple(5, 3); // 15
multiple(5, 3, 4); // 60

後は、同じ名前の違う型で定義された関数も定義することができます。いわゆるオーバーロードです。

function add(a:string, b:string):string;
function add(a:number, b:number):number;

function add(a:any, b:any):any {
    if (typeof a === "string" && typeof b === "string") {
        return a + ":" + b;
    }
    return a + b;
}
add("Hello", "World"); // Hello:World
add(7, 4); // 11
add("Hello", 4); // コンパイルエラー

 

今日はこの辺りで。
次回最終回(予定)はクラスとかのオブジェクト指向に関する話題を紹介しようと思います。

TypeScript入門 その2

前回のTypeScript入門その1に続いて、第2回の今回は、主にデータ型などを説明しようと思います。

1. 主要なデータ型
基本的なデータ型は、次の3つです。
・boolean
取ることができる値はtrueかfalseのどちらかだけです。
・number 
数値を値に取ることができます。整数、小数の区別はありません。
・string
文字列を値に持つことができます。

変数を宣言する時は、下記のようにして型とともに宣言します。

let enabled:boolean;
let money:number;
let name:string;
enabled = true;
money = 100;
name = "Ichiro";

money = "ABC";   // これはダメ。コンパイル時にエラーになります。

このように型を宣言すると、その変数には型に当てはまる値以外は入れられなくなります。JavaScriptでは10 == “10”がtrueになったりということもありましたが、TypeScriptではそもそも比較自体ができなくなります。
とはいえ、何らかの理由で型を指定したくない場合も場合によってはあるかと思います。このような場合はlet value:anyという風にany型を使うことでどのような型でも代入できるようになります。JavaScriptで書かれたソースをTypeScriptに書き換える場合なんかでたまに使うこともあるかもです。

2. 変数宣言の指定子
先ほどの変数宣言の例ではletを使って宣言しましたが、varだと関数の中での宣言なら関数内で、それ以外ならグローバルで使えるようになりますが、letは宣言された構文内のみがスコープになります。
varよりはletを使った方が想定外のところで値が書き換わるようなことがなくなるので、varにしなければいけない理由がある場合以外はletを使った方がいいと思います。
ちなみに、letはJavaScriptではES6から使えるようになりました。
また、変数宣言ではvarとletですが、定数宣言ではconstを使います。constで宣言されると、それ以降値の変更が一切できなくなります。
最近letも使わずconstだけを使うべきだという記事も見ましたが、確かに値を変更することがありえない場合はconstを使うべきですが、letでないと実装しづらいケースも多々あると思うので、個人的には「そこまでこだわらなくてもいいのでは」派です。

3. 配列
TypeScriptにも、JavaScriptと同じように配列は存在します。配列の宣言は下記のように行います。

let data:number[];
data = [1, 1, 2];
data.push(3);
console.log(data);   // 1,1,2,3
data.push("xyz");    // だからダメだってば

このように、配列もどんな型を要素に入れられるかの宣言を行います。なので、配列の要素に指定した型でないものを入れようとするとエラーになります。

この他にも、列挙型(enum)やタプルについても説明したかったのですが、次回にします。

TypeScript入門その1

  1. AltJSとは
    Webプログラミングのサーバー側言語はPHP、Java、Python、Ruby、・・・とたくさんありますが、フロントエンド側の言語となると、ほぼJavaScript一択。しかしJavaScriptにも下記のような問題点があったりします。
    ・型付けが自由すぎてそれに由来する想定外の問題が起きやすい(例えば1==”1″がtrueになったり)
    ・オブジェクト指向が独特(プロトタイプベース)
    ・スコープを絞りにくいそこで、最近ではこう行った問題を解決するために、より実装しやすいプログラミング言語(AltJSと言います)で実装してJavaScriptに変換するというのが流行りになりつつあります。
    というわけで、これから何回かに渡って代表的な AltJSの1つであるTypeScriptについて紹介していこうと思います。
  2. TypeScriptの特徴
    TypeScriptは2012年にマイクロソフトによって開発された言語で、下記の特徴を持っています。
    ・JavaScriptのスーパーセットである(JavaScriptの機能はTypeScriptでも使える)
    ・静的型付けの言語である(数値型の変数に文字列を代入したりするとエラーになる)
    ・クラスやインターフェースなど一般的なオブジェクト指向の機能を持っている(クラスについてはJavaScriptでもES6から使えるようになりましたが)
  3. TypeScriptの環境構築
    まず前提として、npmが使える環境であるとします。とりあえず、Windowsならコマンドプロンプトで、MacならiTerm2で”npm -v”と打ってみて、バージョンが出てくればOKです。入っていない人はこちらを参考にしてNode.jsとnpmをして下さい。
    次に、TypeScriptをインストールします。
    npm install -g typescript
    インストールし終わったら、とりあえず下記の内容をコピーして、.tsを拡張子につけたファイル名(例えばhello.ts)で保存して下さい。

    class Hello {
      public name: string;
      constructor(name: string) {
        this.name = name;
      }
    }
    
    let user = new Hello("World");
    console.log("Hello " + user.name);

    そしたら、”tsc hello.ts”で保存したファイルをコンパイルすればhello.jsというJavaScriptのファイルができているはずです。
    後は、HTMLに埋め込んでブラウザで開けば、検証ツールで”Hello World”と出力されることが確認できますし、node hello.jsでターミナル上で出力を確認してもいいでしょう。次回はTypeScriptで使えるデータ型などについて説明しようと思います。

SpringBoot入門その3 Spring Security

# Spring Securityによる認証・認可
次に、セキュリティを保持するためのフレームワークである「Spring Security」を使って、Userクラスのaccountとpasswordによるログイン機能を作ることを考えてみます。
1. pom.xmlの設定

org.springframework.boot
spring-boot-starter-security

2. application.propertiesの設定
今回はBASIC認証は使わないので、下記の設定でBASIC認証を無効にします(もちろん、有効にする場合はtrueにする)。
“`
security.basic.enabled=false
“`
3. Userのエンティティとリポジトリの作成
「Spring Data JPAによるデータベースへのアクセス」で説明したやり方で、ログインユーザーを定義するユーザーのエンティティとリポジトリを作成します。
4. SecurityConfigクラスの作成
詳細は後日記載しますが、org.springframework.security,config.annotation.web.configuration.EnableWebSecurityをインポートして@EnableWebSecurityをアノテーションにつけます。また、org.springframework.security,config.annotation.web.configuration.WebSecurityConfigurerAdapterをインポートしてWebSecurityConfigurerAdapterを拡張したSecurityConfigクラスを作成します。
こうして作成したSecurityConfigクラスにセキュリティ関連の設定を行なっていきます。ログインの永続化やCSRF対策の設定もここで行います。

SpringBoot入門その2Spring Data JPA

(注、この記事は書き途中です。後日更新します。)
SpringBoot入門その1を書いてから、1年以上経ってしまった(大汗)ということで、今回は1年ぶりのSpringBoot入門その2です。
今回は、Spring Data JPAによるデータベースへのアクセスとSpring Securityによる認証・認可について簡単に紹介したいと思います。
# Spring Data JPAによるデータベースへのアクセス
1. そもそもJPAとは何か
一言でいうと、Javaでの標準のO/Rマッパーです。O/Rマッパーは、ObjectとRelationを対応づける、すなわちオブジェクト指向のクラスとデータベースのテーブルを関連させるものと考えておけばいいでしょう。
例として、「顧客」というクラスがあるとします。メンバー変数としてはID(Integer、必須)、名前(String、必須)、性別(Byte、1:男 2:女)、年齢(Integer)を持っているとします。
この時、対応するcustomorテーブルはこんな感じになるでしょう。
| id | name | gender | age |
| —- | —- | —- | —- |
| 1 | 鈴木 | 1 | 28|
| 2 | 佐藤 | 2 | 30|
| 3 | 田中 | 1 | |
| 4 | 山田 | | 26|
Javaの顧客クラスからDBのcustomorテーブルに簡単にアクセスできるようにする仕組みが、JPAです。JPAには、このマッピングの他にもDBへのCRUD(Create,Read,Update,Delete)処理をカプセル化したAPIおよびJavaオブジェクトを間作するためのクエリ言語(JPQL)を持っています。
2. pom.xmlとapplication.propertiesの設定
まずは、pom.xmlに、下記の内容を追加して、「spring-boot-starter-data-jpa」を読み込ませます。

org.springframework.boot
spring-boot-starter-data-jpa

次に、application.propertiesに下記の設定をします。なお、ここではMySQLを使う場合を想定しています。


spring.datasource.url=jdbc:mysql://localhost:3306/database
spring.datasource.username=user
spring.datasource.password=password
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.jpa.database=MYSQL
logging.level.jdbc=OFF
spring.jpa.hibernate.ddl-auto=validate

3. エンティティとリポジトリの作成
次に、エンティティクラスとリポジトリインタフェースを定義します。
エンティティクラスは、いわゆるMVCの”M”にあたるもので、以下のように定義します。


@Entity
@Table(name = "customers")
public class Customer {
@Id
@GeneratedValue
private Integer id;
private String firstName;
private String lastName;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(nullable = true, name = "username")
private User user;
}

リポジトリインタフェースはJavaからデータベースにアクセスするためのインタフェースで、次のようにします。


import com.example.domain.Customer;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

import java.util.List;

public interface CustomerRepository extends JpaRepository<Customer, Integer> {
@Query(“SELECT x FROM Customer x ORDER BY x.firstName, x.lastName”)
List findAllOrderByName();¥
}

最低限必要なのはエンティティクラスと”org.springframework.data.jpa.repository.JpaRepository”のインポートです。これに”org.springframework.data.jpa.repository.Query”を追加でインポートすることでSQL文を作ってそれを呼び出すメソッドの定義もできるようになります。
それ以外だと、Serviceクラスを作ってそこからエンティティへの操作を定義して、それをコントローラーから呼び出すという使い方をすることが多いです。
Serviceクラスの作り方、使い方については「SpringBoot入門その3」あたりで紹介しようと思います(いつになるかわからないけど)。