AWS SAA合格した

2022/06/25にAWS SAA合格したのでメモ。
AWS CLFに引き続き、こちらもそこまで勉強法がうまくいったとは思えなかった。
正解の勉強法がわからない・・・

学習期間

4週間(1h/日 ※日曜は3時間)
計32hくらい
AWS経験はS3とIAMを個人開発で触り+(上記学習時間とは別で)連続受験のため3週間CLFの勉強済み

試験得点

774点

使用した教材

www.sbcr.jp まずはこの参考書で全体の流れを把握するのに役立った。章末の問題も知識の確認として役立った。
www2.ric.co.jp 本試験の内容に近い問題集。CLFのときはUdemyの模擬問題をやったけど、全然本試験の問題とは違った。あと自分はやっぱり紙派だなと思った。

勉強方法

1週間目
まずは参考書を一周。週末は参考書2週目読んで復習。
2週間目
参考書の模擬試験を毎日解いた。
3週間目
問題集を買って一周した。通勤電車の行き帰りで参考書をもくもく読んでた。
4週間目
自宅で問題集の模擬試験を毎日解いた。通勤電車では問題集の各章を読み返して繰り返し解いた。

試験を受けての感想

問題を解いていて、CLFのときよりわからない問題が多いなという印象だった。
とりあえず合格できたけど、もう一度受けたら受かるかあまり自信がない。
いったん資格試験はここで一区切りにして、もっと業務よりの勉強をしばらく進める。

AWS CLF合格した

2022/05/29にAWS CLF合格したのでメモ。
ただし、勉強法があまりうまくいったとは思っていない。

学習期間

3週間(1h/日 ※日曜は3時間)
計24hくらい
AWS経験はS3とIAMを個人開発で少し触ったくらい
ネットワークの知識はネスペ資格の机上勉強で少し(落ちたけど)

試験得点

800点

使用した教材

www.amazon.co.jp

たぶんこれだけ繰り返し読んでおけばだいたいOK。
問題がもっと解きたいと思ったら下記も一緒にやると尚更よし。

www.udemy.com

勉強方法

1週間目
まずは本を1周読んだ。読んでいく中で、参考書の問題が各章末に数問しかないのに不安を覚えた。週末は参考書2週目読んで復習した。
2週間目
もっと問題を解いておかないと安心できないと思ってUdemyで問題集買った。平日は模擬試験の基本レベル1と2を100%になるまで繰り返した。週末は参考書3週目読んで復習した。
3週間目
Udemyの応用レベル1と2を100%になるまで繰り返した。

試験を受けての感想

思ったより試験はできなかったと感じた。1時間くらいで全問解き終わったけど、なんだか回答にあまり自信を持てなかった。 原因は、試験問題に近い文章の模擬試験をやらなかったから。Udemyの問題はあくまで暗記用、くらいの文章しかなく、簡潔すぎるので 実試験の問題文章には全然近くなかった。とはいえ暗記には大変役に立ったので、Udemyの問題やっとくこと自体はよかった。 あとは、AWS CLFのレベルくらいであれば参考書を繰り返し読むだけでも合格点は取れると感じた。

ネスペの勉強メモ

2022/4/17にネスペ受けてきたので勉強メモ。
結果は現在時点でまだだけど、午後1で詰んで落ちたと思ってる。
2022/6/27追記:やっぱり落ちてた。午後1が43点。来年また頑張る。
学習方針はそこまで間違っていなかったと思うので、次回の受験のために書いとく。

学習期間と学習時間

学習期間:2021年12月~2022年4月(約4.5カ月)
学習時間:約200h ※平日:1.5h(週4日)、日・祝:5h(土曜は0h)

使用した参考書・Webサイト

全体の理解

マスタリングTCP/IP 入門編(第6版) | Ohmsha
ネスペの基礎力 −プラス20点の午後対策:書籍案内|技術評論社

午前対策

ネットワークスペシャリスト過去問道場|ネットワークスペシャリスト.com

午後対策

ネスペ30 知 -ネットワークスペシャリストの最も詳しい過去問解説:書籍案内|技術評論社
ネスペR1 - 本物のネットワークスペシャリストになるための最も詳しい過去問解説:書籍案内|技術評論社
ネスペR3 - 本物のネットワークスペシャリストになるための最も詳しい過去問解説:書籍案内|技術評論社

学習方法

12月:マスタリングTCP/IP
とりあえず試験対策と思わず、全体ざっくり理解しとこうと思って1カ月かけてゆっくり読んだ。

1月:ネスペの基礎力、ネスペ道場
ネスペの基礎力を1カ月かけてゆっくり読んだ。
午前対策は暗記のみ。ネスペ道場で過去8年分(計200問)を覚えるまで刷り込んだ。
解説は全然見てない。

2月:ネスペシリーズ、ネスペ道場
午前対策はネスペ道場で引き続き、平日毎日200問といて暗記。
午後対策はネスペシリーズで日曜に時間をかけて新しい問題を解いた。
平日は解いた問題の復習として繰り返し同じ問題を解いた。

3-4月:ネスペシリーズ、ネスペ道場
午前対策はネスペ道場で引き続き、平日毎日200問といて暗記。
午後対策は引き続き平日は解いた問題の復習(一週間で過去問4年分解くペース)
休日は内容を理解していない問題を再度時間かけて解くのと、ネスペの基礎力の読み直しした。

試験を受けて

午前1は免除、午前2は暗記で簡単に解けた。
午後1で詰んだ。90分で2問解くのだけど、1問目が終わった時点で60分経過。
焦って2問目も適当に埋めたけど、1問目も自信なく頭が真っ白状態で終了・・・
午後2はやけに簡単に感じた。午後1を難しかった分、簡単にした?
個人的にはそう思えた。

反省点

  • 試験と同じ時間を測って問題を解く勉強すべきだった
    基本情報も応用情報も、時間測って勉強しなくても合格できたので油断してた。
    ちゃんと時間測って配分考えて解けるようにしておくべきだった。

  • 実機を用いての学習をすべきだった
    机上での理解のみで、勉強してるときに一度も実機で動きを確認したりしなかった。
    次回受けるときは、実機を使って感覚をつかみながら学習していく。

おわりに

普段はアプリ開発が仕事で、ネットワークの知識は全然浅いものしか持っていない。 そんな中でネスペの勉強をすることは、実務においても生かせる勉強になった。 アプリ開発はネットワークの中ではごく一部であって、障害発生した際に、アプリ外が原因になると インフラ側にお任せしないと解決できなかったり。 ネットワークの知識を身に着けておくと、障害の切り分けももう少し深いところで判断できるようになった。(と思いたい) 資格としても魅力的だし、実務にも役立つ勉強なので、次回もめげずに受験したい。実際に実機を使ったネットワーク構築もやってみたい。

Spring DATA JPA 更新後の値が取得できなくてはまった。

やりたいこと

DBにinsert→insertしたデータを他のアプリがupdate→update後の値を取得したい

結論から言うと、「EntityManagerのキャッシュクリア」で解決。

はまった内容

insertしたデータを他のアプリがupdateするが、update後の値が取得できない。
下のようにupdate後の値が取得できるまでwhileループで何度も検索して無限ループ!!!

MyController.java

public class MyController{
    @Autowired
    MyService service;

    @PostMapping(value = "/insert")
    public String insert(){
        Item item = new Item("test");
        // insert
        service.saveItem(item); 
        // 他のアプリが更新するまでselect繰り返す
        searchUpdatedItem(item.getId);
        
        return "index";
    }

    private void  searchUpdatedItem(Item item){
        Item result;
        do{
            result = service.findItem(item);
        }while(result.isUpdate == false);
    }
}

MyService.java

    @Autowired
    ItemRepository repository;
    
    @Transactional
    public void saveItem(Item item){
        repository.save(item);
    }

    @Transactional(readonly = true)
    public Item findItem(int itemId){
        return repository.getOne(Item itemId);
    }

結論

Entity Managerが自身で更新したデータをキャッシュに保存、他のアプリが更新したデータは管理外のため取得してなかったのが原因でした。whileの中でいくらselectしてもDBじゃなくキャッシュ内の古い情報しか参照してなかったと。

修正したこと

自分の場合は一時的にキャッシュクリアして対応。すべてのエンティティ/エンティティごとにキャッシュの使用有無も設定できるもよう。(参考文献参照ください)

MyService.java

    @Autowired
    ItemRepository repository;
    @Autowired
    EntityManager manager; // これ追加

    
    @Transactional
    public void saveItem(Item item){
        repository.save(item);
    }

    @Transactional(readonly = true)
    public Item findItem(int itemId){
        manager.clear(); // これ追加
        return repository.getOne(Item itemId);
    }

おわりに

Spring Boot使ってるとめっちゃ簡単に実装できるけど、だんだんやりたいことが深くなってくると、フレームワークの中身わかってなくて嵌まることが最近増えた。フレームワークが「気を遣ってこういう処理してくれる」みたいなナレッジもちゃんと勉強して増やしていかないといけないと思いました。

参考文献

メモリを逼迫させずにJPAで大量データを取得する方法 - エンタープライズギークス (Enterprise Geeks)
Java - Spring Data JPAでのキャッシュについて|teratail

Winキー+Rを活用する

好きなアプリやメモをショートカット作って簡単に呼び出す方法。
まんま下記の記事のままだけどすごく便利なのでメモ。
Winキー+R をもっと活用しよう!*平凡かつフツーな日記

こんな感じ

Windowsキー+Rキーで「ファイル名を指定して実行」
ショートカット名に「ec」でEnter(OK)押すとeclipseが立ち上がったり。
f:id:n-yata:20210430152941p:plain
f:id:n-yata:20210430153133p:plain

ショートカットフォルダを作る

好きなところに適当にショートカット詰め込むフォルダを作る。
f:id:n-yata:20210430155411p:plain

フォルダのパスを通す

環境変数の設定からフォルダのパスを通す
f:id:n-yata:20210430153511p:plain
f:id:n-yata:20210430153634p:plain
f:id:n-yata:20210430155213p:plain f:id:n-yata:20210430154046p:plain

ショートカットを詰め込む

パスを通したフォルダの中に好きにショートカットを詰め込む。
自分の場合は、よく使うアプリやよく見るテキストメモ、作業用フォルダなんかを詰め込んでる。

ショートカット名を自分の好きなように編集すればその名前で呼び出せる。
f:id:n-yata:20210430154704p:plain

できなかったこと

一部のアプリは起動してくれないもよう。
自分では1つしか見つけられなかったけど、WinSCPは起動しなかった。

Spring DATA JPAで一括インサート

使用環境

GitHub - n-yata/batch-insert-sample

pom.xml

spring-boot-starter-data-jpaを入れる

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
</dependencies>

application.properties

reWriteBatchedInserts=trueをURLのパラメータに入れる(PostgreSQLの場合)。spring.jpa.properties.hibernate.order_insertsspring.jpa.properties.hibernate.jdbc.batch_sizeを設定。batch_sizeは20~100が推奨値みたい。

# DB接続設定
spring.datasource.url=jdbc:postgresql://localhost:5432/postgres?reWriteBatchedInserts=true
spring.datasource.username=postgres
spring.datasource.password=postgres
spring.datasource.driver-class-name=org.postgresql.Driver
spring.jpa.hibernate.ddl-auto=none
spring.jpa.show-sql=true

#thymeleafの設定
spring.thymeleaf.cache=false

# バッチインサートの設定
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.jdbc.batch_size=100

使用テーブルのCREATE, ALTER

テーブルを作成。シーケンステーブルの増分値を100に変更

DROP TABLE IF EXISTS todoitems CASCADE;

CREATE TABLE IF NOT EXISTS todoitems(
  id SERIAL NOT NULL,
  title VARCHAR(255),
  done BOOLEAN,
  tododate TIMESTAMP NOT NULL,
  user_id VARCHAR(255) NOT NULL,
  PRIMARY KEY (id)
);

ALTER SEQUENCE todoitems_id_seq INCREMENT BY 100;

Modelクラス

@SequenceGeneratorにallocationSize = 100を指定する。

package com.example.model;

import java.io.Serializable;
import java.time.LocalDateTime;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;

@Entity
@Table(name="todoitems")
public class Todoitem implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @SequenceGenerator(name = "todoitems_id_seq", sequenceName = "todoitems_id_seq", allocationSize = 100)
    @GeneratedValue(strategy=GenerationType.IDENTITY, generator = "todoitems_id_seq")
    private Integer id;

    private Boolean done;

    @Column(length=255)
    private String title;

    @Column(nullable=false)
    private LocalDateTime tododate;

    @Column(name="user_id", nullable=false, length=255)
    private String userId;

// Getter, Setterは省略

    @Override
    public String toString() {
        return "Todoitem [id=" + id + ", done=" + done + ", title=" + title + ", tododate=" + tododate + ", userId="
                + userId + "]";
    }

}

Repositoryクラス

package com.example.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.example.model.Todoitem;

public interface TodoitemRepository extends JpaRepository<Todoitem, Integer>{

}

Controllerクラス

repository.saveAll(List<Entity>)で一括登録する

package com.example.controller;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import com.example.model.Todoitem;
import com.example.repository.TodoitemRepository;

@Controller
public class HomeController {
    @Autowired
    TodoitemRepository repository;

    @GetMapping(value = "/")
    public String get() {
        return "index";
    }

    /**
     * サンプルを5件インサート
     */
    @PostMapping(value = "/insert")
    @Transactional(readOnly = false)
    public String insert() {
        List<Todoitem> items = new ArrayList<>();
        Todoitem item;
        for(int i  = 0; i < 5; i++) {
            item = new Todoitem();
            item.setTitle("テスト" + i);
            item.setDone(false);
            item.setTododate(LocalDateTime.now());
            item.setUserId("test_user");
            items.add(item);
        }
        repository.saveAll(items);
        return "index";
    }

    /**
     * アイテム一覧を表示
     * @param model
     * @return
     */
    @GetMapping(value = "/find")
    public String find(Model model) {
        List<Todoitem> items = repository.findAll();
        List<String> lines = new ArrayList<>();

        for(Todoitem item : items) {
            lines.add(item.toString());
        }

        model.addAttribute("items", lines);
        return "index";
    }
}

View(index.html)

thymeleafを使用。5件インサートと全件画面に表示ボタンをつけとく。

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:th="https://www.thymeleaf.org"
    xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
    <form method="get" action="/find">
        <input type="submit" value="findAll">
    </form>
    <form method="post" action="/insert">
        <input type="submit" value="insert to 5 items">
    </form>
    <table>
        <tr th:each="line:${items}">
            <td th:text=${line}></td>
        </tr>
    </table>
</body>
</html>

PostgreSQLのログを出力するようにする

とりあえず一括インサートのサンプルは上記で完成だけど、 eclipse上のログだと1件ずつインサートしているように見える。 アプリからDBに飛んでから変換してる?(reWriteBatchedInserts=trueで)ようなのでPostgreSQL側のログを確認して一括インサートになってるか見てみる。

postgresql.confを編集
デフォルトの状態でインストールしてればたぶんここらへんにある。
C:\Program Files\PostgreSQL\13\data\postgresql.conf

コメントアウトとパラメータをallに変更してSQLが出力されるようにする

# log_statement = 'none'          # none, ddl, mod, all
↓
log_statement = 'all'          # none, ddl, mod, all

PostgreSQLを再起動して編集内容を反映

$ pg_ctl -D "C:\Program Files\PostgreSQL\13\data" restart  

または
f:id:n-yata:20210430095054p:plain

f:id:n-yata:20210430095158p:plain

アプリ起動してインサートすると、logファイルにSQLが書き込まれる。 ログファイルの場所はデフォルトだと下のようなパス。
C:\Program Files\PostgreSQL\13\data\log

一括インサートできていることが確認できる。

2021-04-29 21:15:28.494 JST [49252] LOG:  実行 <unnamed>: insert into todoitems (done, title, tododate, user_id, id) values ($1, $2, $3, $4, $5),($6, $7, $8, $9, $10),($11, $12, $13, $14, $15),($16, $17, $18, $19, $20)

参考文献

Java - spring-data-jpaでbulk insertするにはentity manager を使うしかないのでしょうか。|teratail

Eclipse+JPAでDBからEntityクラスを自動生成

使用環境

初期設定

Spring Bootプロジェクトを作成

f:id:n-yata:20210429153424p:plain f:id:n-yata:20210429153508p:plain f:id:n-yata:20210429153533p:plain

プロジェクトの設定を変更

f:id:n-yata:20210429154819p:plain f:id:n-yata:20210429153746p:plain
「より詳しい構成」を選択
f:id:n-yata:20210429153917p:plain
「接続の追加」を選択
f:id:n-yata:20210429153954p:plain
「接続のテスト」を押してping正常を確認、「完了」
f:id:n-yata:20210429154042p:plain
f:id:n-yata:20210429154330p:plain

設定変更する場合

プロジェクトの設定から「JPA」タブで編集できる
f:id:n-yata:20210429154511p:plain

DBの接続先の設定変更など

ウィンドウの設定からパースぺクティブを追加しておくと便利。
f:id:n-yata:20210429154936p:plain

使ってみる

テーブルからエンティティを生成 f:id:n-yata:20210429155206p:plain f:id:n-yata:20210429155251p:plain
JOINするテーブルとかあればここで設定するっぽい
f:id:n-yata:20210429155308p:plain f:id:n-yata:20210429155415p:plain
「完了」押すと生成される
f:id:n-yata:20210429155430p:plain
こんな感じ
f:id:n-yata:20210429155529p:plain

できなかったこと

persistence.xmlに生成時の設定を書ける?ようだけど。
とりあえず単純に生成するだけだと下記手直し必要だった。

テーブルのスキーマ指定

publicスキーマなら問題ないけど、他のスキーマのテーブルを参照する場合、@Table(name="mutter", schema = "spring")とかスキーマ名を手直し必要

プリミティブ型→ラッパークラス

JPAリポジトリからfindした結果をエンティティに格納する際、double型だとnull格納できずエラーに。自動生成されたエンティティにdoubleがあった場合、null格納の可能性ある際はDoubleクラスに手直しする必要あった。

参考文献

Hibernate Toolsを使ってJPAのEntityクラスを生成する - Qiita