taiPyのお悩み解決ブログ

日々の発見をまとめます!

Spring Frameworkのテスト環境ではH2データベースを使いたいときにどうするといいか。

はじめに

ソフトウェア開発をする上でテストを実施します。テストは大きく分かれて2種類あり、1つ目が人の手で実行する方法、2つ目が自動テストをする方法とがあります。

自動テストをするときに、本番環境と開発やテスト環境を切り替えたいときがあります。今回の私の例を申し上げれば、今、Spring Frameworkを使い、webアプリを作成しています。本番環境ではデータベースにpostgresを使っていたのですが、この環境だとテストがしにくいことが判明しました。逆に、H2データベースと呼ばれるデータベースがあるんですが、そちらを使ったほうがテストが容易であることが調べていくうちにわかったんですよね。でも本番環境はpostgresを使っていきたい。

じゃあ、どうやって本番環境ではpostgresを使ってテスト環境では平日データベースを使うように切り替えていくのか。

これについて本日は解説して行きたいと思います!

目次

前提事項

解説して行くにあたりまずは前提事項を押さえていきましょう。私が開発に使っていた環境は次のようになります。

  • Windows 11
  • eclipse
  • Spring Framework 3.x
  • gradle
  • テスト用にH2データベースを使用

結論

テスト用に設定を用意する! 具体的に言えば、次の2つのステップ。

①テスト用のapplication.properties

②テスト実行時にH2データベースを使用するようにbuild.gradleに書く!

①テスト用のapplication.properties

次のフォルダ構成になります。画面下部の位置に、テスト用のapplication.propertiesがあるかと思います。こちらを作成。(src/test/resourcesを右クリック → 新規 をクリック → ファイル をクリック)

中身は下記の通り。こちらを記述する。

# =の右にはアプリの名前を記載
spring.application.name=yourAppName

# DataSource
# SQLスクリプトの初期化モードを設定
spring.sql.init.mode=always
# Log表示設定 DEBUG=詳細なログ
logging.level.com.example.webapp.repository=DEBUG

# H2 Databaseの設定
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=yourname
spring.datasource.password=yourpass

# H2 Consoleの有効化
# webブラウザで操作可能になる
spring.h2.console.enabled=true

メリット このファイルを作成することで、テスト実行時にこの設定が読み込まれます。つまり、テスト環境と本番環境を分けることが出来ます!

注意点

H2データベースを使用するためにいろいろ設定しないといけないので、うまくいかなかった人はH2データベースの設定を見直してほしい。

  • そもそも設定しているのか。
  • H2データベースの設定とapplication.propertiesの記載内容があっているのか。

②テスト実行時にH2データベースを使用するようにbuild.gradleに書く

testImplementation 'com.h2database:h2'dependenciesに追加する!下のような見た目になるかと思います。一番下です。

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3'
    compileOnly 'org.projectlombok:lombok'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    runtimeOnly 'org.postgresql:postgresql'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter-test:3.0.3'


    testImplementation 'com.h2database:h2'
}

下記は、ChatGPTさんの解答になります。ご参考までに。

testImplementation 'com.h2database:h2'という記述は、Gradleプロジェクトの build.gradle ファイル内で見られるもので、テストコードの実行時にH2データベースを使用するための依存関係を指定しています。

具体的には、'com.h2database:h2'は、H2データベースの依存関係を示しています。つまり、プロジェクトのテストを実行する際に、H2データベースを使用するために必要なライブラリがプロジェクトに含まれることを意味します。

参考文献

Gradleを使用:Springの入力値検証機能を使いたい。spring-boot-starter-validation

はじめに

自分用の備忘録です。 Springの入力値検証機能を使いたいってなりましたが、自分のプロジェクトに必要なライブラリというか機能が備わっていなかったんですよね。

なので、Gradleで依存関係を整理して使えるようにしました!

環境

  • エクリプス
  • Windows
  • Spring

手順

大きく分けて次の2つ。

  1. Spring Boot Starter Dependencyを追加する: Spring Boot Starter Dependencyには、必要な依存関係が含まれています。build.gradleファイルに次のような依存関係を追加します:
   dependencies {
       implementation 'org.springframework.boot:spring-boot-starter-validation'
       // 他の依存関係も追加する場合はここに記述します
   }

これにより、Spring Bootが提供するバリデーション機能がプロジェクトに追加されます。

  1. 入力値の検証を行うクラスを作成する

今回はFormクラスを使っています。入力値を受け取るときにFormクラスを使う方法とModelクラスを使う方法の2種類があると思いますが、Formクラスを使っています。入力値検証をしたい、バリエーションをしたいってなったら基本Formクラスを作成することになるかと存じ上げますが。

入力値の検証を行うクラスを作成します。通常は、入力値を持つDTO(Data Transfer Object)クラスや、コントローラー内で使用されるフォームクラスが対象となります。以下は例です:

   import javax.validation.constraints.NotBlank;

   public class MyForm {
       @NotBlank(message = "名前を入力してください")
       private String name;

       // getterとsetterを省略
   }

ここでは、@NotBlankアノテーションを使用して名前が空でないことを検証しています。

これらの手順を実行することで、GradleプロジェクトでSpringの入力値検証機能を使用できるようになります。

Thymeleafの`[*{birth}]`と`[${birth}]`の違いについて。

Thymeleafの[*{birth}][${birth}]の違い

  1. [*{birth}]:

    • [*{...}]は、プレースホルダー式(Placeholder Expression)として知られています。
    • プレースホルダー式は、Thymeleafがデータをテンプレートに表示する際に、対応するモデルオブジェクトから属性を動的に解決します。
    • この式は、コンテキスト内のオブジェクトのプロパティを参照するために使用されます。*は現在のオブジェクトを指します。
    • 例えば、[*{birth}]は、現在のオブジェクトのbirthプロパティを取得し、それを表示します。
  2. [${birth}]:

    • ${...}は、変数式(Variable Expression)として知られています。
    • 変数式は、明示的にモデルに追加された属性を参照します。モデル内のオブジェクトに直接アクセスするのではなく、モデル内の属性を参照します。
    • 例えば、[${birth}]は、モデル内でbirthという名前の属性が定義されている場合、その値を表示します。

したがって、[*{birth}]は現在のオブジェクトのプロパティを参照し、[${birth}]はモデル内の属性を参照します。どちらを使用するかは、テンプレート内のデータの出所によって異なります。

Spring Framework 超入門, 4種類のDIのアノテーションを使用タイミングと意味について

はじめに

いま、Spring Framework 超入門という本を読んでいます。アマゾンへのリンクはこちら。Spring Framework 超入門

学んだことの備忘録としてブログにメモしておこうと思います。 今回書くことは、 「DIのアノテーション」 について書きます!

Springの勉強をしていると必ずDIという機能をマスターする必要があります。これがSpingの中心的な機能ですので。その時に、アノテーション(例:@Service)をつけるんですよね。私が学習をしていて結構困ったのがいつどんなアノテーションをつけたらいいのかっていうことに困っていたんですよ。今日はこのDI関連のアノテーションというのが、4種類ぐらいあるんですよね。じゃあそれぞれの種類の意味とかいつつけたらいいのとかそこら辺が結構あいまいでした。

今回、DIのアノテーションについて勉強したので説明していきます。 まずは、DIとアノテーションについての概要を説明してから、4種類それぞれのアノテーションの意味とタイミングを説明する流れで本記事は構成されています。

目次

DIとは?

DIとは英語でDependence Injectionと呼ばれ、日本語訳すると「依存性を注入する」ということです。これをすることでJavaのクラスとクラスを疎結合にすることが出来ます。たとえば、AクラスとBクラスがあったとして、AクラスはBクラスがないと生きていけない(恋愛っぽい)をなくすことが出来るんですよね。クラスとクラスを疎結合にすることが出来る。これがDI(Dependence Injection)のメリットです。

デメリットを上げるとすれば、まあ、慣れるまでは少しめんどくさいってことですね。

アノテーションとは?

アノテーションについての概要は次の通りです。

  • アノテーション=annotation=注釈
  • @hogehogeみたいなやつ
  • 外部のソフトウェアにやってほしいことです

import lombok.Data;

// ここ
@Data
public class Cake {
    private String Name;
    private Integer Number;
}

上の例の場合だと、@Dataを使っています。で、import lombok.Data;が外部のソフトウェアを表していて、lombok.Dataを使いますよー。それをインポートしてきますよーって言っています。これを書くことで、このCakeクラスにgetter, setter, equals(), hashCode(), toString()メソッドを自動で作成してくれます。(lombokが勝手にしてくれます。lombok様様です。)

4種類、DIのアノテーション概要とタイミング

DIとあとはアノテーションについて抑えられたところで、実際にDIのアノテーションについて説明していきます。DDのアノテーションは次の4種類があります。

  • @Component
  • @Controller
  • @Service
  • @Repository

それでそれぞれのアノテーションの意味は次のようになっています。

No アノテーション 意味
1 @Component 特定の役割を持たない。なんでも屋さん。
2 @Controller Webのリスクエストを処理します。例えば、GetやPost等を処理して、このページにデータがPostされたから、この処理していこーみたいな。
3 @Service ビジネスロジック。根幹。本質的なプログラムに付与される
4 @Repository データをやり取りするときに使用される。

とりあえず@Componentつけといて、MVCフレームワーク(Webアプリ開発でよく出てくるやつ)のコントローラーを作成したいときは@Controllerをつけて.

じゃあビジネスロジックとか本当に大切なところ、あんまり変わらないところには@Serviceつけといて、データベースとやり取りするところには@Repositoryをつけておくそんな感覚で大丈夫かと思います。

これらのアノテーションたちをつけておくことでコンピューターに、このクラスはこういう意味で使うよって伝えることができる。裏側でいろいろやってくれているので、便利だし、必要だからつけておきましょう!

参考文献

(編集中)学習記録:Java:C - Airport Code , AtCoder Beginner Contest 349

問題と解説へのリンク

問題 C - Airport Code https://atcoder.jp/contests/abc349/tasks/abc349_c

解説へのリンク

問題のポイントとメモ

  • 正規表現でも求めることが出来る!
  • T の最後の文字が X でない場合, T が S の部分列であるかどうか判定すればよいです.T の最後の文字が X である場合, T の最初の2文字が S の部分列であるかどうか判定すればよいです.

解答例

import java.util.Scanner;

public class Main {
    public static boolean check(String S, String T) {
        int i = 0;
        for (char t : T.toCharArray()) {
            while (i < S.length() && S.charAt(i) != t) {
                i++;
            }
            if (i == S.length()) {
                return false;
            }
            i++;
        }
        return true;
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String S = scanner.nextLine().toUpperCase();
        String T = scanner.nextLine();
        scanner.close();

        System.out.println(check(S, T.charAt(T.length() - 1) != 'X' ? T : T.substring(0, T.length() - 1)) ? "Yes" : "No");
    }
}

正規表現バージョン

ChatGPTに変換してもらったがTLEだった。うん。他のコードを参考にするか?C++など。




    
    
  

学習記録:Java:B - Commencement , AtCoder Beginner Contest 349

問題と公式の解答へのリンク

問題 B - Commencement https://atcoder.jp/contests/abc349/tasks/abc349_b

公式の解答へのリンク https://atcoder.jp/contests/abc349/editorial/9778

解答例 自分の

Javaの解答例(自作)

Point

  • 文字が何文字出てきてますかー
  • 1回の繰り返しの文字の種類は、、2回の繰り返しの文字の種類は、、って100回の繰り返しの文字の種類は?ってforループさせます。
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String s = sc.nextLine();
        int sLength = s.length();

        Map<Character, Integer> map = new HashMap<>();

        for (int i = 0; i < sLength; i++) {
            map.put(s.charAt(i), map.getOrDefault(s.charAt(i), 0) + 1);
        }


        for (int i = 1; i <= 101; i++) {
            int count = 0;
            for (Integer num : map.values()) {
                if (num == i) {
                    count ++;
                }
            }

            if (! (count == 0 || count == 2)) {
                System.out.println("No");
                return;
            }
            
        }

        System.out.println("Yes");

        sc.close();
    }
}

公式の解答(Python)をJavaに変換

公式の解答(Python)をJavaに変換

Point

  • アルファベットは26文字
  • char型の'a'は97、'b'は98というように、char型のアルファベットの持つ数値は連続している。

アルファベットを出力するコード

        char c = 'a';
        for (int i = 0; i < 26; i++) {
            System.out.println(c++);
        }

解答例

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String S = scanner.nextLine();
        int[] cnt = new int[26];
// 文字が出てきたら数えている
// 配列のindex=0が1文字目、つまりaを意味している。
        for (char c : S.toCharArray()) {
            cnt[c - 'a']++;
        }

// 1文字連続、2文字連続・・・100文字連続が何種類あるかを数える
        int[] cnt2 = new int[101];
        for (int c : cnt) {
            if (c > 0) {
                cnt2[c]++;
            }
        }

// 判定
        boolean isAllZeroOrTwo = true;
        for (int c : cnt2) {
            if (c != 0 && c != 2) {
                isAllZeroOrTwo = false;
                break;
            }
        }
        System.out.println(isAllZeroOrTwo ? "Yes" : "No");
    }
}

学習記録:Java:A31 - Divisors , 競技プログラミングの鉄則 演習問題集

はじめに

競技プログラミングの鉄則 演習問題集 の A31 - Divisors を解いたので、その備忘録を!

ポイントとミスったポイント

ポイント

  • 包除原理を使う。高校数学で学ぶ集合的なイメージ。重なり合っている部分は重複して数えちゃうから引こうね!ってこと!
  • 3の倍数が何個あるか?という問題だから、普通に3で割った数が3の倍数の個数だよね。
  • 5の倍数も3の倍数と一緒。

ミスったポイント

  • 数え上げちゃった。計算量がバカでかい。O(3N)
    • 正解は4回計算するだけで行けちゃう。
  • long使っていない。intじゃ、小さすぎて正しく表示できないよ。

目次

問題と公式の解答

A31 - Divisors https://atcoder.jp/contests/tessoku-book/tasks/tessoku_book_ae

公式の解答へのリンク https://github.com/E869120/kyopro-tessoku/blob/main/codes/java/chap05/answer_A31.java

解答例

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        long n = sc.nextLong();

        // 数える用の変数を用意するよー
        // 正直用意しなくてもいいよ。公式見てみて。すっきりしている。
        long threeCnt = 0, fiveCnt = 0, fifteenCnt = 0;

        threeCnt = n / 3;
        fiveCnt = n / 5;
        fifteenCnt = n / (5 * 3);


        System.out.println(threeCnt + fiveCnt - fifteenCnt);
        sc.close();
    }
}

誤った回答例(TLE)

時間超過しちゃうよー。計算量がバカでかいからねー。

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        long n = sc.nextLong();

        // 数える用の変数を用意するよー
        int threeCnt = 0, fiveCnt = 0, fifteenCnt = 0;

        for (int i = 1; i <= n; i++) {
            if (i % 3 == 0) {
                threeCnt ++;
            }

            if (i % 5 == 0) {
                fiveCnt ++;
            }

            if (i % (3 * 5) == 0) {
                fifteenCnt ++;
            }
        }

        System.out.println(threeCnt + fiveCnt - fifteenCnt);
        sc.close();
    }
}