Springやってみた①

Springやってみた。
講座はこちら。短いし、シンプルでとっかかりにはすごくよかった。

www.udemy.com

構成は、
こんな感じのフォームに商品番号を入れると、商品名が返ってくるWebアプリ。
↓↓↓↓↓↓

作ったソースの一部。

コントローラー

package com.example.demo;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class ProductController {
    private final ProductService productService;

    //ProductServiceのDI。コンストラクタでDIする
    public ProductController(ProductService productService) {
        this.productService = productService;
    }

    @RequestMapping("/input")
    public String index(ProductForm productForm) {
        return "index.html";
    }

    @RequestMapping("/output")
    public String result(@Validated ProductForm shainForm, BindingResult bindingResult, Model model) {

        if (bindingResult.hasErrors()) {
            return "index.html";
        }

        model.addAttribute("number", shainForm.getNumber());

        //サービス層から商品名を取得してくる
        String name = productService.findByNo(shainForm.getNumber());
        model.addAttribute("name", name);
        return "output.html";
    }
}


サービス

package com.example.demo;

import org.springframework.stereotype.Service;

@Service //Bean化
public class ProductServiceImpl implements ProductService {

    private final ProductRepository productRepository;

    //RepositoryのDI
    public ProductServiceImpl(ProductRepository productRepository) {
        this.productRepository = productRepository;
    }

    @Override
    public String findByNo(String number) {
        //リポジトリから商品を選択
        String name = productRepository.selectByNo(number);
        return name;
    }

}


リポジトリ

package com.example.demo;

import org.springframework.stereotype.Repository;

@Repository //Bean化
public class ProductRepositoryImpl implements ProductRepository {

    @Override
    public String selectByNo(String number) {
        String name;
        // 実際はここがselect結果とかになる。
        // java女子部で聞いたパターンマッチング使ってみた
        switch (number) {
        case "100" -> name = "ルマンド";
        case "101" -> name = "ビスコ";
        default -> name = "登録されていません";
        }
        return name;
    }

}


クラスにアノテーションをつけてBean化すると、何が嬉しいかというと、
こんなことができる、ということらしい。

package com.example.demo;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
public class ProductServiceImplTest {

    @MockBean
    // モック化
    private ProductRepository productRepository;

    @Test
    public void findByNoTest() throws Exception {
        // productRepositoryはmock化しているので、好きな戻り値を設定できる
        when(productRepository.selectByNo("100")).thenReturn("ビスコ");
        ProductService productService = new ProductServiceImpl(productRepository);
        // サービスに100を渡すと、本来はリポジトリからテーブルにアクセスしてselectしてくる作りだが、
        // ↑でモック化して100を渡すとビスコを返す!と定義しているので、その通りビスコを返してくれる。
        // つまりリポジトリが未完成でも、呼び出し元のほうで挙動を仮定して、テストができる。
        String employee = productService.findByNo("100");
        // 結果、OKになる
        assertEquals(employee, "ビスコ");
    }

}


ソースコード全量はこちら。
GitHub - ta202301/TrySpringBoot: Introduction to Spring boot


感想

雰囲気は理解した気がする。
単純に思ったのが、newがないとそれだけでなんか楽。毎回newするのめんどいし、処理の本質には関係ないところなのでなるべく書きたくない。
モック化に関しては、Springじゃなくてもモック自体はできるから、
DIの本領はまだ他のところにありそうだなーと思った。
「依存性の注入」って言葉がいかついけど、触ってみるとなるほどね、って感じだった。

ところで、毎回newしないということは、マルチスレッドとかでバグるやつなのでは?と思ったら、やっぱりそうらしい。

qiita.com

上記で作ってみたリポジトリとかは、呼び出されるたびに生成されるようにしないといけない、ということなのかな。
ID=100でアクセスしたはずが、ID=101の結果が返ってきちゃう、ということになるとまずいよね。

え、シングルトンってじゃあ、どこで使うの・・?と思ったら、既にまとめてくれてる人がいた。

qiita.com

なるほど。実際に設計してみないとぴんとこなそうだなあ。
大きな業務アプリの設計って、やる機会なくて、鍛えるのむずかしい。。

AWS DVAとりました。

もう2か月も経ってしまったが、12月に無事合格してました。一発合格です。うれしい。

SAAをとっていて、素養はあったので、そんなに苦労はしなかった。
とはいえ、SAAよりも開発よりの細かいことを聞かれるので、その辺は大変だった。
(buildspec.ymlはどこに置きますか?とか・・)

勉強に使ったのはこの2つ。

www.udemy.com

www.udemy.com

上が講座で、下が模擬試験。
はい、英語です。日本語のは、C02に対応してるいい講座が見つからなかったので頑張った。
Udemyの字幕と、あとはGoogle翻訳に頼りながら。

SAAとったときより、サーバーレスという概念についての理解が深まった気がする。
SQSとLambdaとDynamoDBがあれば、なんでもできるのでは?という気持ちになったし笑、
勉強しながら業務に戻ったとき、AWSだったらこの調整いらないよな・・なんてことを思ったり。

SAAとったときもなんだけど、AWSせっかく勉強しても仕事で使わないのが悲しい。どうにか使う機会作っていきたいなー。

Java女子部行ってきた

2/3(土)開催のJava女子部に行ってきたので、印象に残ったことのメモなどつらつらと。

Javaのサポート期間。本家のやつは、各バージョンごとに基本3年しかないらしい。
3年ごとにバージョンアップできる業務システムって・・・難しいよね。
だからJava8がいまだに現役なんだなあと思った。

今の現場もJava8なので、今日のセッションで出てきた文法はラムダとStream以外は使えなかった。
switch文のパターンマッチングとか、革命的では??
ラムダ式はまじで使いこなせるようになろうと思った。ちょうど最近作ったツール、ラムダ式で大幅に書き換えられそう。
さすがにもうリリースまで日がないのでリファクタは断念したけど、次回こそは。

あと、JJUG、参加したいなー。前回はちょうど仕事の山と重なって、とても余裕がなかった。
最近やっと落ち着いてきてジムとかも行けるようになってきてるし、自己研鑽やっていきたい。

Java女子部行ってきた

2/3(土)開催のJava女子部に行ってきたので、印象に残ったことのメモなどつらつらと。

Javaのサポート期間。本家のやつは、各バージョンごとに基本3年しかないらしい。
3年ごとにバージョンアップできる業務システムって・・・難しいよね。
だからJava8がいまだに現役なんだなあと思った。

今の現場もJava8なので、今日のセッションで出てきた文法はラムダとStream以外は使えなかった。
switch文のパターンマッチングとか、革命的では??
ラムダ式はまじで使いこなせるようになろうと思った。ちょうど最近作ったツール、ラムダ式で大幅に書き換えられそう。
さすがにもうリリースまで日がないのでリファクタは断念したけど、次回こそは。

あと、JJUG、参加したいなー。前回はちょうど仕事の山と重なって、とても余裕がなかった。
最近やっと落ち着いてきてジムとかも行けるようになってきてるし、自己研鑽やっていきたい。

SQLPlusのスクリプトに渡した引数を更に内部で渡したいとき

ハマったのでメモ。

こんな3つのスクリプトがあるとする。

--first_script.sql
START second_script.sql "TODAY IS" '&1'
START third_script.sql '&1' "IS TODAY"
--second_script.sql
echo '&1' '&2'
--therd_script.sql
echo '&1' '&2'

で、 ↓のようにコマンドを叩いたとき、

START first_script.sql "20240120"

こういう結果を期待していた。

--second_script.sqlの結果
TODAY IS 20240120
--&1がTODAY ISで、&2が親スクリプトに渡した引数
--third_script.sqlの結果
20240120 IS TODAY
--&1が親スクリプトに渡した引数で、&2がIS TODAY

が、やってみるとこうなってしまった。

--second_script.sqlの結果
TODAY IS 20240120
--second_script.sqlの結果
TODAY IS IS TODAY

なぜこうなってしまうのかというと、
second_script.sqlを呼び出したときに、&1が、
second_script.sqlの&1であるTODAY ISに上書きされてしまうらしい。
スクリプトを呼び出した時の&1は引き継がれない。

これを回避するには、親スクリプトを呼び出したときの&1を共通変数にして、
secondとthirdではそれを参照するようにすればよい。

--first_script.sql
DEFINE my_variable = '&1'
START second_script.sql "TODAY IS"
START third_script.sql "IS TODAY"
--second_script.sql
SET SERVEROUTPUT ON
echo '&1' '&my_variable'
--therd_script.sql
SET SERVEROUTPUT ON
echo '&my_variable' '&1'

SQLPlusのスクリプトで生成するファイル名に引数で渡した値を使いたい

ハマったのでメモ。

--create_your_table.sql
-- ファイル名を引数で指定してスクリプトを実行する例
SET SERVEROUTPUT ON
SPOOL your_table_&1..csv

SELECT * FROM your_table;

SPOOL OFF
EXIT;


こうすると、例えば、

START create_your_table.sql "20240120"

で、your_table_20240120.csvが生成される。
引数の前後に.を使いたい場合、..と2個打たないといけないのがみそだった。
ちなみに、ググるよりもchatGPTに聞いた方がはやかったです。chatGPTすごい。

AWS Secrets Manager使ってみた

やってみたのでメモ。

こんな感じで、パスワードを暗号化して保存できるサービス。
DBの認証情報、xmlファイルにべた書きしてるやつよく見るけど、こうすればクラウド上で暗号化して保存できるのか。
いいな。

Lambda関数で自動ローテーションもやってくれる。
アプリ側は決まったget関数でgetするだけなので、認証情報が変わっても影響はない。

すごいな~。今度使ってみたい。