技術とか戦略とか

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

システム改修で発生するデータ移行とは

通常のシステムでは、データベースやファイル等にデータを蓄積します。
そして、システムの改修の際に、そのデータのフォーマットを変更することがあります。
 
データのフォーマットの変更には、「これまでに蓄積されたデータをどのように扱うのか」という問題が付きまといます。
開発者は、データを移行するのかしないのか、移行するのであればどのように移行するのか、ということを判断する必要があります。
ここでは、データ移行について、具体的な例を用いて見て行こうと思います。
 
【例】
商品購入履歴テーブルにて、清算番号をキーとし、いつ誰が何をどれだけ購入したのかを管理しているとします。
システム稼働開始時は、商品コードから単価は一意に求まるとします。
また、当該テーブルを見て、各々の顧客が過去に商品を購入したことがあるかどうかを見る必要があるとします。
 
ここで、各々の清算において値引きを可能にするシステム改修を行うとします。
また、売り上げを正しく求めるため、清算毎の単価を新たに管理する必要が出てきたとします。
 
この場合、商品購入履歴テーブルのフォーマットを以下のように変更する必要が出てきます。

f:id:akira2kun:20210410183802j:plain
 
ここで、過去に蓄積されたデータの扱いについて、以下のような方針を考える必要が出てきます。
 
【対応方針】
1.データ移行せず、旧商品購入履歴テーブルをそのまま残す
データ移行が困難であり、プログラムの改修箇所が少ないのであれば、データ移行しないという選択肢があります。
データ移行が困難な状況としては、データ量が多く時間がかかる、システムを停止できる時間が少ない、といった要因があります。
 
この方針の場合、過去の購入履歴を参照するプログラムに対して改修が必要になります。
新商品購入履歴テーブルと旧商品購入履歴テーブルの両方を見る、という改修を行う必要があります。
例えば、顧客コード"0000001"の顧客が過去に商品を購入したかどうかを見たい場合、新商品購入履歴テーブルになければ旧商品購入履歴テーブルを見る、とする必要があります。
 
この方針のデメリットとしては、プログラムロジックが複雑になり、保守性が低下することが挙げられます。
もし、自動化されていない運用作業で商品購入履歴テーブルを見る必要がある場合、その作業が煩雑になることにも注意する必要があります。運用ミスの原因にもなり得ます。
 
2.データ移行する
データ移行する場合、旧商品購入履歴テーブルのデータを、新商品購入履歴テーブルに移し替える必要があります。
システムを一時的に停止し、移行用のプログラムを実行させ、旧商品購入履歴テーブルのデータを新商品購入履歴テーブルのフォーマットに合わせた形で移行します。
(失敗に備えてバックアップを取得することも重要です)
 
データ移行する際には、フォーマットをどのように合わせるのかを考える必要があります。
今回の例で言うと、移行したデータの単価に何をセットするのか、ということを考える必要があります。
移行日以前の単価を見る必要が無いのであれば、nullで問題ありません。
見る可能性があるのであれば、現在の単価(過去に単価が変動しているのであれば当時の単価)をセットする必要があります。

f:id:akira2kun:20210410183830j:plain

 

Vue.js:componentで独自タグを定義する

Vue.jsの重要な機能の一つとして、componentという機能が存在します。
これは部品化に役立ちます。
 
この機能を利用することで、独自にタグを定義できるようになり、冗長な記述を減らすことができます。
例えば、今回はサンプルコードとしてsample1.htmlとsample2.htmlを用意していますが、sample1.htmlでは同じようなdivタグの記述が続くのに対し、sample2.htmlはdivタグの記述を独自のmy-componentタグにまとめることで同じような記述が続くのを防ぐことができています。
 
【サンプルコード】
・sample1.html
<!DOCTYPE html>
<html lang="ja">
<head>
 <meta charset="UTF-8">
 <title>Vue.js - sample1</title>
 <style>
 .my-button {
  background-color: #ccc;
  text-align: center;
  border: 3px solid #000;
 }
 </style>

</head>
<body>

<div id="app">
 <div class="my-button" v-on:click="func1">
  <strong>機能:</strong>挨拶
 </div>
 <div class="my-button" v-on:click="func1">
  <strong>機能:</strong>ハローワールド
 </div>
</div>

<script src="https://unpkg.com/vue"></script>
<script>
// Vueオブジェクトの生成
var app = new Vue({
 el: "#app",
 methods: {
  func1: function() {
   alert('Hello World!')
  }
 }
});
</script>
</body>
</html>
 
・sample2.html
<!DOCTYPE html>
<html lang="ja">
<head>
 <meta charset="UTF-8">
 <title>Vue.js - sample2</title>
 <style>
 .my-button {
  background-color: #ccc;
  text-align: center;
  border: 3px solid #000;
 }
 </style>

</head>
<body>

<div id="app">
 <my-component>挨拶</my-component>
 <my-component>ハローワールド</my-component>
</div>

<script src="https://unpkg.com/vue"></script>
<script>
// コンポーネント定義("my-component"という独自タグを作成)
// templateのslotタグにはmy-componentタグで与えられた値が挿入される
Vue.component('my-component', {
 template: `
 <div class="my-button" v-on:click="func1">
  <strong>機能:</strong>
  <slot></slot>
 </div>
 `,
 methods: {
  func1: function() {
   alert('Hello World!')
  }
 }
});

// Vueオブジェクトの生成
var app = new Vue({
 el: "#app"
});
</script>
</body>
</html>
 
【実行結果】
実行結果はsample1.htmlとsample2.htmlで同じです。
以下のように2つの部品が出てきます。

f:id:akira2kun:20210404013726j:plain

 
部品をクリックするとポップアップが表示されます。

f:id:akira2kun:20210404013737j:plain

 
----
  
この記事では紹介しませんが、componentの記述を外部ファイルに切り出すことも可能です。
外部ファイルに切り出すことで、同じ部品(component)を複数ファイルで利用することができるようになります。
これについては、Webで「Vue.js components」と検索すれば情報が出てきます。
 
なお、現在はhtmlファイルをChromeで開いて実行していますが、外部ファイルのimportを行う場合はこの方法での実行は困難です。
(セキュリティ対策のため)
外部ファイルのimportの実行を試すには、Vue CLIでサーバを立てる等の対応が必要になります。

SaaS・PaaS・IaaSの覚え方

クラウドコンピューティングのサービス形態を覚えるための簡単な表を作りました。
 
SaaS・PaaS・IaaSの概要

f:id:akira2kun:20210601004417j:plain

 頭文字を取って「SPI」と覚えると、より覚えやすいかもしれません。
 ちなみに、「SPI」は就職試験で使われる有名な適性試験の名前です。
 
SaaS・PaaS・IaaSでの資源の扱い

f:id:akira2kun:20210601004434j:plain

 
----------------------
 
情報処理技術者試験に関する記事の目次
https://1drv.ms/b/s!AivF3bzWXOzuhG1Xk5hscKYqkLkM

Java:任意の順番でのソート

JavaのCollection型(サブクラスにList型等がある)は、Collections.sortメソッドでソートすることが可能です。
Collections.sortメソッドは、第一引数にソートしたいCollection型のオブジェクト、第二引数にComparator型のオブジェクトを渡します。
第二引数のComparator型のオブジェクトにより、ソート順が決まります。
 
Collections.sortメソッドでは、Comparator型のcompareメソッドを利用してソートを行います。
ソート順を定義するためには、Comparator型を実装したクラスを独自に作成し、compareメソッドをオーバーライドして独自のロジックを記述する必要があります。
compareメソッドの説明は以下です。
https://docs.oracle.com/javase/jp/8/docs/api/java/util/Comparator.html#compare-T-T-
簡単に言うと、compareメソッドの第一引数と第二引数について、戻り値がマイナスの値の場合は「第一引数→第二引数」の順番に並び、戻り値がプラスの値の場合は「第二引数→第一引数」の順番に並びます。
 
これを利用して、任意の順番でソートを行うサンプルが以下になります。
 
【サンプルコード】
・MySortMain.java
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class MySortMain {

  public static void main(String[] args) {

    // ソート対象のリストを作成する
    List<String> list = new ArrayList<>();
    list.add("メグリネ ルカ");
    list.add("ハツネ ミク");
    list.add("カガミネ レン");
    list.add("カガミネ リン");

    // ソート(文字コード順)
    Comparator<String> mySort1 = new MySort1();
    Collections.sort(list, mySort1);
    System.out.println("[文字コード順のソート]");
    for (String str : list) {
      System.out.println(str);
    }

    // ソート(独自ソート順)
    Comparator<String> mySort2 = new MySort2();
    Collections.sort(list, mySort2);
    System.out.println("");
    System.out.println("[独自ソート順のソート]");
    for (String str : list) {
      System.out.println(str);
    }

  }

}
 
・MySort1.java
import java.util.Comparator;

// 今回はString型を対象としているが、独自型にも同じ方法で対応できる
public class MySort1 implements Comparator<String> {

    @Override
    public int compare(String o1, String o2) {
      // o1の文字コードからo2の文字コードを減算した値を返すメソッド
      return o1.compareTo(o2);
    }

}
 
・MySort2.java
import java.util.Comparator;

// 今回はString型を対象としているが、独自型にも同じ方法で対応できる
public class MySort2 implements Comparator<String> {

  @Override
  public int compare(String o1, String o2) {
    return mySortOrder(o1) - mySortOrder(o2);
  }

  // 独自のソート順の定義
  public int mySortOrder(String str) {
    if (str.equals("ハツネ ミク")) { return 1; }
    else if (str.equals("カガミネ リン")) { return 2; }
    else if (str.equals("カガミネ レン")) { return 3; }
    else if (str.equals("メグリネ ルカ")) { return 4; }
    else {return 5;}
  }

}
 
【実行結果】
文字コード順のソート]
カガミネ リン
カガミネ レン
ハツネ ミク
メグリネ ルカ

[独自ソート順のソート]
ハツネ ミク
カガミネ リン
カガミネ レン
メグリネ ルカ

java:通常のクラスと匿名クラス(無名クラス)の書き方の比較

以前の記事(https://cyzennt.co.jp/blog/2020/10/16/%e7%84%a1%e5%90%8d%e3%82%af%e3%83%a9%e3%82%b9%e3%81%a8%e3%81%af/)で「通常のクラスと匿名クラスの書き方の比較を見たい」という意見があったので、少し補足を行います。
 
サンプルコードの通り、匿名クラスの場合、クラスに名前を付ける必要が無く、またオブジェクト生成と同時に処理を定義することができます。
これにより、以下のようなメリットがあります。
・記述を簡潔にできる
・使い捨てのクラスを定義でき、使い捨てであるという意図も明確にできる
・(ソースファイル名とクラス名を同じにする場合)管理が容易になる
 
【サンプルコード】
・AnonymousClass.java
public class AnonymousClass {
  private String str = "Hello Class!";
  public String getStr() {
    return str;
  }
  public void print() {
    System.out.println(getStr());
  }
}
 
・AnonymousSubClass.java
// 通常のクラスの定義(クラスの継承)
public class AnonymousSubClass extends AnonymousClass {
  public void print() {
    System.out.println("NonAnonymous:" + getStr());
  }
}
 
・AnonymousInterface.java
public interface AnonymousInterface {
  public void print();
}
 
・AnonymousImplementsClass.java
// 通常のクラスの定義(インターフェースの実装)
public class AnonymousImplementsClass implements AnonymousInterface {
  public void print() {
    System.out.println("NonAnonymous:Hello Interface!");
  }
}
 
・AnonymousMain.java
public class AnonymousMain {
  public static void main(String[] args) {

    // 通常のクラスのオブジェクトの生成(クラスの継承)
    AnonymousClass nac = new AnonymousSubClass();
    nac.print();

    // 匿名クラスの定義とオブジェクト生成(クラスの継承)
    AnonymousClass ac = new AnonymousClass() {
      public void print() {
        System.out.println("Anonymous:" + getStr());
      }
    };
    ac.print();

    // 通常のクラスのオブジェクトの生成(インターフェースの実装)
    AnonymousInterface nai = new AnonymousImplementsClass();
    nai.print();

    // 匿名クラスの定義とオブジェクト生成(インターフェースの実装)
    AnonymousInterface ai = new AnonymousInterface() {
      public void print() {
        System.out.println("Anonymous:Hello Interface!");
      }
    };
    ai.print();
  }
}
 
【実行結果】
NonAnonymous:Hello Class!
Anonymous:Hello Class!
NonAnonymous:Hello Interface!
Anonymous:Hello Interface!

情報処理技術者試験対策「共通鍵暗号方式・公開鍵暗号方式とSSL」

この記事では、暗号化が必要な理由や共通鍵暗号方式・公開鍵暗号方式の概要、SSLの仕組みの概要について書きます。
 
1.暗号化が必要な理由
ノード(端末やサーバ等、他の機器との通信を行う主体を指す)間の通信内容は、Wiresharkのようなパケットキャプチャ(ソフトウェア)で確認することが可能です。
どのような形で確認できるのかは、Googleの画像検索(https://www.google.com/search?q=Wireshark&sxsrf=ALeKk02z0RxYtT7G0O_rVmDxd3LoryWp4g:1615711689095&source=lnms&tbm=isch&biw=1366&bih=657)で調べればイメージがわきやすいと思います。
 
そして、ユーザ/パスワードのような重要な情報を送受信する際も、同じようにパケットキャプチャで通信内容を見ることができてしまいます。
もし、そのような重要な情報が平文(生データ)で送受信されていたとしたら、それはセキュリティ面で重大なリスクとなります。
例えば、ユーザ/パスワードを盗み見た第三者が、なりすましでログインすることができてしまいます。
 
それを防ぐために、「鍵」と呼ばれる通信を行う者同士であらかじめ共有されたデータを使用し、特定のアルゴリズムにより通信するデータを暗号文に変換します。
そうすることで、第三者に通信内容を盗み見られたとしてもその中から重要な情報を取得されるのを防ぐことができます。
「鍵」を持たない第三者が暗号文を見ても内容を理解できない、という所が重要です。
 
2.共通鍵暗号方式・公開鍵暗号方式の仕組み
暗号化の方式は大きく分けて二つあり、「共通鍵暗号方式」と「公開鍵暗号方式」があります。
 
2.1.共通鍵暗号方式
これは、あらかじめ2者間で共有された共通鍵を使用する方式です。
図解すると以下のようになります。

f:id:akira2kun:20210501003619j:plain
 
この方式は、方式が単純であることから暗号化・復号を早く行うことができるのが利点ですが、多くの鍵を用意する必要がでてきてしまう欠点があります。
上記の図の通りなのですが、2者の組み合わせ毎に鍵を用意する必要が出てきてしまうため、n(n-1)/2の分だけ鍵が必要になってしまいます。
(各々のノード(n)は、他の各々のノード(*(n-1))に対して鍵を用意する必要がある。ただし、2者間で別々の鍵を用意する必要はない(/2)。)
 
2.2.公開鍵暗号方式
これは、それぞれの主体が秘密鍵と共有鍵を管理し、通信を行う時に通信相手に共有鍵を渡して通信を行う方式です。
図解すると以下のようになります。

f:id:akira2kun:20210501003636j:plain
 
この方式は、少ない鍵で通信を行うことができる利点がありますが、方式が難しく暗号化・復号を早く行うことができません。
必要な鍵の数は、それぞれの主体が秘密鍵と共有鍵の2つの鍵さえ持っていれば良いので、必要なのは2nの分のみです。
 
3.SSLについて
インターネット上で通信を安全に行う仕組みとして、SSLと呼ばれる仕組みがあります。
SSLは、インターネットプロトコルであるhttpsにも使われています。
(重要な情報を送受信する時にはhttpsを使うべきであると言われたことがあると思いますが、httpsが安全な理由はSSLを使っているからです)
 
SSLでは、先で説明した共通鍵暗号方式と公開鍵暗号方式を利用しています。
また、サーバ証明書を利用するのも特徴です。
サーバ証明書とは、認証局(サーバの正当性を証明する第三者の企業)からサーバに対して付与されるものであり、通信を行うサーバが正当なものである(犯罪等に悪用されることがない)ことを証明するデータです。
仕組みは以下の通りです。

f:id:akira2kun:20210501003653j:plain
 
----------------------
 
情報処理技術者試験に関する記事の目次
https://1drv.ms/b/s!AivF3bzWXOzuhG1Xk5hscKYqkLkM

情報処理技術者試験対策「排他制御・デッドロック」

この記事では、情報処理技術者試験で問われる排他制御デッドロックについて、基礎的なことを書いていきます。
 
排他制御デッドロックを理解する前に、トランザクションについて理解する必要があります。
トランザクションとは、複数の処理をひとまとまりにしたものです。
例えば、出庫時に在庫数を更新するトランザクションは、下記の処理をひとまとまりにすることで実現できます。
・在庫テーブルから在庫量を取得する
・取得した在庫量から出庫した分を差し引く
・差し引いた在庫量を在庫テーブルに反映させる
 
このトランザクションを実行する上で注意が必要なのは、トランザクション実行中に在庫テーブルが更新されないことを保証する必要があることです。
もし、在庫量を取得した後に在庫テーブルが更新されてしまった場合、トランザクションで算出した在庫量でその更新を上書きしてしまいます。
それを防ぐために、トランザクション中で在庫テーブルにロック(排他制御)をかけ、トランザクション実行中に在庫テーブルが更新されないようにします。

f:id:akira2kun:20210501002637j:plain

 
このように更新結果の不整合を防げるのがロックなのですが、デメリットもあります。
複数のトランザクションがロックをかけた場合、複数のトランザクションがお互いにロック待ちの状態となり、トランザクションを終了できないのでロックを解除することもできず、(強制的にトランザクションを終了させるまでの間)永久にトランザクションが終了しなくなることがあります。
このような現象を、デッドロックと呼びます。

f:id:akira2kun:20210501002707j:plain
 
デッドロックを防ぐための代表的な手段として、トランザクションの間でロックする順番を統一するという手段があります。
この手段を用いることで、他のトランザクションが中途半端に進んで中途半端にロックをかけることがなくなるので、デッドロックを防ぐことができるようになります。

f:id:akira2kun:20210501002723j:plain
 
----------------------
 
情報処理技術者試験に関する記事の目次
https://1drv.ms/b/s!AivF3bzWXOzuhG1Xk5hscKYqkLkM