ストレージ知識確認チェックリスト(随時更新)【クラウド活用の教科書を読んでみた】

はじめに

前回に続き「クラウド活用の教科書」の知識チェックリストに記載されていたワードについて 調べてまとめました。今回はストレージの記事となります。 参考

ストレージチェックリスト

ブロックストレージ

ブロックストレージでは、データをブロックに切り分けて、別々のピースとして保存する。ブロック単位でのデータアクセス※を行うため、DBMSにおける データファイル格納先として利用されるのが一般的。iSCSI、FCといったストレージネットワークを用いてストレージクライアントに割り当てられる。 ブロックストレージは、データ経路のオーバーヘッドが少ないため、大容量のデータを高速に転送できる。
※ブロックアクセス・・・固定長のブロック単位でデータを読み書きする手法

f:id:xxyoko10:20210120205739p:plain

SAN(Storage Area Network)

ストレージ装置やテープ・ライブラリ装置をサーバと接続させる、LAN(Local Area Network)とは別の専用のネットワークのこと。 SANでは「ファイバ・チャネル」という、TCP/IPとはまったく異なるプロトコルが用いられる。 光ファイバ・ケーブルを用いて各デバイス(サーバ、ストレージ/テープ装置)同士を接続する。 中継装置として「ファイバ・チャネル・スイッチ」と呼ばれるものが利用される。 ファイバ・チャネル・スイッチを利用することで、複数のサーバとSAN対応ディスク・システムを接続して、その共有が行える。 また、LANとは別のストレージ専用のネットワークとなるため、サーバが抱える大容量データのバックアップなどが発生しても、LANへの負荷がほとんど発生しないというメリットがある。

NAS(Network Attached Storage)

LAN(イーサネットFDDIなど)に直接接続されるストレージである。 ほとんどのNASは、RAID構成のハードディスクを内蔵し、NAS専用OS(NAS OS)によってネットワークを経由したファイル共有を可能にしている。 クライアントとの通信インターフェイスはネットワークのみであり、設定なども通常はネットワークを介して行うことになる。クライアントからは、NFS(Network File System)やCIFS(Common Internet File System:Windowsネットワークでネットワーク上のリモート・ファイル・システムにアクセスするためのプロトコル)などのファイル転送プロトコルを使って、ファイルの読み書きが可能である。このようにファイル転送プロトコルを用いるため、異機種間のファイル交換や共有も可能となっている。

ミラーリング、RAID10、RAID5、RAID6

RAIDとは複数のハードディスクを組み合わせて、信頼性の高いディスク装置を実現しようとする方式のこと。 RAIDには複数の種類がある。ミラーリングを実現するのがRAID1である。ミラーリングとは全く同じデータを複数のディスクに書き込むこと。 あるディスクが故障しても、処理を継続できるため可用性の高いシステムを構築することができる。 RAID5はデータを複数のハードディスクに分散して格納する方式。さらにパリティデータもあわせて書き込みを行うことで、耐障害性を高めた構成となっている。 エラーを修復するためのパリティデータをデータ・ブロックの組ごとに生成し、分散して記録する。たとえ1台のディスクが壊れても、 残りのディスクに格納されたデータとパリティから、失われたデータを復活させることができる。 RAID6はデータを複数のハードディスクに分散して格納する。さらにパリティデータを二重で書き込みを行うことで、耐障害性を大幅に高めた構成となっている。

コントローラー

データを入出力するための各種インターフェース、入出力を制御するプロセッサーとメモリ、および専用のソフトウェアで構成される 「データ入出力専用に特化したコンピューター」のこと。

キャッシュ

ストレージが持っている機能で、一時的なデータが保存される。読み込みにおいて、ストレージにキャッシュが存在すれば実際にディスクまでアクセスしなくて済むので、処理が高速になる。キャッシュの管理はLRUアルゴリズムで行われる。(使用頻度の高いデータがキャッシュに残るようにするアルゴリズム)またストレージの仮想化/階層化も一般的になってきており、使用頻度の高いデータは高速なストレージに、使用頻度の低いデータは低速なストレージに移すこともできる。

同期コピー・非同期コピー

同期コピーは、ほぼリアルタイムでコピーし続けることになる。万一のデータ喪失のリスクを最小に抑えられるが、ネットワークの帯域消費も大きくなる。非同期は、ある程度の時間差を許容しつつコピーしていくもの。同期コピーの場合、データがもっとも頻繁に変更される時点でのデータ転送量に合わせて帯域を確保する必要がある。

マウント・アタッチ・デタッチ

マウントは別々のディスクに格納されているファイルシステムをくっつけることができる。ファイルシステムを分けておくことで、領域不測の際の影響を小さくすることができる。 f:id:xxyoko10:20210207232624p:plain アタッチはストレージ装置や記憶領域など、ハードウェア資源を特定の仮想化環境に追加して利用できるようにすること。 デタッチはハードウェア資源を切り離して無効にする操作や動作のこと。

シンプロビジョニング

ストレージの仮想化で用いられる機能で、OSに対してストレージ装置に搭載している実際の容量以上のサイズに見せることができる。 OSには搭載サイズ以上のディスク容量を認識させつつ、ストレージ側で必要に応じて徐々にディスクを追加していくことができるようになる。 データ容量の将来予測が難しい場合の手段の一つと言える。

差分バックアップ

差分バックアップは、最後のフルバックアップ以降に変更されたファイルのみをバックアップすること。 フルバックアップに比べて時間短縮が可能。

https://active.nikkeibp.co.jp/atclact/active/14/100700086/111700009/?ST=act-server&P=2 https://www.atmarkit.co.jp/fsys/keyword/007san_nas/007san_nas.html#:~:text=NAS%E3%81%A8SAN%E3%81%AE%E6%8E%A5%E7%B6%9A,%E3%83%90%E3%83%83%E3%82%AF%E3%82%A2%E3%83%83%E3%83%97%E8%A3%85%E7%BD%AE%E3%82%92%E6%8E%A5%E7%B6%9A%E3%81%99%E3%82%8B%E3%80%82 https://note.cman.jp/server/raid/raid6/

https://www.itmedia.co.jp/enterprise/spv/0705/28/news005.html

OS知識確認チェックリスト(随時更新)【クラウド活用の教科書を読んでみた】

はじめに

クラウドについて勉強したいと思い、「クラウド活用の教科書」を読みました。 本で、OSとストレージを理解することが成長の鍵となると著者が述べていました。 OS、ストレージのワード理解チェックリストが記載されていたので、調べてまとめてみました。(ワードの説明は本には記載されていなかったので、自分で調べる必要があった)今回はOSのワードを調べました。ストレージについては次回の記事に載せます。

参考

OSチェックリスト

プロセス

メモリ上で動いている実行中のプログラム。Windows OSにおけるプログラムの実行単位。ユーザーが何らかのアプリケーションを起動すると、実際には1つ以上のプロセスが起動することになる。実行中のプロセスと実行ファイルとの対応や、各プロセスのID、プロセスの説明、使用中のメモリサイズ、動作状態、優先度、使用中のリソースハンドルの数などの情報は、タスクマネージャーの「プロセス」タブの内容を見れば分かる。プロセスは、そのプロセスごとに独立したメモリ空間が割り当てられる。

スレッド

1つのプロセスの中には1つ、もしくは複数のスレッドが含まれており、このスレッドが実際にCPUで実行される単位となる。プロセスは、そのプロセスごとに独立したメモリ空間が割り当てられていて、スレッドは他のスレッドとメモリ空間を共有することができる。実行コアが複数あるCPUでは、「マルチスレッド」を活用したプログラミングが重要になる。マルチスレッドを用いると、アプリケーション内で必要に応じて複数の処理を並行して進められるため、処理の速度と精度が飛躍的に向上する。

マルチスレッドのイメージ f:id:xxyoko10:20210110233527p:plain

タスクマネージャーから、プロセス、スレッドを確認できる。 f:id:xxyoko10:20210110204737p:plain

カーネル

ユーザからの指示に従ってハードウェアを稼動させる"頭脳"の役割を担っているOSの中核の機能のこと。具体的に何をやっているのかというと、 「ハードウェアを稼動させる指示を出す、もっと具体的には、メモリー管理、ファイルの管理、デバイスドライバとしての役割、プロセスの管理」などを担っている。

システムコール

システムコールはアプリケーションとOSの管理をするリソースとの間を仲介しており、アプリケーションがリソースを間違った方法で利用することを防いだり、 セキュリティ上問題のあるような利用を防ぐ役割をもっている。これによってアプリケーションはリソースの利用を安全かつセキュアに行うことができる。 リソースの安全な利用の例としては、例えばプロセスによるメモリ領域の確保(mmapを使用した)がある。メモリというハードウェアリソースは、他のプロセスやOSと共有しており、過った方法で利用すると他のプロセスやOSを破壊してしまう恐れがある。プロセスがメモリ領域を確保したいときにこのシステムコールを介することで、OSが各プロセスへのメモリ領域の割り当てを安全な方法で行うことができる。

メモリダンプ

メモリダンプとは、クラッシュダンプとも呼ばれ、実行中のプログラムについて、その瞬間の物理メモリの情報をそのままハードディスクなどに出力したファイルのことを意味する。 メモリダンプには、そのときの実行状態などが記録されるため、プログラムのクラッシュやハングアップなど、異常終了等の発生時のトラブルシュートをする有効な調査手法として利用できる。

f:id:xxyoko10:20210112192457p:plain
Windows10のメモリダンプ設定項目

BIOSブートローダ

電源投入によって開始されるOS起動までの一連の動作をブートストラップという。 ブート ストラップの流れは大きくBIOSブートローダーに分けることができる。 BIOSは、「Basic Input/Output System」の略称で、PCの根幹となるプログラム。 PCのマザーボード(主基板)に実装された不揮発性メモリ(PCの電源を切っても記憶を保持できるメモリ)に組み込まれていて、PCの電源を入れたときに最初に起動される。OS(WindowsMac OSなど)のようにハードディスクに格納されているプログラムではない。実態はマザー ボード上のROMに格納されている。 起動されたBIOSは、PCに接続している周辺機器や組み込まれているパーツをチェックして使用できる状態に準備した後、ハードディスクなどの外部記憶装置からOSを起動するプログラムを実行する。なお、BIOSはカスタマイズすることも可能。ブートローダーはOSを検出・起動する。OSを格納しているディスクの先頭セクタ(ディスク状の記憶装置における最小の記録単位で、各セクタには一定量のデータが格納される)に記録されている。

f:id:xxyoko10:20210112211121p:plain

デーモン

UNIX系OSで利用者に見えない裏側で常に起動しているプログラム。Windowsでは同じ意味で「サービス」と呼ぶ。

ファイルシステム

記憶装置に保存されたデータを管理し、操作するために必要な機能。OSが提供する機能の一つで、ほとんどのOSはファイルシステムでファイルを管理している。 ファイルシステムは、ファイルを操作するためのインターフェイスを提供している。 ファイルシステムの基本的な機能として、ファイルの管理、ファイルの保存、暗号化機能、圧縮機能などがある。

ドライバ

パソコンに接続されている周辺装置をOSによって制御するために用意されたプログラムのこと。OSとハードウェアの橋渡しの役目がある。 OSがあらかじめ周辺機器に対応していなくても、動作させることができる。

参考

https://wa3.i-3-i.info/word11032.html https://www.atmarkit.co.jp/ait/articles/1410/30/news150.html http://www.words-search.com/it/424 https://www.wake-mob.jp/2019/10/it.html https://garop.com/203/ https://qiita.com/uguis410/items/17ec1e447e9716bfdca7 https://qiita.com/ftakao2007/items/d0fbecd3873054f603f5 https://linuc.org/study/knowledge/358/ https://linuc.org/study/knowledge/358/ https://www.atmarkit.co.jp/ait/articles/1111/16/news161.html https://www.school.ctc-g.co.jp/columns/kaga/kaga10.html#:~:text=%E3%83%A1%E3%83%A2%E3%83%AA%E3%83%80%E3%83%B3%E3%83%97%E3%81%A8%E3%81%AF%E3%80%81%E3%82%AF%E3%83%A9%E3%83%83%E3%82%B7%E3%83%A5,%E3%81%93%E3%81%A8%E3%82%92%E6%84%8F%E5%91%B3%E3%81%97%E3%81%BE%E3%81%99%E3%80%82 park12.wakwak.com PCの故障時に役立つBIOSとは?基礎知識と設定画面の起動方法 | パソコン修理・サポートのドクター・ホームネットがお届けするコラム demonとdaemon【Linuxデーモンは悪魔じゃねぇ!むしろ】-English Workbooksー英語ノート https://englishworkbooks.blogspot.com/2019/09/demon-daemon.html ファイルシステムの種類と特徴 | ITの基礎知識|ITパスポート・基本情報 ドライバ パソコン初心者講座 https://basics.k-labo.work/2017/10/23/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%81%AE%E7%A8%AE%E9%A1%9E%E3%81%A8%E7%89%B9%E5%BE%B4/

JSFのバージョンアップについて【メモ】

仕事でJSFのバージョンアップを実施することになったので、調べたことのメモと参考にしたサイトのURLを載せます。

1. JSF 1.2 JARを/WEB-INF/lib(存在する場合)から削除する
2. JSF 2.3JARを落とす 
3. faces-config.xmlJSF 2.3仕様に準拠するようにのルート宣言を更新

<faces-config
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_3.xsd"
    version="2.3">

4.依存関係を置き換え、新しいバージョンのJSFを使用

<dependency> 
    <groupId>com.sun.faces</groupId> 
    <artifactId>jsf-api</artifactId> 
    <version>2.3</version> 
</dependency> 

<dependency> 
    <groupId>com.sun.faces</groupId> 
    <artifactId>jsf-impl</artifactId> 
    <version>2.3</version> 
</dependency>

Apache MyFaces Extension Validator(extVal)で相関チェック - n-agetsumaの日記

[ThinkIT] 第4回:環境設定 (3/3)

Apache MyFaces - MyMemoWiki

クイックスタート - Apache MyFaces - Apache Software Foundation

JSF 1.2からJSF 2.0への移行

JSFを利用する場合

JSFをSpring Bootに統合してアプリケーションを作成

はじめに

JSFとSpring Bootを組み合わせてアプリケーションを作成しました。 前回はThymeleafをテンプレートに使用していましたが、今回はJSFのFaceletを使用します。
すべてのコードは以下に載せてます。
github.com

どんなものを作ったか

ToDoリストを作りました。
最初の画面のボタンをクリックするとToDoリスト画面に遷移します。
f:id:xxyoko10:20201229215700p:plain f:id:xxyoko10:20201229215733p:plain

「Save」ボタンでリストを追加できます。 削除したいNoを入力して「Delete」ボタンを押下すると、リストの削除ができます。
f:id:xxyoko10:20201229220150p:plain

環境

Windows 10
Java 1.8
Spring Framework 5.3.2
STS 4.8.1
JSF 2.3.7

構成

f:id:xxyoko10:20201230153653p:plain

テンプレート

http:localhost:8080/index.jsf を入力して最初の画面が開くように設定します。

 <f:view xmlns="http://www.w3c.org/1999/xhtml"
        xmlns:f="http://java.sun.com/jsf/core"
        xmlns:h="http://java.sun.com/jsf/html">
    <h:head>
        <meta charset="utf-8"/>
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
        <title>TO-DO application</title>
    </h:head>
    <h:body>
        <div>
            <p>ToDoリストアプリケーションへようこそ</p>
            <p style="height:50px">
                <h:form>
                    <h:commandButton value="Todoリストを利用する" action="#{jsfController.loadTodoPage}" />
                </h:form>
            </p>
        </div>
    </h:body>
</f:view>

web.xmlJSFで使用する専用のサーブレットを登録しておきます。「Facesサーブレット」を登録しています。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
          http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0">

<servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>facesServlet</servlet-name>
    <url-pattern>*.jsf</url-pattern>
</servlet-mapping>
</web-app>

コントローラークラス

最初の画面から、ToDoリスト画面への遷移はコントローラーが担当します。「Todoリストを利用する」ボタンを押すと処理が走ります。

package com.example.controller;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Scope(value = "session")
@Component(value = "jsfController")
public class JsfController {

    public String loadTodoPage() {
        return "/todo.xhtml";
    }
}

遷移先のToDoリスト画面です。

<f:view xmlns="http://www.w3c.org/1999/xhtml"
        xmlns:f="http://java.sun.com/jsf/core"
        xmlns:h="http://java.sun.com/jsf/html">
    <h:head>
        <meta charset="utf-8"/>
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
        <title>TO-DO application</title>
    </h:head>
    <h:body>
        <div>
            <div>
                ToDoを追加しましょう
            </div>
            <h:dataTable value="#{todoService.allTodo}" var="item">
                <h:column>
                    <f:facet name="header">
                    <h:outputText value="No"/>
                    </f:facet>
                    <h:outputText value="#{item.id}"/>
                </h:column>
                <h:column>
                    <div style="display:block; float:left; width:60px"></div>
                </h:column>                 
                <h:column>
                    <f:facet name="header">
                    <h:outputText value="やること"/>
                    </f:facet>
                    <h:outputText value="#{item.message}"/>
                </h:column>
                <h:column>
                    <div style="display:block; float:left; width:60px"></div>
                </h:column>                                 
                <h:column>
                    <f:facet name="header">
                    <h:outputText value="期限" style="display:block; float:left"/>
                    </f:facet>
                    <h:outputText value="#{item.deadline}"><f:convertDateTime  pattern="yyyy年MM月dd日"/></h:outputText> 
                </h:column>
            </h:dataTable>
        </div>
        <div><br></br><br></br><br></br><br></br><br></br>
            <div>
                New:
            </div>
            <h:form>     
                <h:panelGrid columns="8">     
                    <h:outputLabel for="message" value="やること"/>
                    <h:inputText id="message" value="#{todoService.todo.message}"/>
                    <div style="display:block; float:left; width:60px"></div>
                    <h:outputLabel for="deadline" value="期限"/>
                    <h:inputText id="deadline" value="#{todoService.todo.deadline}" style="width:80px"><f:convertDateTime  pattern="yyyyMMdd"/> </h:inputText>(yyyyMMdd)
                    <h:commandButton value="Save" action="#{todoService.save}"/>
                </h:panelGrid>
            </h:form>
            <h:form>  
                <h:panelGrid columns="3">        
                    <h:outputLabel for="id" value="削除"/>
                    <h:inputText id="id" value="#{todoService.todo.id}" style="width:60px"/>
                    <h:commandButton value="Delete" action="#{todoService.delete}"/>
                </h:panelGrid>  
            </h:form>            
        </div>
    </h:body>
</f:view>

サービスクラス

todo.xhtmlにToDoの要素を表示します。

package com.example.service;

import java.util.Collection;
import java.util.Comparator;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import com.example.model.Dao;
import com.example.model.Todo;

@Scope(value = "session")
@Component(value = "todoService")
public class TodoService {

    @Autowired
    private Dao<Todo> todoDao;
    private Todo todo = new Todo();

    public void save() {
        todoDao.save(todo);
        todo = new Todo();
    }

    public void delete() {
        todoDao.delete(todo);
        todo = new Todo();
    }

    public Collection<Todo> getAllTodo() {
        return todoDao.getAll();
    }

    public Collection<Todo> getAllTodoSortedByPriority() {
        return todoDao.getAll().stream().sorted(Comparator.comparingInt(Todo::getId)).collect(Collectors.toList());
    }

    public int saveTodo(Todo todo) {
        return todoDao.save(todo);
    }

    public Todo getTodo() {
        return todo;
    }
}
package com.example.model;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

import org.springframework.stereotype.Component;

@Component
public class TodoDao implements Dao<Todo> {

    private List<Todo> todoList = new ArrayList<>();

    @Override
    public Optional<Todo> get(int id) {
        return Optional.ofNullable(todoList.get(id));
    }

    @Override
    public Collection<Todo> getAll() {
        return todoList.stream().filter(Objects::nonNull)
                .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
    }

    @Override
    public int save(Todo todo) {
        todoList.add(todo);
        int index = todoList.size();
        todo.setId(index);
        return index;
    }

    @Override
    public void delete(Todo todo) {
        todoList.set(todo.getId() - 1, null);
    }

}

TodoServiceクラスの getAllTodo() メソッドがはじめに呼ばれ、ToDoの要素をすべて表示します。@Scope(value = "session")で データをセッションスコープで保持しています。

つまずいたところ

  • cannot find the declaration of element 'faces-config' エラー発生
    アプリケーション起動時に『cannot find the declaration of element 'faces-config'.』というエラーが発生しました。
    エラーの原因は「faces-config.xml」にありました。
<?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd"
    version="2.2">
    <application>
        <el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
    </application>
</faces-config>


4行目の【xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd】のURLの記述が間違っていました。

<?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
  http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd"
    version="2.2">
    <application>
        <el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
    </application>
</faces-config

xsdファイルのURLを変更することで、エラーを解決できました。
参考:jtdz-solenoids.com

  • java.lang.ClassNotFoundException: javax.enterprise.context.spi.Contextual エラー発生
    アプリケーション起動時に『java.lang.ClassNotFoundException: javax.enterprise.context.spi.Contextual』というエラーが発生しました。
    「pom.xml」に以下のライブラリーを追加することで治りました。
     <dependency>
            <groupId>javax.enterprise</groupId>
            <artifactId>cdi-api</artifactId>
            <version>1.2</version>
        </dependency> 


参考: stackoverflow.com

参考

www.codeflow.site

Spring Bootでアプリケーション作成②

はじめに

前回の続きでSpring Frameworkを使用したアプリケーションを作成します。
今回作成したコードはすべて以下にあげているので、良かったら参考にしてください。

github.com

エンティティークラスの作成

  • テーブルの列がエンティティークラスのフィールドに対応付けられる

  • 主キーを表す@Idを付与する

  • クラス名、フィールド名、フィールドの型にデータベースのテーブル名、列名、列型を指定することで、自動的にマッピングされる

  • Lombokの@Getter、@Setterを使用するのでgetter, setterメソッドは記述する必要なし(Lombokコンパイル時に自動で生成してくれる)

package com.spring.data.jpa.example.repository.entity;

import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Entity
@Getter
@Setter
@ToString
public class Employee implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * 性別の列挙
     */
    public static enum Sex {
        male, female
    }

    /** 社員番号 */
    @Id
    @GeneratedValue
    private Integer no;

    /** 名前 */
    @Column(length = 20, nullable = false)
    private String firstName;

    /** 苗字 */
    @Column(length = 20, nullable = false)
    private String lastName;

    /** 性別 */
    @Column(length = 10)
    @Enumerated(EnumType.STRING)
    private Sex sex;

    /** 生年月日 */
    private java.sql.Date birthday;

    /** メールアドレス */
    @Column(unique = true)
    private String mailAddress;
}

サービスクラスの作成

  • テーブルの情報をすべて取得する
  • @Transactionalを使用して、メソッドが呼び出されるとトランザクションが開始される
package com.spring.data.jpa.example.service;

import java.util.List;

import com.spring.data.jpa.example.repository.entity.Employee;

public interface EmployeeService {
    public List<Employee> getEmployeeList();
}
package com.spring.data.jpa.example.service;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.spring.data.jpa.example.repository.entity.Employee;

@Service
@Transactional(readOnly = true)
public class EmployeeServiceImpl implements EmployeeService {
    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public List<Employee> getEmployeeList() {
        return entityManager.createQuery("SELECT e FROM Employee e",
                                        Employee.class).getResultList();
    }
}

コントローラークラスの作成

  • 取得したテーブルのデータをモデルに設定する *テーブルの情報を画面にわたす

  • @RequestMappingを使用して、メソッドにアクセスするためのURLを定義する

  • 「/employee/lis」にアクセスすると、「list」メソッドが呼び出される

package com.spring.data.jpa.example.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import com.spring.data.jpa.example.service.EmployeeService;

@Controller
@RequestMapping("employee")
public class EmployeeController {
    @Autowired
    private EmployeeService employeeService;

    @RequestMapping("list")
    public String list(Model model) {
        model.addAttribute("employees", employeeService.getEmployeeList());

        return "employee/list";
    }
}

テンプレートの作成

  • コントローラークラスから受け取ったデータを画面に表示する
<html xmlns="http://www.w3.org/1999/xhtml"
        xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<style type="text/css">

table {
    border-collapse: collapse;
}
th, td {
    border: 1px darkgray solid;
}

</style>
</head>
<body>
<table>
    <thead>
        <tr>
            <th>No</th>
            <th>First Name</th>
            <th>Last Name</th>
            <th>Sex</th>
            <th>Birthday</th>
            <th>Mail Address</th>
        </tr>
    </thead>
    <tbody>
        <tr th:each="employee : *{employees}">
            <td th:text="${employee.no}"></td>
            <td th:text="${employee.firstName}"></td>
            <td th:text="${employee.lastName}"></td>
            <td th:text="${employee.sex}"></td>
            <td th:text="${employee.birthday}"></td>
            <td th:text="${employee.mailAddress}"></td>
        </tr>
    </tbody>
</table>
</body>
</html>

SQLの作成

*起動時にテーブルのcreate、insertをしてくれる

CREATE TABLE IF NOT EXISTS employee (
    no INTEGER PRIMARY KEY,
    birthday DATE,
    first_name VARCHAR(20) NOT NULL,
    last_name VARCHAR(20) NOT NULL,
    mail_address VARCHAR(30),
    sex VARCHAR(10)
);
INSERT INTO employee
        ( no, birthday, first_name, last_name, mail_address, sex )
    VALUES
        (1,'1980-01-31','太郎','山田','taro.yamada@mail.com','male');
INSERT INTO employee
        ( no, birthday, first_name, last_name, mail_address, sex )
    VALUES
        (2,'1985-02-10','一郎','鈴木','ichiro.suzuki@mail.com','male');
INSERT INTO employee
        ( no, birthday, first_name, last_name, mail_address, sex )
    VALUES
        (3,'1985-03-10','花子','石井','hanako.ishii@mail.com','female');

アプリケーションを起動する

Spring Bootアプリケーションを実行 ⇒ URL『http:localhost:8080/employee/list』を入力 f:id:xxyoko10:20201227013717p:plain

テーブルのデータが表示されました。

補足

  • テーブルの作成方法について 今回は起動時に「schema.sql」を読み込むことで、テーブルを作成しました。
    「schema.sql」を作成しなくても、テーブルを自動で作成する方法があります。
    「application.properties」の設定を一部変更します。
spring.jpa.hibernate.ddl-auto=none
spring.jpa.hibernate.ddl-auto=create

「spring.jpa.hibernate.ddl-auto」の値を「create」に変えるだけです。
こうすることで、「schema.sql」がなくてもテーブルを自動で作成してくれます。
ちなみに、「spring.jpa.hibernate.ddl-auto=create」の状態で「schema.sql」を作成した場合、 テーブルのcreate、insertが失敗しました。

まとめ

テーブルのデータを取得して、画面に表示するアプリケーションを作成できました。

参考

Spring Data JPAプログラミング入門

Spring Data JPAプログラミング入門

Spring Bootでアプリケーション作成①

はじめに

来月から仕事でSpring Frameworkを使用することになったので、
勉強のために以下の本を参考にアプリケーションを作成しました。

Spring Data JPAプログラミング入門

Spring Data JPAプログラミング入門

バージョンの違いなどもあって、エラーが発生したりスムーズにいかなかったところもあったので、 解決策などを忘れないように記録しています。

どんなものを作るか

サービスクラスで、テーブルから社員リストを取得して画面に表示させます。

f:id:xxyoko10:20201225184505p:plain

環境

Windows 10
Java 1.8
Spring Framework 5.3.2
STS 4.8.1
JSF 2.3.7

構成

f:id:xxyoko10:20201225190436p:plain

Thymeleafの設定

テンプレートエンジンとしてThymeleafを使用しているので、pom.xmlにThymeleafの
バージョン指定を追加します。

本にはバージョンは3.0.3.RELEASEと記述されていました。

<thymeleaf.version>3.0.3.RELEASE</thymeleaf.version>

ここを本通りに記述すると、エラー指摘されます。
メッセージ【Missing artifact org.thymeleaf:thymeleaf-spring5:jar:3.0.3.RELEASE】

Spring5がサポートしている最新バージョンに変更します。
以下で最新バージョンを確認
f:id:xxyoko10:20201225134102p:plain

The Central Repository Search Engine

最新バージョンである『3.0.12.RELEASE』に変更すると、エラー指摘が消えました。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.spring.data.jpa.example</groupId>
    <artifactId>spring-data-jpa</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>spring-data-jpa</name>
    <description>Example project for Spring Data JPA</description>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>    
        <java.version>1.8</java.version>
       <thymeleaf.version>3.0.12.RELEASE</thymeleaf.version>
        <thymeleaf-layout-dialect.version>2.1.2</thymeleaf-layout-dialect.version>
        <thymeleaf-extras-java8time.version>3.0.0.RELEASE</thymeleaf-extras-java8time.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-java8time</artifactId>
            <version>3.0.3.RELEASE</version>
        </dependency>      
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.17</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>              

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

application.propertiesの設定

データベースの接続情報をapplication.propertiesに追加します。
アプリケーションを起動したところエラーが発生しました。

spring.thymeleaf.mode=HTML
spring.thymeleaf.cache=false

spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none

spring.datasource.url=jdbc:mysql://localhost:3306/work
spring.datasource.username=[Connection User Name]
spring.datasource.password=[Connection User Password]
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.sql-script-encoding=utf-8
spring.datasource.initialization-mode=always

サーバーのタイムゾーンエラーが発生しました。

エラーメッセージ
java.sql.SQLException:The server time zone value '���� (�W����)' is unrecognized or represents more than one time zone.

You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value

if you want to utilize time zone support.


サーバーのタイムゾーンの名前が不明であるみたいなことを指摘されました。 解決方法は【spring.datasource.url=jdbc:mysql://localhost:3306/work】に【?serverTimezone=JST】を追加するそうです。

spring.thymeleaf.mode=HTML

spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none

spring.datasource.url=jdbc:mysql://localhost:3306/work?serverTimezone=JST
spring.datasource.username=[Connection User Name]
spring.datasource.password=[Connection User Password]
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.sql-script-encoding=utf-8
spring.datasource.initialization-mode=always

これでタイムゾーンエラーが解決しました。
参考:SpringBootのMySQL接続で「The server time zone value」エラー - Code Jamjar

  • com.mysql.jdbc.Driver設定でエラー
    タイムゾーンエラー解決後、今度は以下のエラーが発生しました。

    エラーメッセージ
    Loading class com.mysql.jdbc.Driver'. This is deprecated. The new driver class iscom.mysql.cj.jdbc.Driver'.

    The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.


    com.mysql.jdbc.Driverドライバーは非推奨で、新しいドライバーはcom.mysql.cj.jdbc.Driverです、と指摘されました。
    com.mysql.cj.jdbc.Driverに修正します。

spring.thymeleaf.mode=HTML

spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none

spring.datasource.url=jdbc:mysql://localhost:3306/work?serverTimezone=JST
spring.datasource.username=[Connection User Name]
spring.datasource.password=[Connection User Password]
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.sql-script-encoding=utf-8
spring.datasource.initialization-mode=always


再度、アプリケーションを起動するとエラーはなくなりました。

まとめ

pom.xmlとapplication.propertiesを設定して、アプリケーションを正常に起動するところまで出来ました。 次回は、エンティティー、サービス、コントローラー、テンプレートの作成をします。

static ファクトリメソッドについて

はじめに

今日Effective Javaの本を読み始めました。
最初の章にstatic ファクトリメソッドについて書かれていたのですが、
字が多くてなかなかイメージしずらかったので、いつものように簡単な例を出しながら
自分なりにまとめてみました。

コンストラクタの代わりにstatic ファクトリメソッドを使う

クライアントがインスタンスを得る方法は2通りあります。
1つはクラスが提供するコンストラクタを利用する方法です。
もう1つは、クラスのインスタンスを返すstatic ファクトリメソッドを利用する方法です。
コンストラクタは毎回インスタンスを生成しますが、static ファクトリメソッドを使うと、 既存のインスタンスを使いまわすように制御することも可能になります。

実際にstatic ファクトリメソッドを利用してみます。

public class FactoryMethod {

    private static List<FactoryMethod> pool = new ArrayList<FactoryMethod>();   

    private FactoryMethod() {
    }

    public static FactoryMethod valueOf() {
        if(pool.size() == 0) {
            pool.add(new FactoryMethod());     // インスタンスを生成してプールに追加する
        }

        return pool.get(0);
    }

}

valueOf()メソッドで、既存のインスタンスが存在していないかを確認して、 ない時だけ、インスタンスを作成しています。

public class Main {
    public static void main(String[] args) {
        FactoryMethod obj1 = FactoryMethod.valueOf();
        FactoryMethod obj2 = FactoryMethod.valueOf();

        if(obj1 == obj2) {
            System.out.println("obj1とobj2は同じインスタンスです。");
        }
    }
}

f:id:xxyoko10:20200429201841p:plain

同じインスタンスを取得できました。
これで重複したオブジェクトの生成を避けることができます。

また、コンストラクタとは違って、メソッドに適切な名前を付けることができるので
コードを読みやすくなります。

デメリットもあるみたい

extendsして、サブクラスを作成することができません。
public またはprotectedのコンストラクタを持っていないからです。
f:id:xxyoko10:20200429205009p:plain

継承ができないので、コンポジションを代わりに使うことになるでしょう。

参考文献