20200521のMySQLに関する記事は2件です。

ID重複例(雇用調整助成金の記事を見て)

はじめに

https://www3.nhk.or.jp/news/html/20200520/k10012437361000.html

雇用調整助成金オンライン申請に関する記事を見て、
じゃあ どんなときにIDが重複するのかを、私の経験を元にまとめました。

最大値+1

[果物マスタ]

ID 果物
1 りんご
2 みかん
3 いちご
: :
4 バナナ ( ← 同時に登録 )
4 ぶどう ( ← 同時に登録 )

この場合、最大値3+1、すなわち「4」が次のIDです。

しかし複数のプロセスが「同時」に最大値を取得すると、
両プロセスともに「4」という重複IDが割り振られてしまいます。
(一方が4、他方が5とはならない。)

コーディングにもよりますが、重複IDだけ割り振られて、
ぶどうの insert エラーをシステムが適切に処理しなかった場合、
ぶどうを登録したユーザに対して
「4:バナナ が正常に登録されました」
と、身に覚えのないメッセージが表示されるでしょう。

※「最大値+1」が悪いわけではなくて、
設計やコーディングが甘いことが問題です。

ランダム値

[果物マスタ]

ID 果物
13245 りんご
97860 みかん
82073 いちご
: :
56718 バナナ ( ← ランダム値が重複 )
56718 ぶどう ( ← ランダム値が重複 )

random_int() などで得られるランダム値をIDにする設計も、
たまに見かけます。これも重複IDが発生し得ます。

マスタを同時刻に登録しようとして、偶然、同じランダム値が
生成される可能性は、ゼロではありません。

IDの頭に「年月日時分秒」を付けても同じです。
同時刻に同じランダム値が生成されれば、意味がありません。

ID使いまわし + 未削除データ

前述の「最大値+1」の場合ですが、
「4:バナナ」を削除したら、次回 登録時、
再度「4」が割り振られてしまいます。

[果物マスタ(親)]

ID 果物
1 りんご
2 みかん
3 いちご
4 ぶどう ( ←「4:バナナ」を削除後 登録 )

[生産地マスタ(子)]

ID 果物
1 青森
2 和歌山
3 栃木
4 フィリピン ( ← 未削除データのため「山梨」を登録できない!)

このとき共通IDの別テーブルに、
例えば果物マスタの子テーブル「生産地マスタ」に、
バナナの生産地「4:フィリピン」が うっかり消されずに残っていたら、
ぶどうの生産地が登録できないかもしれません。

まとめ?

主キー、排他制御、トランザクション、AUTO_INCREMENT、シーケンス等を
システムに合わせて設計して、重複しないID生成を心がけましょう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UbuntuにxamppインストールからのJDBCの動作確認

はじめに

WebサーバとJDBCの仕組みを勉強するために,xamppを使って自分でつくってみたときの手順です.
Ubuntuでやってる人が少ないのか,同じようなエラーに遭遇した人が少なかったので記事にしてみました.
勉強用に作っているので,あえて統合開発環境Eclipseは使用していません.

バージョン等情報

仮想マシン:VMware Workstation 15 Player
ゲストOS:Ubuntu 18.04LTS
xammp:7.4.5

xamppのインストール

xamppは下記サイトでインストーラーをダウンロードし,適当にインストールしてください.
何も困ることはないはず.
https://ja.osdn.net/projects/xampp/

インストールが完了すると,/optのなかにlammpフォルダが出来上がっているはずです.
下記コマンドを入力するとApacheとMySQLとProFTPDが立ち上がります.

$ sudo /opt/lampp/lampp start

ちなみに停止するときは,

$ sudo /opt/lampp/lampp stop

です.

ウェブブラウザのアドレスに「localhost」と入力し,立ち上げたWebサーバに入れることを確認します.

入れない場合の対処法
下記nmapコマンドで,ポート80番が開いているか確認してみてください.

$ nmap localhost

開いてなければ,Ubuntuのファイアウォールではじかれてる可能性が高いです.
下記ufwコマンドでポート80番(http)を開放し,再度nmapコマンドで空いたかどうか確認してください.

$ sudo ufw allow 80

ちなみに,Ubuntuはデフォルトでポート80番は閉じてます.
80番ポートを開放すると,セキュリティリスクが上がるので注意してください.

データベースの作成

次にテスト用のデータベースを作成します.

ウェブブラウザで「UbuntuのIPアドレス」にアクセスし,右上のphpMyAdminに入り,
rootでログインします.

「ユーザアカウント」タブを開き,「ユーザアカウントを追加する」を選択します.
「ユーザ名」「ホスト名」「パスワード」を入力し,
今回はこのように設定しました.
ユーザ名:piyo
ホスト名:localhost
パスワード:piyopass

「ユーザアカウント専用データベース」欄の「同名のデータベースを作成してすべての特権を与える.」にチェックを入れたのち,一番下の「実行」ボタンを押して,アカウントを生成します.
これで,アカウントとアカウントと同名のデータベースが作成できました.

とりあえずテスト用のデータを入力していきます.
テーブル名:book
カラム:(id(int型),name(VARCHAR型),price(int型),data(DATE型))

挿入タブを選択し,適当にデータを入れます.
下記SQLコマンドはそのときのコマンドです.
ウェブブラウザのインターフェースで挿入できるので,下記コマンドを直接入力する必要はありません.

INSERT INTO `book` (`id`, `name`, `price`, `date`) VALUES ('1', '1st_chapter', '777', '2020-05-21');

MySQL設定

次に,MySQLにアクセスできるように設定します.

まずは,ファイアウォールの設定で,ポート3306番(MySQL)を開けます.

$ sudo ufw allow 3306

次に,MySQLのskip-networkingオプションをoffにします.
ホームディレクトリに.my.mcnfファイルを作成し,以下の2行のテキストを入力します.

~/.my.cnf
[mysqld]
skip-networking = off

これがかなり重要だったのですが,skip-networkingオプションをoffにしないと,3306番ポート経由でアクセスできません.
ちなみにMySQLのバージョンによって設定ファイルのデフォルトの設定などが違うそうで,かなり苦労しました.
ちなみに設定ファイルの作り方は下記ファイルに書いてありました.

/etc/mysql/my.cnf
#
# The MySQL database server configuration file.
#
# You can copy this to one of:
# - "/etc/mysql/my.cnf" to set global options,
# - "~/.my.cnf" to set user-specific options.
# 
# One can use all long options that the program supports.
# Run program with --help to get a list of available options and with
# --print-defaults to see which it would actually understand and use.
#
# For explanations see
# http://dev.mysql.com/doc/mysql/en/server-system-variables.html

# This will be passed to all mysql clients
# It has been reported that passwords should be enclosed with ticks/quotes
# escpecially if they contain "#" chars...
# Remember to edit /etc/mysql/debian.cnf when changing the socket location.

# Here is entries for some specific programs
# The following values assume you have at least 32M ram
!includedir /etc/mysql/conf.d/

MySQLの設定を読み込ませるため,xamppごと再起動します.

$ sudo /opt/lampp/lampp restart

ポート3306番が開いているか確認します.

$ netstat -lt

作成したアカウントで入れるか試します.
下記コマンドでSQLにログインします.
パスワード入力を求められますので入力します.

$ /opt/lampp/bin/mysql -u piyo -h localhost -p

きちんとログインできなければ,アカウントの設定を見直してください.
ログインした状態で,下記コマンドを入力します.

SHOW VARIABLES LIKE 'skip_networking';

表示結果が下記のようにskip_networkingがoffになっていたら,設定完了です.

+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| skip_networking | OFF   |
+-----------------+-------+

これでSQL側の準備は完了です.

JDBCの設定

次にJDBCの準備をします.
まずはaptコマンドで必要なライブラリをインストールします.

$ sudo apt-get install libmysql-java

javaのコンパイル時にmysql.jarが読み込まれるように,CLASSPATHに追加しときます.

$ echo "export CLASSPATH=$CLASSPATH:/usr/share/java/mysql.jar" >> ~/.bashrc

コンパイル時に毎回-cpオプションしてもいいのですが,面倒くさいのでbashに追加しときました.
下記のサンプルコードを用意します.

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

public class test{
    public static void main(String[] args){

        final String URL = "jdbc:mysql://localhost:3306/piyo";//ホスト名:localhost データベース名:piyo
        final String USER = "piyo";//ユーザ名
        final String PASS = "piyopass";//パスワード
        String SQL = "select * from book;";//SQL分(データベースbookを選択)

        //JDBCがインポートできているか確認
        try{
                Class cls = Class.forName("com.mysql.jdbc.Driver");
                System.out.println("JDBC sucess");
        }catch(ClassNotFoundException e){
                e.printStackTrace();
                System.exit(1);
        }

        //JBDCを使用してデータベースにアクセス
        try(
            Connection conn = DriverManager.getConnection(URL, USER, PASS);//SQLにログイン
            PreparedStatement ps = conn.prepareStatement(SQL)){//SQL文の実行
            try(ResultSet rs = ps.executeQuery()){//rsにデータベース情報を格納
                while(rs.next()){//選択カラムの更新
                    System.out.println(//読み込んだカラムの表示
                        rs.getInt("id") + " " +
                        rs.getString("name") + " " +
                        rs.getString("price") +" "+
            rs.getDate("date"));
                }           
            }catch(SQLException e){//データベース情報の取得エラー
                e.printStackTrace();
                System.exit(1);
            }
        }catch(SQLException e){//ログインもしくはSQL分の実行エラー
            e.printStackTrace();
            System.exit(1);
        }finally{
            System.out.println("finish");//何事もなければ実行
            System.exit(0);
        }
    }
}

コンパイル&実行します.

$ javac test.java
$ java test

ここで,
java.sql.SQLException: No suitable driver found
エラーが発生した場合は,/usr/share/java/mysql.jarが読み込みされていない可能性があるので,
もう1度CLASSPATHを見直してください.
下記のように出力されればOKです.

JDBC success
1 1st_chapter 777 2020-05-21
finish

まとめ

Ubuntu環境でxmappをインストールし,JDBCでSQLコマンドを叩き,結果を取得することができました.
今後はJDBCを使ってブラウザに結果を表示させたりしたいですね.

謝辞

bld1509様
UbuntuにJDBCをインストールする際に助かりました
https://bld1509.hatenadiary.org/entry/20080624/1214317382

itdevats様
skip-networkingのトラブルシューティングでお世話になりました.
https://qiita.com/itdevat/items/ca5184dc8445380b966e

ITSakura様
サンプルコード作成の参考になりました.
https://itsakura.com/java-mysql

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む