技術とか戦略とか

IT技術者が技術や戦略について書くブログです。

私が所属する会社から入門書が発行されました!スクールも同時開校です!

表題の通り、Javaプログラミング入門書「絶対にJavaプログラマーになりたい人へ」が発行されました!
Amazon kindleで読むことができます。
 
私も執筆の多くの部分に関わっています。
 
ご興味がある方は是非とも手に取ってみてください!


 
同時に、プログラミングスクール「サイゼントアカデミー」が開校されました!
サポートやフィードバックが欲しくなりましたらこちらも是非!
 
詳しい情報は、会社のブログにて書かせていただきました。

cyzennt.co.jp

ITシステムの安定稼働は神頼み?

GWということで、ITシステムのリリースを控えている会社は少なくないと思います。
リリースするシステムは是が非でも安定稼働させたいものです。
 
そこで、神様の力を借りて、システムの安定稼働を祈願するのはいかがでしょうか?
嘘のような話かもしれませんが、神社に出向いたり、祭壇を作ったり、お札を貼ったりする現場は実在しますし、筆者の経験上、その効果は無視できないものであると思っています。
 
----
 
東京都内で働く人にとって最も有名で手が出しやすいのは、神田明神だと思います。
神田明神の公式ページはこちらです。

www.kandamyoujin.or.jp


 
公式ページの「アクセス」の通りですが、御茶ノ水駅から徒歩5分、新御茶ノ水駅から徒歩5分、末広町駅から徒歩5分、秋葉原駅から徒歩7分と、都内で働いている人にとってはアクセスしやすい場所にあります。
また、「FAQ」の通り、この神社はITの安全を祈願する場所でもあります。「IT情報安全守護」というお札も販売されています。
 
----
 
神頼みには科学的な根拠はないかもしれませんが、筆者の経験上、関係者の精神面、特に「神頼み」という発想に至るまでの過程において、何かしらの効果はあると思っています。
 
どれだけ準備を怠らなかったとしても、システムが安定稼働するかどうかは運次第な所が要素が残ります。
ハードウェアが故障するかもしれませんし、連携している他社のシステムの障害に巻き込まれるかもしれません。
そして、人間がすることなので、ミスを完全にゼロにすることもできません。
 
運だと思えることまで徹底して準備を行い、それでも何かできることがないかと考えた末に辿り着くのが「神頼み」という発想です。
目の前の準備に追われ、余裕がないプロジェクトにおいては、神頼みしようと思うような精神状態ではなくなります。
「神頼み」という発想に至った時点で、品質は高い傾向にあり、実際に安定稼働もする、というのが、筆者の見解です。
 
筆者も、「神頼み」という発想に至るまで準備を怠らないようにしたいものです。

JPAでのDelete実装方法三選

Spring Framework + JPAでのDelete実装方法について、調べるのに少し苦労しましたので、実装方法をまとめます。
 
----
 
1.CrudRepositoryインターフェースのメソッドを利用する
全件削除、1つ~複数のエンティティクラスのインスタンスを指定して削除、1つ~複数の主キーの値を指定して削除するだけであれば、CrudRepositoryインターフェースのメソッドを利用することでシンプルな実装で対応できます。
CrudRepositoryインターフェースのメソッドについては、以下の公式ドキュメントで説明されています。
https://spring.pleiades.io/spring-data/commons/docs/current/api/org/springframework/data/repository/CrudRepository.html
 
以下は、全件削除を行うサンプルコードです。
(コードは一部を抜き出したものです)
 
【エンティティクラス】
・Message.java
package com.example.demo.domain.message.model;

import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.Table;

import lombok.Data;

@Entity
@Table(name="message")
@Data
public class Message {
    
    // ID
    @Id // 主キーに対して付与
    private int id;

    // 投稿内容
    private String text;
    
    // 種別ID
    private String kindId;
}
 
【Daoクラス】
・MessageDao.java
package com.example.demo.repository;

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

import com.example.demo.domain.message.model.Message;

public interface MessageDao extends JpaRepository<Message,String> {

}
 
【Serviceクラス】
今回の例では、ServiceクラスにCrudRepositoryインターフェースのメソッドを記述します。

・MessageService.java
package com.example.demo.domain.message.service;

import org.springframework.transaction.annotation.Transactional;

public interface MessageService {

    /** 削除(全件) */
    @Transactional // メソッドを抜ける時にcommit発行
    public void deleteAll();

}
 
・MessageServiceImpl.java
package com.example.demo.domain.message.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.example.demo.domain.message.service.MessageService;
import com.example.demo.repository.MessageDao;

@Service
public class MessageServiceImpl implements MessageService {

    @Autowired
    private MessageDao dao;
    
    /** 全件削除 */
    @Override
    public void deleteAll() {
        dao.deleteAll();
        return;
    }

}
 
【Controllerクラス】
今回のサンプルでは省略しますが、Serviceクラスのメソッドを呼び出すことで、削除が実行されます。
 
----
 
2.SQL文を直接記述する
極力避けるべきではありますが、要件が複雑な場合はSQL文を直接記述せざるを得なくなる場合があります。
今回の例では、「主キー以外の項目を削除条件に指定する」「削除件数を取得する」という要件を満たすためにSQL文を直接記述します。
 
【エンティティクラス】
先ほどの例と同じです。
 
【Daoクラス】
・MessageDao.java
package com.example.demo.repository;

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

import com.example.demo.domain.message.model.Message;

public interface MessageDao extends JpaRepository<Message,String> {

    /* JPQL定数 */
    final String JPQL_DELETE_MESSAGE_BY_KINDID = " DELETE "
                                                + " FROM Message m "
                                                + " WHERE m.kindId = :kindId ";
    
    /** 削除(種別ID指定) */
    @Modifying
    @Query(JPQL_DELETE_MESSAGE_BY_KINDID)
    public long deleteByKindId(String kindId) throws DataAccessException;

}
 
【Serviceクラス】
・MessageService.java
package com.example.demo.domain.message.service;

import org.springframework.transaction.annotation.Transactional;

public interface MessageService {

    /** 削除(全件) */
    @Transactional // メソッドを抜ける時にcommit発行
    public long deleteAll();

}
 
・MessageServiceImpl.java
package com.example.demo.domain.message.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.example.demo.domain.message.service.MessageService;
import com.example.demo.repository.MessageDao;

@Service
public class MessageServiceImpl implements MessageService {

    @Autowired
    private MessageDao dao;
    
    /** 種別ID="0001"を削除 */
    @Override
    public long deleteAll() {
        return dao.deleteByKindId("0001");
    }

}
 
【Controllerクラス】
Serviceクラスのメソッドを呼び出すことで、削除が実行されます。
 
----
 
3.Derived deleteBy Methodsを使用する
「主キー以外の項目を削除条件に指定する」「削除件数を取得する」という要件であれば、Derived deleteBy Methodsを使用することで、SQL文を直接記述せずとも実現できます。
Derived deleteBy Methodsについては、以下のページで使い方が解説されています。
https://www.baeldung.com/spring-data-jpa-deleteby
 
以下は使用例です。
 
【エンティティクラス】
先ほどの例と同じです。
 
【Daoクラス】
・MessageDao.java
package com.example.demo.repository;

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

import com.example.demo.domain.message.model.Message;

public interface MessageDao extends JpaRepository<Message,String> {

    /** 削除(種別ID指定) */
    public Long deleteByKindId(String kindId);

}
 
【Serviceクラス】
【Controllerクラス】
先ほどの例と同じです。

Java:リテラル用の領域の確保について

Javaの変数の型は、プリミティブ型と参照型に大別されます。
そして、変数の値が同じであるかどうかを確認する場合、プリミティブ型は == で同じ値であることを確認できる(同じ値の場合はTrueになる)のに対し、参照型の場合は原則として == では確認できず、equalsメソッドを使う必要があります。
 
しかし、参照型の場合であっても、== で確認できる場合があります。
それは、参照型の変数にリテラルソースコード内に直接記述された定数値)を直接代入した場合です。
 
以下で、順番を追って説明していきます。
 
【メモリへの値の保持の方法】
変数の値はメモリに保持されます。
メモリは1バイト(8ビット)ずつ細かく区分けされており、それぞれの区分けについて「アドレス」と呼ばれる値により一意に場所を特定します。
(アドレスのバイト数は、64ビットOSの場合は8バイトです)
 
プリミティブ型も参照型も、「メモリに保持される」ということは変わりませんが、メモリに保持する値は変わります。プリミティブ型変数では値そのものをメモリに格納するのに対し、参照型変数ではメモリ上にその変数用の保存領域を確保した上で、その保存領域の場所を指し示すアドレスを変数の領域に格納します。
 
例として、以下のソースコードについて考えます。
import java.util.*;

public class Main {
    public static void main(String args) throws Exception {
        
    // プリミティブ型変数の例
    int i1 = 1;
    
    // 参照型変数の例
    int
il1 = new int {1,2,3};
    }
}
 
このソースコードの場合のメモリのイメージは以下です。
プリミティブ型変数の「i1」と、参照型変数の「il1」では、値の保持の方法が異なります。

 
【リテラル用の領域の確保】
リテラルの場合は、リテラル用の領域が別途確保されます。
そして、同じ値のリテラル値が複数の箇所で記述されていたとしても、全ての箇所において同じ領域が使いまわされます。
 
参照型変数でインスタンス化した(newした)場合は、仮に同じ値だとしても、インスタンス化する度に異なる領域が確保されるため、その挙動とは異なるものとなります。
 
例として、以下のソースコードについて考えます。
import java.util.*;

public class Main {
    public static void main(String args) throws Exception {
        
        // リテラルのアドレスを代入
        String s1 = "hoge";
        String s2 = "hoge";
    
        // 新たに領域を確保し、そのアドレスを代入
        String s3 = new String("hoge");
        String s4 = new String("hoge");

        // 他のリテラルのアドレスを代入
        String s2 = "fuga";
    }
}
 
このソースコードの場合のメモリのイメージは以下です。
リテラル値を代入した「s1」「s2」と、インスタンス化で新たに領域を確保した「s3」「s4」では、値の保持の方法が異なります。

 
【比較を行った場合の挙動の違い】
上記より、リテラル値を代入した場合と、インスタンス化を行った場合では、値の保持の方法が異なります。
 
この違いが表れるケースの一つとして、値が同一であるかどうかを確認するケースがあります。
リテラル値を代入した参照型変数同士を比較する場合は、インスタンス変数同士の比較と同じように、== で値が同一であることを確認できます。
しかし、インスタンス化を行った参照型変数の比較では、== で値が同一であることを確認することはできず、equalsメソッドを使用する必要があります。
 
これをソースコードで示すと以下の通りです。
(実行結果となる標準出力はコメントで示しています)
import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {
        
        // リテラルを代入する場合
        String s1 = "hoge";
        String s2 = "hoge";
        System.out.println(s1 == s2); // true
        
        // インスタンス化する場合
        String s3 = new String("hoge");
        String s4 = new String("hoge");
        System.out.println(s3 == s4); // false
        System.out.println(s3.equals(s4)); // true
    }
}
 
----
 
参照型変数の比較について正しく説明する機会があったため、この記事を作成しました。

様々なタスクの処理方法

仕事をしていると、大小様々なタスクが発生します。
特に、管理職やリーダーのような、上のポジションに就くと、その傾向が強くなります。

この記事ではタスクの処理方法を紹介します。
以下の処理方法を適切に使い分けることで、効率良く仕事ができるようになります。
 
・タスクリストに上がる前に処理する
タスクが発生したらタスクリスト(バックログ)で管理したくなりますが、タスクの管理には認知資源が必要になり、タスクの一覧を俯瞰して確認したり、タスクの内容を思い出したりするのに時間がかかるようになってしまいます。
例えば「メールや電話で連絡する」のようなすぐ終わるタスクなのであれば、すぐに対応して、タスクリストに書かずに済ませるのが一番良いです。
「仕事ができる人は返信が早い」と良く言われますが、これはタスクリストでの管理をできる限り避けようとするための行動と言えるでしょう。
 
・タスクリストに挙げて、優先度が高いものから対応する
すぐに対応できないタスクについては、タスクリストに挙げるのが鉄則です。
タスクリストに挙げることで、対応忘れを防ぐと共に、効率的にタスクを消化する作戦を立てやすくなります。
効率的にタスクを消化する作戦として、基本的なものは「優先度が高いタスクから対応する」というものです。
「優先度が高いタスク」とは、具体的には「期限が迫っていて先延ばしも難しいタスク」を指します。
期限や工数が不明の場合、先延ばしの可否が不明の場合は、あらかじめ関係者に確認を取ったり見積もりをしたりするべきです。
 
・適切な人にタスクを振る
上のポジションに就くと、自分一人では対応しきれない量のタスクを扱う必要が出てきます。
その場合は、タスクの対応を他の人に依頼する必要が出てきます。
タスクを依頼する場合は、タスクの性質を見て、適切な人に依頼するべきです。
例えば、自分が対応しようとした場合に調査や準備で時間がかかる難しいタスクは、専門家に振るのに適しています。
逆に、自分であれば簡単に対応でき、他の人が対応したとしてもサポートできるようなタスクであれば、若手に振るのに適しています。
 
・タスクを組み替える
発生したタスクを組み替えることで、効率良くタスクを処理できるようになるケースがあります。
1つのケースとして、複数のタスクを1つのタスクにまとめることで、同時に処理できるようになるケースがあります。
例えば、「システムA向けの監視バッチを作成する」と「システムB向けの監視バッチを作成する」という2つのタスクについて、この2つのシステムの共通点が多い場合は、両方に対応した監視バッチを作ることで同時に処理できるようになります。
別のケースとして、1つのタスクを複数のタスクに分解することで、タスクを効率良く処理できるようになるケースがあります。
例えば、「プログラムを作成する」というタスクについて、難しい部分と簡単な部分があるのであれば、それぞれ別のタスクに分解して別の人に振った方が効率良く処理できます。
 
・タスクを処理しないことを決めて許可を得る
発生したタスクの中には、「実は対応しなくても良いタスク」「むしろ対応しない方が良いタスク」が紛れ込むことがあります。
そのようなタスクについては、認知資源の節約のため、タスクを入れ込んだ人に対応しないことの許可を得た上で、タスクリスト上で完了の扱いにするべきです。
例えば、「上司向けにシステムの使い方を書類を作って教える」というタスクが発生したとします。
ここで、既存のQ&Aページを教えることでその上司がシステムの使い方を理解できるのであれば、自分で書類を作る必要は無くなります。Q&Aを教え、その上司がシステムの使い方を理解し、書類を作る必要性が無くなったことを本人に確認することで、タスク消化とみなすことができます。

BIツール「Amazon QuickSight」の紹介

実務でBIツール「Amazon QuickSight」の導入を支援する機会がありましたので、ツールの紹介をします。
 
【BIツールとは】
BIとは「ビジネスインテリジェンス」の略であり、事業上の意思決定のために、情報を収集・加工し、分析し、知見を得ることを指します。

ここで言う「情報」とは、売上や費用等の自社の利益に関するデータ、及びそれを左右する競合他社や経営環境に関するデータのことを指します。
データの形式としては、CSVファイルやリレーショナルデータベース等の表形式を思い浮かべるとわかりやすいでしょう。

上記のデータに対し、集計を行い、グラフや表や図の形式に加工することで、「どの地域でどの商品の売れ行きが良いのか」「自社の製品と競合他社の製品の価格差は自社の利益にどのような影響を及ぼすのか」「猛暑の年と冷夏の年で自社製品の売上がどの程度変わるのか」といった有益な知見を得ることができます。
その知見は、「どの分野に集中的に投資すれば良いのか」「自社の製品の価格はどのように決めれば良いのか」「毎年の生産量はどのように決めれば良いのか」といった、経営上の意思決定を行う上で役に立ちます。

BIツールは、上記の活動を支援するための各種ツールのことを指します。
身近な所で言うと、表形式のデータの取り扱いと関数を用いた集計、グラフの表示をサポートする「Microsoft Excel」はBIツールとみなすことができるでしょう。
 
Amazon QuickSightとは】
Amazon QuickSight」(以下「QuickSight」)はBIツールの一種です。
BIツールには様々な種類がありますが、QuickSightは、グラフや表や図を一画面で一覧できる「ダッシュボード」を作成するツールに分類されます。

QuickSightで作成できるダッシュボードは強力なものであり、例えば以下のような機能を備えています。
・表示のドリルダウンとドリルアップ(表示されるデータの粒度の細分化・集約化)
・データのフィルタリング(プルダウンやチェックボックスを用いたデータの抽出)
・マップビジュアル(地図の上にデータの大小を視覚的に表現する機能)

QuickSightはローコードで上記の機能を備えたダッシュボードを作成することができ、アプリケーション開発の専門的な知識が無くとも少ない工数でBIを実現できます。
 
Amazon QuickSightで扱うデータ】
QuickSightはダッシュボード作成を支援するツールですので、その元となるデータは自分で用意する必要があります。

QuickSightは様々な形式のデータに対応しており、例えば以下のような形式のデータに対応しています。
CSV形式やTSV形式等のファイル
・各種リレーショナルデータベース(Amazon RDS、OracleSQL ServerMySQLPostgreSQL等)
Amazon S3(オンラインストレージ)や、S3へのSQLでのクエリをサポートするAmazon Athena

QuickSightでは、SQLの集計関数で実現できる集計機能は一通りサポートしており、集計元のデータと集計後のデータを連動させた形で表示(例えば、各店舗のデータの一覧表と、一覧表上の全店舗の平均売上高を同時に表示)させることが多いので、用意するデータは集計する前のものとし、集計はQuickSight上で行うとスムーズに実装が進みます。
例えば、以下のようなデータを用意すると良いです。

チュートリアル_ 準備完了済みの Amazon QuickSight データセットを作成する - Amazon QuickSight

https://docs.aws.amazon.com/ja_jp/quicksight/latest/user/example-prepared-data-set.html

 web-and-social-analytics.csv.zip

 

Amazon QuickSightのデモ】
QuickSightのデモが公式に公開されています。
このデモを操作することで、QuickSightがどのようなツールなのかイメージがつくと思います。

DemoCentral

https://democentral.learnquicksight.online/#Analysis-DashboardDemo-AnyTel-Insights

 

Amazon QuickSightのチュートリアル
QuickSightのチュートリアルが公式に公開されています。

このチュートリアルをこなすことで、QuickSightを使用した簡単なダッシュボード作成を行えるようになります。
チュートリアルで基礎を学んだ後に、高度な機能を調べたり、QuickSightを実際に使用しながら学ぶことで、より難しいダッシュボードも作成できるようになります。

Amazon QuickSight - Visualization Basics (Japanese)

https://catalog.us-east-1.prod.workshops.aws/workshops/aa601d0b-84c9-4f77-b9a7-5954d8574cd5/ja-JP

CDツールを使わない場合のデプロイ手順のイメージ

現在の開発現場・運用現場では、CD(Continuous Delivery)ツールを使用したデプロイ(サーバーへの資材配置)が一般的になっています。
CDツールの設定を行うのは一部の技術者のみであることもあり、CDツールを使用しない場合の原始的な手順でのデプロイ手順は、経験の浅い技術者にはイメージしにくいものであると感じています。
 
そこで、この記事では、CDツールを使用しない場合のデプロイ手順のイメージを書いていきます。
デプロイ手順を知ることで、CDツールを使う理由やそのありがたさを理解しやすくなると思います。
 
----
 
開発した機能を本番サービスに反映させるためには、開発環境で開発を行った資材(プログラムの実行モジュールやスクリプト、設定ファイル等)を商用環境に配置する必要があります。
配置を行うためには、最低限下記の作業が必要になります。
(コマンドのイメージも併記します)
 
1.商用環境のサーバーへのログイン
ターミナルでxxxサーバーにログイン(IPアドレス、ユーザー名、パスワードを入力)

2.ファイル共有サーバーからの資材取得
#> ftp xxx.xxx.xxx.xxx
#> username
#> password
#> get xxx.tar
#> bye

3.資材(圧縮ファイル)の解凍
#> tar xvf xxx.tar

4.解凍後の資材の配置
#> cp -rp xxx yyy
 
上記の作業を行うだけでも、コマンドの誤り・漏れによる作業ミスの可能性がありますし、入るサーバーを間違えてしまうことすらあります。
作業を引き継ぐことにも困難さを伴い、作業手順書のようなものが必要になります。
 
加えて、下記のような作業を伴うことも多く、実際の手順はより複雑になります。
・デプロイ時のサービス停止や機能制限、それに伴う設定変更
 (サーバー毎に設定が異なることも多い)
・デプロイ前のバックアップ取得
・デプロイ後の定型的な確認作業
 
----
 
作業ミスを防いだり引継ぎを容易にしたりする上で、CDツールの利用が有効になります。
CDツールでは、コマンドや設定を登録し、複数のサーバーに対して画面からGUIベースで作業することができるようになります。
 
イメージとしては以下のようなものになります。
(以下は、CircleCIのチュートリアルの一部です)
 
Hello World - CircleCI
https://circleci.com/docs/ja/hello-world/