okinawa

IT勉強メモ

React基礎

書き途中。

重要部分の抜粋

React扱うなら最低限覚えておきたい部分。

※1、クラスコンポーネントもあるが、ここでは扱わない。

参考サイト

とてもわかりやすい上に、仕組みまで解説してくれる素晴らしい教材だった。
www.udemy.com

サーバー起動方法の忘れ用メモ

下記コマンドを実行する。

cd c:\Users\ユーザー名\Downloads\react-guide-material\react-guide-material\06_control_and_form\src\130_reminder

npm install

npm start

JavaScript基本

ES modulesは別ファイルの読み込み

JavaScriptファイルから別のJavaScriptファイルを読み込む仕組み。

https://www.codegrid.net/articles/2017-es-modules-1/

・エクスポート

export { 関数名1,関数名2,変数名1,変数名2,・・・ } //1ファイルからいくつでもエクスポート可能な記法

default export 関数名や変数名 // 1ファイルから1つだけエクスポートする記法

・インポート

import { 関数名1,関数名2,変数名1,変数名2,・・・ } from "ファイルのパス"//defaultじゃない方

import 関数名や変数名 from "ファイルのパス" //defaultをインポート

オブジェクト、プロパティとは

JavaScriptのオブジェクトはプロパティの集合です。プロパティとは名前(キー)と値(バリュー)が対になったものです。

オブジェクトとは?

// プロパティを持たない空のオブジェクトを作成
const obj = {};

// プロパティを持つオブジェクトを定義する
const obj = {
    // キー: 値
    "key": "value"
};

プロパティとは?

// 
const obj = {
    // ↓これがプロパティ。キーと値がセットになったもの
    key: "value"
};

参考
オブジェクト · JavaScript Primer #jsprimer

メソッドと関数の違い

メソッドは一言で表すと、オブジェクトが持っている関数のこと。

参考
【JavaScriptの入門】関数とメソッド | ワードプレステーマTCD

コールバック関数

コールバック関数とは、引数に渡された関数のこと。

function outLog(callbackFunc) {
  const result = callbackFunc(3);//引数で渡されたコールバック関数を呼び出す
  console.log(result);
}

function multiply(num) {//これが引数に渡される関数。コールバック関数。
  return num * 2;
}

debugger;// ここで処理が止まる便利機能
outLog(multiply);//引数に関数を渡す

map filter

mapは配列の全要素を1つずつ取り出す。for文と同じ。

filterは配列の全要素から条件に合致したものだけ取り出す。

const array = [1,2,3,4,5];

for(let i = 0; i < array.length; i++) {
  console.log(array[i] * 2);
}

//上記のfor文をmapで書く
array.map(val => val * 2);//引数valに配列の全要素が1個ずつ入ってくる
console.log(array.map(val => val * 2));

for(let i = 0; i < array.length; i++) {
  if (array[i] >= 3) {
     console.log(array[i] * 2);
  }
}

//上記のfor文をfilterで書く
array.filter(val => val >= 3);//引数valに配列の全要素が1個ずつ入ってくる
console.log(array.filter(val => val >= 3));

//map filterの複合
const array = [1,2,3,4,5];
array.map(val => val * 2).filter(val => val >=6);//2倍したもので6以上をreturnする
console.log(array.map(val => val * 2).filter(val => val >=6));// 6 8 10

・書き方

map(第1引数=配列の全要素, 第2引数(省略可)=index値, 第3引数(省略可)=配列 => 処理内容);

filter(第1引数=配列の全要素, 第2引数(省略可)=index値, 第3引数(省略可)=配列 => 処理内容);

array.map(val, index, array => { return /*処理内容*/ });

array.filter(val, index, array => { return /*処理内容*/ });

分割代入

分割代入とは配列の要素を1個ずつ変数に代入すること。

//配列
const array = [1,2,3]:
const [a, b, c] = [1,2,3);
const [d, , f} = [1,2,3];//d=1,f=3,eはなし
console.log(d);
console.log(f);

//オブジェクト
const{bbb, aaa} = {aaa:"オブジェクトaaa", bbb:"オブジェクトbbb"};
console.log(aaa);//オブジェクトaaa
console.log(bbb);//オブジェクトbbb

Promise(非同期処理)とは?

resolve()するとthen()の処理に移る。

reject()するとcatch()の処理に移る。

qiita.com

Promiseでのエラーハンドリング

reject()をcatchしないと処理は停止するらしい。(コンソールにエラー文は出る)

ja.javascript.info

React基礎

JSX構文

JavaScriptの構文を拡張したもの。

HTMLに似た構文がコード内で使用できる。

JSX構文で書くとBABELによってReactのcreateElement関数に変換される。→さらにJavaScriptのオブジェクトに変換される。

JSX → createElement関数 → JavaScriptオブジェクト、と変換されていく。

//JSX構文
import React from "react";

const Example = () => {
  return (
    <h1 className="abc">Hello!</h1>
  );
};
console.log(Example());
export default Example;

//BABELで変換後
const Example = React.createElement("h1", {
  className: "abc"
}, "Hello!");

// JavaScriptオブジェクトに変換後
const Exapmle {
  type: h1
  props: {
    chlderen: 'Hello!'
    className: 'abc'
  }
}

コンソール画面

Babel変換サイト↓
Babel · Babel

コンポーネント

画面はコンポーネントという部品でできている。

コンポーネント入れ子可能。

再利用可能。コードが使い回せる。

可読性の向上。変更したいコンポーネント内だけ見ればいいので。

疎結合になる。コンポーネントごとにテスト可能。ただし、 コンポーネント毎の依存度を低くしないとだめっぽい。

コンポーネントJavaScriptの関数として定義する。関数名の頭文字は大文字。 関数コンポーネントと言う。

新規プロジェクトの作成

コマンドプロンプトで下記を実行。

npx create-react-app プロジェクト名 //プロジェクト作成1
npx create-react-app プロジェクト名 --template redux-typescript //プロジェクト作成2 ReduxとTypeScript入りならこっち

npm start //サーバー起動&プロジェクト実行(作成したプロジェクト内にcdコマンドで移動してから)

npm run eject //隠しファイルを表示

pacage.jsonのscriptsに各コマンドの詳細が載っている。

新規プロジェクトの作成2(こっちの方が一般的らしい)

npm create vite@latest

npm install //作成したプロジェクト内にcdコマンドで移動してから)

npm run dev //サーバー起動&プロジェクト実行

CSSの当て方

  • 1,import CSSファイルのパス
  • 2,コンポーネント関数の中でクラス名、ID等を指定。
  • 3,あとはCSS側でクラス名やID等を指定するだけ。

コンポーネントの分け方

Fragment(JSX)

不要なタグを追加したくないときに使う。

JSXでreturnする要素は、1つのルート(最上位)要素でreturnする必要がある。

例えばdiv要素が最上位に2つあるとエラーになる。

最上位に複数の要素を作りたい、かつ不要なタグを追加したくない時にFragmentで囲う。

 <div>aaa</div>
 <div>bbb</div>//これだとエラー

<Fragment>
 <div>aaa</div>//Fragmentで囲えばOK
 <div>bbb</div>
</Fragment>

Fragmentには、クラス名やID名は付けられず。 key属性だけ付けられる。

JSX内でJavascriptを書く

{ }内に式を書くとJavaScriptの式として評価される。

ちなみに式はOKで文はダメ。

const Expression = () => {
    const title = "Expression"
    const array = ['item1','item2','item3']
    const hello = (arg) => `${arg} Function`;
    const jsx = <h3>Hello JSX2</h3>//JSXのオブジェクト扱い
    const bool = true;

    return (
        <div className="expression">
            <h3>Hello {title.toLowerCase()}</h3>
            <h3>{array}</h3>
            <h3>{hello('Hello')}</h3>
            {/* 画面に表示されない*/}
            {<h3>Hello JSX</h3>}
            {jsx}
            {bool}
        </div>

    )
}
式と文の違い

式は、何らかの値を返すもの(変数に代入できるもの)。

文は、変数宣言、for文、if文やセミコロンで区切るもの。

JSXの{ }内で使用できるのは式のみ。

const num = 1; //1は変数に代入できるので式
const a = if(num === a) //if文は変数に代入できないので文
const bool = num === a '一致' : '不一致' //三項演算子の結果は変数に代入可能。式。

props(他のコンポーネントに値を渡す)

ほかコンポーネントに渡す値のことをpropsという。引数のようなイメージ。

特徴1
コンポーネントから子コンポーネントに値を渡す。
基本的には親→子だけ可能で、子→親に値を渡すことは出来ない。

特徴2
propsは読み取り専用。
親から子に渡された値(props)を子側で変更するのは不可。
constと同じで再代入不可ということ。

キーワード:分割代入
分割代入は、オブジェクトからプロパティを取り出す機能。

コンポーネント(Example.js)↓

import Child from "./components/Child";

const Example = () => {
    const hello = (arg) => `Hello ${arg}`;
    // const o = {
    //     color: "red",
    //     num: 123
    // }
    return (
        <>
            <Child 
                // POINT propsには全てのタイプの値を渡すことができます。
                // {...o}
                color="blue"
                fn={hello}
                bool
                obj={{ name: 'Tom', age: 18 }}
            />
            <Child color="red" />
        </>
    )
};

export default Example;

コンポーネント(Child.js)

import "./Child.css";

const Child = ({color: c = 'green', num, fn, bool, obj}) => {//ここでExample.jsから渡された値を受け取る。=の後はデフォルト値(なくてもよい)。分割代入と言う。
  return (
    <div className={`component ${c}`}>
      <h3>Hello Component</h3>
      <h3>{num}</h3>
      <h3>{fn('Props')}</h3>
      <h3>{ bool ? 'true' : 'false'}</h3>
      <h3>{ obj.name + obj.age }</h3>
    </div>
  );
};

export default Child;
props.childerenとは

コンポーネントを丸ごと別のコンポーネントに渡したい時に使う。

今回の例だとContainerコンポーネントにProfileコンポーネントを渡している。

画像だとContainerが金色の枠線。Profileが中身で、これがchildrenに当たる。↓

children

Example.js
Container.jsにProfile.jsのプロパティを渡している。

import Profile from "./components/Profile";
import Container from "./components/Container";

const profile = [
  { name: "Takashi", age: 19, country: "Japan" },
  { name: "Jane", age: 28, country: "UK", color: "red" },
];

const Example = () => {
  return (
    <div>
      {/* 書き方1 */}
      <Container title="Childrenとは?" >
        <Profile {...profile[0]}/>
        <Profile {...profile[1]}/>
      </Container>

      {/* 書き方2 */}
      <Container title="Childrenとは?" children={
        [
          <Profile key={profile[0].name}{...profile[0]}/>,
          <Profile key={profile[1].name}{...profile[1]}/>
        ]
      }

      // 書き方3(分けて渡したいとき用)
      children2={<Profile key={profile[0].name}{...profile[0]}/>}
      children3={<Profile key={profile[1].name}{...profile[1]}/>}
      />
    </div>
  );
};

export default Example;

Container.js
Profile.jsのプロパティをExample.jsから受取。

import "./Container.css";

const Container = ({ title, children, children2, children3 }) => {
  return (
    <div className="container">
      <h3>{title}</h3>
      {children}
      {children2}
      {children3}
    </div>
  );
};

export default Container;

Profile.js
Container.jsに渡される子コンポーネント

import "./Profile.css";

const Profile = ({ name, age, country, color }) => {
  return (
    <div className={`profile ${color}`}>
      <h3>Name: {name}</h3>
      <p>Age: {age} </p>
      <p>From: {country}</p>
    </div>
  );
};

export default Profile;

イベントと状態

イベント

書き方の基本

イベント名={ 処理内容 }

const Example = () => {

  const clickHandler = () => {
    alert('ボタンクリック');
  }


  return (

    <button onClick={clickHandler}>クリックしてね</button>
    <div>
      <label>
        入力値のイベント:
        <input
          type="text"
          onChange={() => console.log("onChange検知")}
          onBlur={() => console.log("onBlur検知")}
          onFocus={() => console.log("onFocus検知")}
        />
      </label>
    </div>
  );
};

export default Example;
状態(state)

状態というのは何らかの値のこと。

値を保持する。変数とは別物。

・基本の書き方

import { useState } from 'react';
//useStateは[ 状態用変数、状態更新用関数 ]を返す。
const [状態用変数、状態更新用関数] = useState(/*引数=状態用変数の初期値*/);

・サンプル

import { useState } from 'react'
const Example = () => {
  //useStateは[ 状態用変数、状態更新用関数 ]を返す
  const [count, setCount] = useState(0);//状態用変数countの初期値0で呼び出し
  const clickButton = () => {
    return setCount(count + 1);//状態更新用関数setCountで状態用変数countを更新する
  };
  return (
    <>
      <p>クリック回数:{count}</p>
      <button onClick={clickButton}>+</button>
    </>
  );
};

export default Example;
変数と状態の違いは?

状態を変更すると、画面が再描画される。

画面更新(F5)しても値が保持されるのが特徴。

ReactでのStateと変数の違いと使い分け | MaxaBlog

状態の更新は即時ではない

状態の更新をまず予約し、再レンダリング後に状態が確定する。

状態更新を予約 → 再レンダリング → 状態確定

例:ボタンを押すと数字をプラス1される
setcount(count + 1);を2つ書いているので、プラス2されそうだが、実際にはされない。
なぜなら、状態変数countは初期値で、下記の流れで処理されるから。

count + 1(状態更新を予約。この時点ではcount=0なので、0+1で予約はcount=1)
count + 1(状態更新を予約。この時点ではcount=0なので、0+1で予約はcount=1)
Exampleコンポーネント実行(再レンダリング)
countが1に確定(状態確定。count=1)

つまり「0 + 1」を2回実行していることになる。

import { useState } from "react";

const Example = () => {
    const [count, setcount] = useState(0);
    const countUp = () => {
        setcount(count + 1);
        setcount(count + 1);//プラス2されそうに見えるがされない
        // setcount(prevCount => { return prevCount + 1 }); ←こう書かないとプラス2されない
    };

  return(
    <>
        <p>現在のカウント数: {count}</p>
        <button onClick={countUp}>+</button>
        
    </>
  )
};

export default Example;
オブジェクト型&配列の状態(state)変更時の注意点

1.オブジェクト型のステート変更する時は全プロパティを変更する

2.オブジェクト型&配列のステート変更時は、新しいオブジェクトを渡す

const Example = () => {
    const humanObj = { name: "John", age: 20 };
    const [human, setHuman] = useState( { name: "John", age: 20 })

    const numArray = { 1,2,3 };
    const { nums, setNums } = useState(numArray);
  };


  const changeName = (e) => {
    //1.オブジェクト型のステート変更する時は全プロパティを変更する
    setHuman({ name: e.target.value  })//ageプロパティがないのでダメ
    setHuman({ name: e.target.value, age: human.age })//OK
    setHuman({ ...human, name: e.target.value,  }) //スプレッド演算子。プロパティが多い時はこれが便利。

    //2.オブジェクト型のステート変更時は、新しいオブジェクトを渡す
    human.name = e.target.value;
    setPerson(human);//humanオブジェクトを使いまわしているのでダメ
    setHuman({ name: e.target.value, age: human.age })//OK

    //2.配列のステート変更時は、新しいオブジェクトを渡す
    nums = [ 4,5,6 ];
    setNums(nums);//nums配列を使いまわしているのでダメ
    setNums([ ...nums ]) //OK。スプレッド演算子。nums配列の中身を展開している。
  }
ReactでgetElementBy~が使われないのなぜ?

多分ステートがあるから。

例えば、textareaの入力をspanにコピーする場合もステートで十分。

state使用例

import { useState } from "react";
const Example = () => {

  const [textArea, setTextArea] = useState("");

    //テキスト入力をステートにセット
    const changeTextArea = (e) => {
      setTextArea(e.target.value);
    }
 
  return (
    <>
// テキストエリアの入力をspanにコピー
      <p><textarea value={textArea} onChange={changeTextArea}></textarea></p>
      <p><span>{textArea}</span></p>
    </>
  );
};

export default Example;
状態(state)特徴・注意点まとめ

ここらへんの仕組みは参考教材の動画がわかりやすいので要参照。

■特徴

  • ステートが変わると再レンダリングされる(重要!!!)
  • ステートは再レンダリング後も保持。F5で画面全体再更新すると消える。
  • ステートはコンポーネント毎に保持
  • ステートはpropsで渡せる
  • ステートを更新する時は更新用関数を使う

■注意点

  • コンポーネントの中で呼び出す
  • if/for文の中で呼び出さない
  • 状態更新は即時ではない(予約)
  • オブジェクト型のステート変更する時は全プロパティを変更する
  • オブジェクト型と配列のステート変更時は、新しいオブジェクトを渡す
  • コンポーネントが消えるとステートも初期化される
【重要】Stateが変わると再レンダリングされるってどういうこと?

テキスト入力すると、stateが変わる。

stateが変わる度に再レンダリング、つまりExample関数コンポーネントが再実行されている!

ここは覚えておきましょう。

import { useState } from 'react';

//Stateが更新されるとExample関数が再実行される
const Example = () => {
  const [state, setState] = useState("");

// state更新関数
  const setText = (e) => {
    setState(e.target.value);
  }

  return (
    <>
    // state更新関数をonChangeで呼び出し
      <input type="text" onChange={setText} />
      <p>{state}</p>
    </>

  );
};
【重要】逆にStateが変わらないと、値が変わっても画面に反映されない

↑コンソールではnonStateの値更新が確認できるが、画面には反映されず0のまま。

import { useState } from 'react';

//Stateが更新されるとExample関数が再実行される
const Example = () => {
  const [state2, setState2] = useState(0);

// state更新関数
  const countUp2 = () => {
    console.log(1 + state2 + ` = state2`)
    setState2(prev => prev + 1);
  }
  
// 普通の変数更新関数
  let nonState = 0;
  const countUp = () => {
    nonState += 1
    console.log('nonState = ' + nonState)
    return nonState;
  }

  return (
    <>
      <p>{state2}</p>
      <button onClick={countUp2}>state+1</button>
      <p>{nonState}</p>
      <button onClick={countUp}>nonState+1</button>
    </>

  );
};

ちなみに、nonState更新 → state2更新

で再レンダリングしてもnoStateが反映されないのは、state2更新の度にExample 関数全体が実行されるので、

let nonState = 0;

で初期化されるから。

Stateは再レンダリング後も保持されるので画面に反映される。

普通のJavaScriptとは感覚が違うのねえ。

制御構文とフォームの制御

配列をリスト表示

map関数を使う。

const numArray = [1, 3, 5]

// 中略
return (
<ul>
 {numArray.map((num) => <li>{num}</li>)}
</ul>
);
リストにはkeyをつける

ここは文字だと分かりづらいので、参考教材の動画を要参照。

keyを付けないと警告が出る↓

console

・keyを付ける理由

keyをつけないとリストの一部だけ更新した場合でも、リストの要素が全て更新されてしまうから。

Reactは要素ツリーの差分を検出して、DOMを更新している。

しかし、リストにkeyをつけないと要素を特定できないため、全更新になる。

・keyの注意点

keyには一意の値をつける。(同一ツリー内で一意ならOK)

keyにはなるべくIndex値をつけない。

const numArray = [1, 3, 5]

// 中略
return (
<ul>
 {numArray.map((num) => <li key={num}>{num}</li>)}//ここでkey
</ul>
);
色々なif条件(if && ?? 3項演算子)

&&はtrueの場合のみ、&&の右の値を表示。

??はnullかudefinedなら、??の右の値を表示。それ以外なら変数の値をそのまま表示。

const Example = () => {
  const flag = true;
  const hoge = "入っています";
  const hoge2 = undefined;
  return (
    <>
      <p>{flag && "true"}</p>//&&記法
      <p>{flag ? "true" : "false"}</p>//三項演算子
      <p>{hoge ?? "null or undefined"}</p>//??記法。入っています
      <p>{hoge2 ?? "null or undefined"}</p>//??記法。null or undefined
    </>
    );
  };

ReactJSのJSX内で条件式の書き方を3つ紹介【使いやすい順に解説】 #React - Qiita

input要素

基本的にはHTMLと同じ。

import {useState} from "react";
const Example = () => {
  const [text, setText] = useState();
  return (
    <div>
      <label htmlFor="aaa">ラベル</label>
      <p>
        <input
          id="aaa"
          placeholder="入力サンプル"
          // value="aaa"
        />
      </p>
    </div>
  );
};

export default Example;

DockerでJava・Apatche・Tomcat・MySQL環境を構築

構成図

参考サイト

ここらへんのサイトを眺めながら作業すると理解度が高まる。

Apache(httpd)とTomcatの連携
ApacheTomcatはhttpではなく、AJPというプロトコルで通信する
Apache httpd と Tomcat を連携させる方法 |
エーフロンティア|ApacheへのHTTPリクエストをTomcatに転送する
Dockerコンテナ(Apache+Tomcat)の設定ファイルを作成

TomcatMySQLの接続
Tomcat 8.0系でのデータソース定義 - 俄
Apache Tomcat 8 (8.0.53) - JNDI Datasource HOW-TO

・Dockerのnetworkを固定IPで作成
docker-compose 固定IPアドレスを指定

Dockerのネットワーク、基礎理解は万全ですか?【Dockerコンテナ・グレートジャーニー④】 #Docker - Qiita

大まかな流れ

Apache(httpd)とTomcatの連携
httpd.confを編集してAJPを有効にする。
Tomcatのserver.xmlを編集してAJPを有効&HTTPを無効にする。

TomcatMySQLの接続
JDBCTomcatに配置&context.xmlを編集してDB接続を有効にする。

・docker-compose.yml&Dockerfile作成

・テスト用のJavaプログラム作成

・docker compose実行で終了

Apache(httpd)とTomcatの連携

  1. httpdTomcatAJPで通信できるようにするため、設定ファイルに変更を加える。
  2. 変更後の設定ファイルをホストマシンにコピーする(docker composeで変更後の設定ファイルでコンテナを作るため)

httpdの設定ファイル変更
設定ファイルはhttpdコンテナの下記にある。
/usr/local/apache2/conf/httpd.conf

httpd.confに下記の変更を加えてAJPを有効にする&Tomcatの所在を伝える

# 下記2行を探してコメントアウトを解除。AJPを有効にする。
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_ajp_module modules/mod_proxy_ajp.so

# 2行を追加。場所はどこでもOK。httpdへのアクセス全てをTomcatへ転送する。
ProxyPass / ajp://172.30.0.3 # Tomcatのパス。別サーバならIPアドレス。
ProxyPassReverse / ajp://172.30.0.3 #Tomcatのパス。別サーバならIPアドレス。

変更が終わったら、httpd.confをコピーしてホストマシンの下記に配置。(後でhttpdコンテナにコピーする用)
C:\Users\username\Documents\dockertest\apa\httpd.conf

Tomcatの設定ファイル
設定ファイルはTomcatコンテナの下記にある。
/usr/local/Tomcat/conf/server.xml

<!-- 下記の行をコメントアウト解除&編集 -->
    <!--
    <Connector protocol="AJP/1.3"
               address="::1"
               port="8009"
               redirectPort="8443"
               maxParameterCount="1000"
               />
    -->

↓編集後

    <Connector protocol="AJP/1.3"
               address="0.0.0.0"
               port="8009"
               redirectPort="8443"
               secretRequired="false"
               maxParameterCount="1000"
               />

server.xmlの変更点2↓

<!-- 下記の行を探してコメントアウトする。HTTP通信は使わないので -->
    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443"
               maxParameterCount="1000"
               />

変更が終わったら、server.xmlをコピーしてホストマシンの下記に配置。(後でTomcatコンテナにコピーする用)
C:\Users\username\Documents\dockertest\tomcat\server.xml

TomcatMySQLの接続

MySQLJDBC配置とTomcat設定ファイルの変更が必要。

JDBC配置
1,MySQL :: MySQL コネクタからダウンロード。JDBC Driver for MySQLのPlatform Independentを選択する。

2,ダウンロードしたらJDBCのjarファイルを下記に配置する。(後でTomcatコンテナにコピーする用)
C:\Users\username\Documents\dockertest\tomcat\mysql-connector-j-8.2.0.jar

・Tomcaとの設定ファイル変更
変更する設定ファイルはTomcatコンテナの下記にある。
/usr/local/Tomcat/conf/context.xml

<!-- 下記を追加する -->
 <Resource name="jdbc/MyDB" auth="Container"
          type="javax.sql.DataSource"
          maxTotal="100" maxIdle="30" maxWaitMillis="10000"
          username="root" password="rootpass"
          driverClassName="com.mysql.cj.jdbc.Driver"
          url="jdbc:mysql:172.30.0.4:3306/test?allowPublicKeyRetrieval=true&useSSL=false"/>
     <!-- url="jdbc:mysql:MySQLのパス。別サーバーならIP:ポート番号/DB名 -->

変更したらcontext.xmlをコピーして、ホストマシンの下記に配置する。(後でTomcatコンテナにコピーする用)
C:\Users\username\Documents\dockertest\tomcat\context.xml

Dockerイメージ・コンテナ用のファイル作成

作業は、イメージ作成のためにDockerfile作成と、コンテナ作成のためにdocker-compose.ymlを作成する。

docker-compose.ymlの作成

ファイル名は必ずdocker-compose.ymlで作成する。

ホストマシンの下記に配置する。
C:\Users\username\Documents\dockertest\docker-compose.yml

version: '3'
services:
#コンテナ名。apacheのコンテナ
  apachetest:
#Dockerfileからイメージ作成。パスはdocker-compose.ymlからの相対パス
    build: ./apa
#Docker内の仮想ネットワーク名。LANを作るイメージ
    networks:
      apatommynet:
        ipv4_address: 172.30.0.2
# ホストマシンからコンテナへの接続ポート指定。ホストの8080に接続があったらコンテナの80に接続する
    ports:
      - 8080:80
  tomcattest:
    build: ./tomcat
    networks:
      apatommynet:
        ipv4_address: 172.30.0.3
# ホストマシンの保存領域とコンテナの保存領域を同期する。ホストのwebappsフォルダとコンテナのwebappsフォルダを同期
    volumes:
      - C:\Users\username\Documents\dockertest\tomcat\webapps:/usr/local/tomcat/webapps
  mysqltest:
    image: mysql
    networks:
      apatommynet:
        ipv4_address: 172.30.0.4
    volumes:
      - mysqlvolume01:/var/lib/mysql
# 環境変数の指定
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
      MYSQL_DATABASE: testdb
      MYSQL_USER: user
      MYSQL_PASSWORD: userpass
# ネットワーク作成
networks:
  apatommynet:
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 172.30.0.0/16
          gateway: 172.30.0.1

# ボリューム作成
volumes:
  mysqlvolume01:

※追記:ApacheTomcatMySQLと通信するから固定IPにしたが、コンテナ名でもコンテナ間通信はできるのかも?

コンテナ間通信については下記が参考になる。
qiita.com

Dockerfileの作成

Dockerfileは拡張子なしで「Dcokerfile」で作成する。

Dckerfileを元にイメージが作られる。

Apachehttpd)用

# ベースとなるイメージ
FROM httpd

# ローカルのhttpd.confファイルをコンテナの/usr/local/apache2/conf/へコピー
COPY httpd.conf /usr/local/apache2/conf/

作成後、下記に配置する。
C:\Users\username\Documents\dockertest\apa\Dockerfile

Tomcat

# ベースとなるイメージ tomcatのバージョン9。バージョンはwarファイルと合わせる
FROM tomcat:9

# ローカルのserver.xmlファイルをコンテナの/usr/local/tomcat/conf/へ上書きコピー
COPY server.xml /usr/local/tomcat/conf/
COPY context.xml /usr/local/tomcat/conf/
COPY mysql-connector-j-8.2.0.jar /usr/local/tomcat/lib

tomcatのバージョンについて
Eclipseでwarファイルをエクスポートするときに、下記の画面が出るので、Tomcatコンテナのバージョンはここと合わせる。

Eclipseエクスポート

作成後、下記に配置する。
C:\Users\username\Documents\dockertest\tomcat\Dockerfile

Javaプロジェクト作成

Eclise

TestTest.java

import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class TestTest
 */
@WebServlet("/TestTest")
public class TestTest extends HttpServlet {
    private static final long serialVersionUID = 1L;

    /**
    * @see HttpServlet#HttpServlet()
    */
    public TestTest() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
    * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
    */
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        String result = "";
        try (Connection con = DriverManager.getConnection(
                "jdbc:mysql://172.30.04:3306/testdb?allowPublicKeyRetrieval=true&useSSL=false",
                "root",
                "rootpass");
             PreparedStatement pstmt = con.prepareStatement("SELECT * FROM sys.sys_config;");
             ResultSet rs = pstmt.executeQuery()) {
            // 検索結果を表示
            while (rs.next()) {
                result = rs.getString("variable");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

        request.setAttribute("message", result);

        RequestDispatcher dispatcher = request.getRequestDispatcher("/jsp/index.jsp");
        dispatcher.forward(request, response);
    }

    /**
    * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
    */
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // TODO Auto-generated method stub
        doGet(request, response);
    }

}

index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
Hello!World!
 <h1>メッセージ: <%= request.getAttribute("message") %></h1>
</body>
</html>

docker compose実行

下記コマンドを実行。

docker compose -f C:\Users\username\Documents\dockertest\docker-compose.yml up -d

実行後、warファイルを作成して下記に配置する。
C:\Users\username\Documents\dockertest\tomcat\webapps\ApatcheTomcatTest.war

しばらくすると自動でコンパイルされて、クラスファイル等が作られる。

そしたら「http://localhost:8080/ApatcheTomcatTest/TestTest」にアクセス。

この画面が出たら成功↓メッセージ:の後にSELECT結果が表示される。

【Java】 WSL上のDocker内のMySQLにホストマシンから接続する方法

接続イメージ

ポイント

WSLがIPアドレスを持っているので、HostNameをWSLのIPアドレスにする必要がある。

ホストPCでMySQLのjarファイルを準備

※ホストPCでのJavaの環境構築は終わっている前提

下記サイトで、JDBC Driver for MySQL (Connector/J)をダウンロード。(Platform Independent)
MySQL :: MySQL コネクタ

Javaプロジェクトのlibフォルダにjarファイルを格納。クラスパスを通す。

MySQLのDockerコンテナ作成

docker run --name mysql000ex11 -dit -p 3305:3306 -e MYSQL_ROOT_PASSWORD=myrootpass -e MYSQL_DATABASE=wordpress000db -e MYSQL_USER=wordpress000kun -e MYSQL_PASSWORD=wkunpass mysql --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --default-authentication-plugin=mysql_native_password

・-p 3305:3306の説明
-p ホスト側ポート番号:コンテナ側ポート番号

ホスト側ポート番号は未使用のものなら何でもOK。

コンテナ側ポート番号は3306。MySQLは3306を使用しているので。

ホストマシンの3305に接続があったらDockerコンテナの3306に接続しますよ、ということ。

ホスト側のWorkBenchから接続確認

上記だと接続できないのでWSLのIPアドレスを調べる。(HostnameをWSLのIPアドレスに変更する必要あり)

WSLのIPアドレスを調べる

WSLはホストPCとは別のIPアドレスを持つため、ホストからWSL上のDockerコンテナ内のMySQLにアクセスするためには、WSLのIPアドレスが必要。

コマンドプロンプトで下記コマンドを打つ。

> wsl #wsl起動
> ip a #ipアドレス表示

eth0の inetの部分がWSLのIPアドレス

改めて、WorkBenchで接続確認。Test Connectionで成功したらOK。

JavaからMySQLに接続テスト

package main;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class Main {

    public static void main(String[] args) throws SQLException {
        // TODO 自動生成されたメソッド・スタブ
        // データベースに接続
        Connection con = DriverManager.getConnection(
          "jdbc:mysql://WSLのIPアドレス:3305/DB名?useSSL=false",
          "root",
          "rootパスワード"
        );
        // SQL文の実行
        PreparedStatement pstmt = con.prepareStatement("SELECT * FROM sys.sys_config;");
        ResultSet rs = pstmt.executeQuery();
        // 検索結果を表示
        while (rs.next()) {
          System.out.println(rs.getString("variable"));
        }
        // 後処理(リソースのクローズ)
        rs.close();
        pstmt.close();
        con.close();
    }

}

・結果
SELECT結果が返ってきた!成功!

diagnostics.allow_i_s_tables
diagnostics.include_raw
ps_thread_trx_info.max_length
statement_performance_analyzer.limit
statement_performance_analyzer.view
statement_truncate_len

Dockerの基本

参考書籍

めちゃくちゃわかりやすくて感動した。

Linux系の用語解説もあってよかった。

サポートサイト(AWSでのDocker構築あり)
https://book.mynavi.jp/supportsite/detail/9784839972745.html

参考サイト集

Dockerで環境構築する時はここを見ながらやる。

docker-compose.ymlの参考サイト

日本語公式がある!
docs.docker.jp

Dockerfileの参考サイト

こちらも公式
docs.docker.jp

Dockerコマンド一覧の参考サイト

公式!
docs.docker.jp

Docker Network(コンテナ間通信)の参考サイト

qiita.com

久しぶりにdocker-compose.yml書くと忘れてしまうのがnetwork。

networkに所属していればコンテナ名だけで通信可能。

Dev Container参考サイト

Dev ContainerはVsCode拡張機能

qiita.com

dodosu.hatenablog.jp

メチャクチャ便利だが、まだ紹介記事が少ない。

DockerとVsCodeをインストールすれば、あとはyml・Dockerfile・devcontainer.jsonだけで環境構築できてしまう。流行ってほしい!

サーバは遠隔操作できるPC

Dockerではないんだけど、この本を読んでサーバに対するイメージが変わったのでメモ。

何となくサーバには苦手意識というか、得体のしれないものという感覚があった。

でも、家で使っているPCと大して変わらないのだ。

PCに入っているOS(LinuxOS)が違うのと、サーバ用のソフト(ApatcheやNginx)がインスコされているってだけなのよね。

ほんでサーバに置いてあるファイルにインターネット経由でアクセスできる。

大体のサーバソフトはhtdocsディレクトリというのが公開用ディレクトリ。

ここにHTMLやCSSなどを置く。

Dockerのコンテナも大雑把に言えば1つのPCとみなせる。

なのでコンテナにApatcheを入れればWebサーバに、DBを入れればDBサーバに、Ubuntuを入れればGUI操作できる普通のPCみたいに扱えるのかな?

動作環境

LinuxOSの上でしか動かせない。

なぜならコンテナの中にLinuxOSぽいものがあり、コンテナ外のLinuxOSのカーネルとやり取りするから。

コンテナ内もLinuxOS対応ソフトしか使えない。

つまりLinuxマシンの中に、さらに隔離されたLinux環境を作る。

今回はWindows11のWSL2を使用。

カーネルとは

カーネルとはLinuxOSの核の部分。

周辺部分がディストリビューションUbuntuRedHatCentOsなど。

ディストリビューションRed Hat系とDebian系)

RedHat
Red Hat Enterprise Linux
CentOS
Fedora

DebianDebian
Ubuntu

イメージとは

CD/DVD等のインストールファイルみたいなもの。

イメージからコンテナが作られる。

Docker Hubとは

イメージの配布サイト。

OSっぽいもの + Apatche + MySQLみたいなよくある開発環境が色々配布されている。

Dockerfileでイメージ作成

Dockerfileはイメージ作成用のコマンドが記述されたファイルのこと。

dockerfileコマンドというのが用意されている。

Dockerfileのファイル名は拡張子なしで「Dockerfile」

・Dockerfileの記述例
FROM イメージ名
COPY コピー元 コピー先

下記は、Apatcheのイメージ利用する。
index.htmlをコンテナ内の/usr/local/apatche2/htdocs/にコピーする↓

FROM httpd
COPY index.html /usr/local/apatche2/htdocs/

・Dockerfileからイメージをビルド

docker build -t コンテナ名 Dockerfileのパス

コンテナのライフサイクル

下記の一連の流れがライフサイクル。

コンテナを 作る→起動する→停止する→破棄する→作る→

コンテナ内のソフトをアップデートすることはせず、コンテナ破棄して新しいのを作り直すのが一般的らしい。

バンバン作っては破棄を繰り返すものだそうで。

マウント(ホストとコンテナ接続)

コンテナ外部、つまりホストマシンの保存領域とコンテナを接続することをマウントと言う。

マウントしておけばコンテナ内のデータをホストに保存できる。

開発だとソースファイルなどは全てマウントして外部に保存しておく。

コンテナを破棄してもマウントしたデータは無事。

マウント対象の保存領域のことをボリュームという。

マウントはDocker用語ではなくて、取り付けるという意味で、USBメモリを接続することもマウント。

ボリュームマウント・バインドマウント

ボリュームマウントはDocker Engineが管理している領域内にボリュームを作成して保存する。
手軽に扱えるが、直接操作しづらい。

バインドマウントはホストマシンのディレクトリやファイルに保存する。
普通のファイルと同じように扱えるので、頻繁に編集したいファイルはここに置く。

docker volume create ボリューム名(ボリュームマウント)
docker run (省略) -v ホストの記憶領域パス:コンテナの記憶領域パス(バインドマウント)

dockerコマンドの文法

peguinという名前のコンテナを起動。

docker 上位コマンド 副コマンド 対象名
docker container run penguin

※containerを省略して「run penguin」でもOK

・container:上位コマンド(12種類)
対象の種類

・run:副コマンド
動詞

・penguin:対象(名前)
目的語

オプションと引数

penguinというコンテナをバックグラウンドで(オプション)モード1(引数)で起動。

docker container run -d peguin --mode=1

・-d:オプション
オプションはコマンドに対して、細かい設定をするもの。
オプションはまとめて書ける。
-d -i -tをまとめて-dit。

・mode=1:引数
対象に持たせたい値を書く。
文字コードの指定やポート番号指定など。

docker run

docker runは以下3つをまとめて実行してくれる。

docker image pull

docker container create

docer container start

デーモンとは

UNIXLinuxで動くプログラムで、常に裏で動き続けるもののこと。

メーラーデーモンとか。

コンテナと通信する

デフォルトではコンテナは外部との通信ができない。

そのため、外部通信可能なコンテナを作成する。

apa000ex2という名前のApatche入りコンテナを、ポート番号8080(ホスト)から80(コンテナ内のApatche)へ接続できるようにして作成。

docker run --name apa000ex2 -d -p 8080:80 httpd

httpd:Apatcheのイメージ

どんな感じで通信するのか

container内のApatcheはポート80で通信を待機している。

しかし、未設定だとコンテナは通信を受け付けない。

そのため、ホストマシン(ポート番号は8080でなくても良い)からポート80番で通信しますよと設定する。

container同士の仮想ネットワーク

異なるcontainerを接続する仮想ネットワークを作る。

docker network create ネットワーク名

↓作ったネットワークに接続するコンテナを作成する

docker run --name test_container --net ネットワーク名

ファイルコピー(コンテナ⇔ホスト)

docker cp ホスト側パス コンテナ名:コンテナ側パス(ホストからコンテナ)
docker cp コンテナ名:コンテナ側パス ホスト側パス(コンテナからホスト)

コンテナ内でbashを使う(コンテナ内部に入る)

docker execでbash使用モードに入る。

exitでbash使用モードを終了する。

docker exec -it コンテナ名 /bin/bash

echo "Hello World!"

exit

Docker compose(一括命令)

構築に関わるコマンド文を定義ファイルに書き込んで、一括実行する。

拡張子はyaml yml

ファイル名は「docker-compose.yml」

Docker composeは1つのフォルダに1つしか置かない。複数置きたいときはフォルダ分ける。

YAML Ain't Markup Language。YAMLマークアップ言語ではないの略らしい。

Dockerfileはイメージを作るもの。Docker composeは命令の集合。

docker-comose.ymlの記述例

MySQLWordpressの環境構築。

version: "3"
services:
  mysql000ex11:
    image: mysql:5.7
    networks:
      - wordpress000net1
    volumes:
      - mysql000vol11:/var/lib/mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: myrootpass
      MYSQL_DATABASE: wordpress000db
      MYSQL_USER: wordpress000kun
      MYSQL_PASSWORD: wkunpass
  wordpress000ex12:
    depends_on:
      - mysql000ex11
    image: wordpress
    networks:
      - wordpress000net1
    volumes:
      - wordpress000vol12:/var/www/html
    ports:
      - 8085:80
    restart: always
    environment:
      WORDPRESS_DB_HOST: mysql000ex11
      WORDPRESS_DB_NAME: wordpress000db
      WORDPRESS_DB_USER: wordpress000kun
      WORDPRESS_DB_PASSWORD: wkunpass
networks:
  wordpress000net1:
volumes:
  mysql000vol11:
  wordpress000vol12:

Docker Composeのコマンド

docker compose -f yamlファイルのパス up -d (コンテナ作成&起動)
docker compose -f yamlファイルのパス down (コンテナ破棄。ボリュームとイメージは残る)

初歩英語でコミュニケーション2

初めて放送大学の面接授業を受けた。

生徒同士でクイズ形式で問題を出すのは面白いし、勉強になった。

発音は対面で聞くのが一番だなあ。

とても良かったので、今後も面接授業を受けたいと思いつつ、土日が完全に潰れるのはつらくて悩ましい。

発音

Rは舌を引くだけ。丸めない。舌を上に付けないら行。
日本人からすると曖昧な音になる感じ。

Lはら行より舌を先端に付ける。歯の裏。

Fは唇を挟む。無声音。

文法

動詞の変形

write + 3単現 = writes
write + 2単現= write
write + 3単過= wrote
write + 3単過完 = written

↓否定・疑問文は全て原形

Dose+ 3単現 = write
Do + 2単現= write
Did + 3単過= write
Had + 3単過完 = write

DoesやDidなどの助動詞が動詞の変形を肩代わりする。
だから助動詞と呼ばれる!
is am are can will may shall mustも同様。

am is are は「~は」ではない

~はではない。

A (am is are) B

A = B ということ

その他

no he isn't 使わない

Is he students?
→No, he isn't.(ほぼ使わず)
→No. he's not.(こっちが普通)

ニュアンスが微妙に違うらしい。

pleaseは語尾が無難

語尾につけるのが無難。

頭に付けると若干上から目線らしい。
上司が言うなら自然なようで。

ライティングは簡単日本語にしてから英語

脳内で思い浮かんだ日本語をそのまま英訳するのは困難。

いったんシンプルな日本語にしてから英語に変換する。

willとgoing toの微妙な違い

will:今思いついた未来

going to:しっかり決めた未来や予定

設計メモ

IPAの設計ガイドブックを読んだので、設計時の注意事項を書いてみた。

参考

IPA。障害未然防止のための設計知識の整理手法ガイドブック https://www.ipa.go.jp/archive/files/000058073.pdf

書き方サンプル

### 機能・処理(デバイス・機器・プロセス)
機能名 or 処理名は必須。デバイス・機器・プロセスは任意。

### アニメーション表示(Android)
遅延を入れないと結果だけが即表示される。

文字列処理

文字列の長さ。NULL。空白。

半角カタカナ・全角カタカナ混在(DB)

全角カタカナでLIKE検索すると半角カタカナの濁点・半濁点がヒットしない問題 - okinawa

数値処理

数値以外の混入。負の値。小数点。0除算。

小数点処理

切り上げ、切り捨て、四捨五入。

SQLJavaの切り上げ差異。(FLOOR関数とBigDecimal)

金額処理

税額計算。「単品×税×個数」と「全商品合計金額×税」とは異なる。
1個ずつ税額計算するか、全部まとめて税額計算するかの違い。

・例
97円2個=194円
→ 194
1.08=209.52(切捨て209円)

97円1.08=104.76(切捨て104円)
→104円
2個=208円

日付

日付の桁数。

日付Formatが違わないか。

スラッシュ入りか無しか。

月末は29~31のどれか。

閏年

ファイル・ディレクト

本当にファイル・ディレクトリか?

ファイル・ディレクトリ未存在。

名前が変わる可能性は?

誰かが開いてないか?

中身が空。0KB。

アクセス制限。readonlyとか。

既に存在するファイル名で作って、上書き保存は不可。
→abc.csvが既にあり、Javaで新たにabc.csv作って保存は無理。
→abc.csvに追記して保存なら可能。

file.close()しないと削除できない。

CSV

列数合わないエラー。

PDF

PDFBoxというApacheのライブラリあるよ。

マスタテーブル

マスタテーブルの値が変更になったときの影響は?(例:請求先コード)

マスタテーブルの値をトランザクションテーブルに登録→その後マスタだけ変更すると?

ダイクストラ法

放送大学の問題解決の数理で学んだアルゴリズムの覚書。

ダイクストラ法とは

2頂点間の最短距離を求める。

重みがマイナスだと使えない。

計算量

O(頂点数2) (オリジナルの場合)

O((頂点数+辺の数)* log頂点数) (優先度付きキューの場合)

※厳密な計算量はWikipediaを参考にすること。

処理概要

  1. 全ての頂点に、始点からの距離無限大を仮で書き込む。(初期化)
  2. 始点に、始点からの距離0を書き込む。(初期化)
  3. 次に行ける頂点に対して、始点からの距離を計算、現在書き込まれている距離より短ければ更新する。
  4. 始点からの距離が最も小さい&未訪問の頂点を選び、その頂点に行く。
  5. 未訪問頂点がまだあれば、3~4を繰り返す。
  6. すべての頂点が訪問済になったら終了。

ソースコード

グラフ

N=6
visited=[False]*N
#始点からの距離。初めは全頂点に無限値にする
startFromDistance=[math.inf]*N 

#(頂点番号,重み)のタプルを持つグラフ
G=[[(1,1),(2,5)]
   ,[(0,1),(2,4),(3,2)]
   ,[(0,5),(1,2),(4,2)]
   ,[(2,3),(5,3)]
   ,[(2,1),(5,4)]
   ,[]]

def dijkstra(G, vertex):
  heap=[(0,0)]#重み、頂点番号(優先度付きキュー。最も重みの軽い頂点から優先で取り出す)
  startFromDistance[0] = 0

  while len(heap) > 0:
    distance, now = heapq.heappop(heap)
    
    if visited[now]:
      continue
    visited[now] = True

    for next, weight in G[now]:
      if visited[next]:
        continue

      # nowから到達可能な頂点の距離更新
      newDistance = startFromDistance[now] + weight
      if newDistance < startFromDistance[next]:
        startFromDistance[next] = newDistance

        #次の行き先候補を格納
        heapq.heappush(heap, (newDistance, next))
  return startFromDistance

print(dijkstra(G, 0)) #[0, 1, 5, 3, 7, 6]

定式化

最短経路問題を定式化する。

頂点集合(V)={0 1 2 3 4 5}

辺集合(Ei,j)={(0,1), (0,2),(1,0),(1,2),(1,3),(2,0),(2,1),(2,4),(3,2),(3,5),(4,2),(4,5)}

重み集合(Wi,j)=W01=1, W02=5, W10=1, W12=2, W13=2, W20=5, W21=4, W24=2, W32=3,W35=3, W42=1, W45=4

決定変数(Xi,j)
「辺(i, j) ∈ E」 が最短路に含まれる場合、Xi,j=1

「辺(i, j) ∈ E 」が最短路に含まれない場合、Xi,j=0

移動距離Zは

始点sの出入

終点tの出入

始点と終点以外の地点の出入り

Xijは0か1をとる  Xij ∈ {1,0} for (i, j) ∈ E

決定変数が0か1をとる問題を整数最適化問題と呼ぶ。

定式化例題

グラフ
画像のグラフを最短経路問題として定式化する。

・目的変数

Z(移動距離)= X01 + 5X02 + X10 + 2X12 + 2X13 + 5X20 + 4X21 + 2X24 + 3X32 + 3X35 + 4X45

・制約条件

(X01 + X02) -(X10+X20) = 1   始点sの出入り

(X42 + X45) - X24 = -1     終点tの出入り

(X10 + X12+X13) - (X01+X21) = 0 頂点1の出入り

(X20+X21+X24) - (X02 + X12 + X32 + X42) = 0 頂点2の出入り

(X32 + X35) - X13 = 0  頂点3の出入り

-(X35 + X45) = 0  頂点5の出入り

XIJ >= 0 for (i, j) ∈ E  Xijは0か1をとる

参考

Dijkstra's Algorithm

ダイクストラ法の解説とPythonでの実装例 - PyDocument

www.youtube.com