PythonのMySQLdbでCREATE TABLEなど

Scalaのコードで埋まるはずが今度はPythonだったりします。
MySQLサーバーを立ち上げて色々といじって遊んでいるのですが
CREATE TABLEやDROP TABLEといったDDLをアプリケーションから
実行できないか試してみました。
テーブルのCREATEをアプリケーションから実行できると
やれることの幅が広がりますね。

#!/usr/bin/python2.7.2
# -*- coding: utf-8 -*-

import sys, math, datetime
import MySQLdb

# constant
DB_HOST = "127.0.0.1"
DB_USER = "user"
DB_PSWD = "pswd"
DB_PORT = 3306

# 時刻表示
def disp_time(prc):
    systime = datetime.datetime.now().strftime(u'%Y/%m/%d %H:%M:%S')
    print prc + ' -> ' + systime

# SQL実行
def execute_sql():

    try:
        conn = MySQLdb.connect(host=DB_HOST, db="test", port=DB_PORT,\
                               user=DB_USER, passwd=DB_PSWD, charset="utf8")    
    
        cursor = conn.cursor()
        cursor.execute("DROP TABLE IF EXISTS hoge")
        cursor.execute("CREATE TABLE hoge(\
                        id INT(11) PRIMARY KEY AUTO_INCREMENT,\
                        com VARCHAR(200))")
        
    except MySQLdb.Error, e:    
        print "SQL ERROR %d: %s" % (e.args[0], e.args[1])
        sys.exit(1)
        
    finally:
        if conn: conn.close
        if cursor: cursor.close

# メイン処理部
if __name__ == "__main__":

    disp_time('start')

    execute_sql()

    disp_time('end  ')


GitHubにてPythonシリーズのリポジトリも作成してみました。
上記のコードも公開しております。


MetalPython/MySQL/mysql_ddl.py at master · cobot00/MetalPython · GitHub



今回は以下のサイトを参考にさせてもらいました。
意外とDDLを実行する例が見つからなかったので非常に助かりました。
Thanks a lot!

MySQL Python tutorial

githubはじめました

タイトルのとおり、ようやくgithubにリポジトリを作成いたしました。
アカウントはかなり前から取得していたのですが、
新しいことを始める際は最初の一歩を踏み出すのが大変でありますな。
これまで蓄えてきたものを少しずつアップしていこうと思いますが
まずは手始めにJavaのコードをば。


cobot00/JavaTraining · GitHub

Java初心者向けの簡単な実装演習集です。
JUnitのテストを解として、実装を完了させる作業を繰り返すことで
Java、およびJUnitに慣れる事を目的としています。
各パッケージ毎に1つの実装課題となるディレクトリ構成になっています。
実装→テストというサイクルを繰り返して、オールグリーンを目指します。
なお、JDK1.5以上の環境を前提としています。

・calculate
実装対象:EasyCalculator.java
テスト:EasyCalculatorTest.java
実装例:EasyCalculatorImpl.java

四則演算の実装です。
強引に例外処理も盛り込んでいます。


fizzbuzz
実装対象:FizzBuzz.java
テスト:FizzBuzzTest.java
実装例:FizzBuzzImpl.java

いわゆるFizzBuzz問題です。
約数の組合せが可変になっているので若干難易度が高くなっています。


・copy
実装対象:ListFactory.java
テスト:ListFactoryTest.java
実装例:ListFactoryImpl.java

リストの複製、およびインスタンスの識別を目標としています。


・hashtable
実装対象:HashTableConverter.java
テスト:HashTableConverterTest.java
実装例:HashTableConverterImpl.java

ListからMapを生成します。
なお、ここでの「hashtable」はコレクション実装におけるハッシュテーブルを指し、
Hashtableクラスの使用を推奨するものではありません。


・date
実装対象:EasyDate.java
テスト:EasyDateTest.java
実装例:EasyDateImpl.java

Immutableな日付クラスの実装です。
日付演算の面倒さを経験することで日付ライブラリのありがたみを実感します。

Javaのジェネリクスと継承のいい関係の模索

継承関係のあるクラス系統にジェネリクスを導入するので苦労していたのですが
Comparableに関しては以下のように書けることに気付きました。
ジェネリクスの型パラメーターには具象クラスしか指定できないという
錯覚を持っていたのがこれまでハマっていた原因でした・・・。


Comparableの実装クラスが数多く必要だがcompareToメソッドの実装は
抽象クラスだけに持たせたいといったケースになります。
ComparableのcompareToメソッドの実装を持つ抽象クラスを以下のようにします。
自分自身をComparableの型パラメーターに指定するのがポイント。
なお、JDK1.5環境で動作するコードになります。

※指摘によりcompareTo、getKeyにfinal修飾子を追加しました

public abstract class LongKeyEntity implements Comparable<LongKeyEntity> {

    private final Long key;

    public LongKeyEntity(long key) {
        this.key = Long.valueOf(key);
    }

    public final int compareTo(LongKeyEntity o) {
        return key.compareTo(o.getKey());
    }

    public final Long getKey() {
        return key;
    }
}

そのサブクラス達。

public class Son extends LongKeyEntity {
    
    public Son(long key) {
        super(key);
    }
}

public class Daughter extends LongKeyEntity {

    public Daughter(long key) {
        super(key);
    }
}

上記のような記述ならばサブクラスのインスタンス同士で比較が可能です。
抽象クラスを指定したListに一緒に入れる事もできます。
List<Object>に何でも詰め込めることを考えたら、当然ですね・・・。

import java.util.*;

public class GenericsTest {

    public static void main(String[] args) {
        compare(1L, 2L);
        compare(1L, 1L);
        compare(2L, 1L);

        setList();
    }

    private static void compare(long left, long right) {
        Son son = new Son(left);
        Daughter daughter = new Daughter(right);

        System.out.println("left = " + son.getKey() + ", right = "
                + daughter.getKey() + " -> compare result = "
                + son.compareTo(daughter));
    }

    private static void setList() {
        List<LongKeyEntity> list = new ArrayList<LongKeyEntity>();
        list.add(new Son(10L));
        list.add(new Son(5L));
        list.add(new Son(1L));
        list.add(new Daughter(4L));

        Collections.sort(list);

        for (LongKeyEntity each : list) {
            System.out.println("key = " + each.getKey());
        }
    }
}

コンパイルもとおり、実行結果は以下のようになります。


left = 1, right = 2 -> compare result = -1
left = 1, right = 1 -> compare result = 0
left = 2, right = 1 -> compare result = 1
key = 1
key = 4
key = 5
key = 10

JavaのリフレクションでprivateなMapの値を変更する

JUnitでのテストの都合でprivateなMapの値を強制的に変更する必要があったので
リフレクションを使って試しに書いてみました。
なお、JDK1.6での記述になります。

import java.lang.reflect.Field;
import java.util.Map;

public class ForcedValueSetter {

    private ForcedValueSetter() {
        // コンストラクタの隠蔽
    }

    public static void setValueToMap(Object target, String fieldName,
            Object key, Object value) {
        Class<?> refClass = target.getClass();

        try {
            Field field = refClass.getDeclaredField(fieldName);
            field.setAccessible(true);

            try {
                @SuppressWarnings("unchecked")
                Map<Object, Object> map = 
                    (Map<Object, Object>)field.get(target);
                map.put(key, value);
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }
}


以下のようなサンプルクラスでテストを行っています。

import java.util.*;

public class MapHolder {

    private Map<String, String> map = new HashMap<String, String>();

    private String[][] values = { 
            { "1", "a" },
            { "2", "b" },
            { "3", "c" } };

    public MapHolder() {
        for (String[] each : values) {
            map.put(each[0], each[1]);
        }
    }

    public String get(String key) {
        return map.get(key);
    }
}
import org.junit.Test;
import static org.junit.Assert.*;
import static org.hamcrest.core.Is.*;

public class ForcedValueSetterTest {

    @Test
    public void get() {
        final MapHolder target = new MapHolder();

        ForcedValueSetter.setValueToMap(target, "map", "1", "A");
        ForcedValueSetter.setValueToMap(target, "map", "2", "B");
        ForcedValueSetter.setValueToMap(target, "map", "3", "C");
        ForcedValueSetter.setValueToMap(target, "map", "4", "D");

        assertThat(target.get("1"), is("A"));
        assertThat(target.get("2"), is("B"));
        assertThat(target.get("3"), is("C"));
        assertThat(target.get("4"), is("D"));
    }
}

SQLでテーブルデータの一括作成、複製

多人数で同一環境を利用する、または負荷テストで大量のデータが必要となる
場合などに知っておくと便利なSQLテクニックの紹介になります。


[EMPテーブル]
ID: 整数型の列
NM: 文字列型の列

上記のようなテーブルに数千件、数万件規模のデータを登録するのは非常に面倒です。
以下のようなSQLを実行すれば1回でまとめて登録することが出来ます。
必要に応じて、接頭辞などを設定すれば規則性を持ったレコードも作成できます。
以下の例ではID列に10001~20000の範囲の値を持つレコードを作成しています。
なお、FROM句に指定するテーブルは任意で件数さえ条件に合えば何でも構いません。


・EMPテーブルに1万件のデータを一括作成するSQLの例

[Oracle]
INSERT INTO EMP
 (ID,
  NM)
SELECT
  ROWNUM + 10000, /* 他のデータと被らないように値を調整する */
  'HOGE' || trim(to_char(ROWNUM,'00000'))
FROM
  EMP /* 作成したい件数以上のレコードを保持する任意のテーブル */
WHERE
  ROWNUM <= 10000 /* 作成したい件数を設定する */

[MySQL]
INSERT INTO EMP
 (ID,
  NM)
SELECT
  @rownum := @rownum + 1,
  concat('HOGE', lpad(@rownum - 10000, 5, '0'))
FROM
  EMP, /* 作成したい件数以上のレコードを保持する任意のテーブル */
 (select @rownum := 10000) as INIT /* 変数初期化 */
LIMIT 10000; /* 作成したい件数を設定する */


次はレコードの複製です。
以下のようなSALARYテーブルに上記で作成したEMPテーブルのレコードと
対応するレコードを複製します。

[SALARYテーブル]
ID: 整数型の列(EMPテーブルのIDと対応)
SALARY: 整数型の列


・SALARYテーブルにレコードを複製するSQLの例

INSERT INTO SALARY
 (ID,
  SALARY)
SELECT
  EP.ID,
  SL.SALARY
FROM
  SALARY SL,
  EMP EP
WHERE
  SL.ID = 1 /* 複製元のレコードを特定する条件を指定する */
AND
  EP.ID BETWEEN 10001 AND 20000; /* 複製対象の条件を指定 */ 

JDK1.6におけるTimestampのvalueOfの実装変更について

TimestampクラスのvalueOfメソッドの実装がJDK1.6でstrictモードに変わっていて
酷い目にあったので仕様の詳細についてまとめてみました。
月日の部分が2桁ないとIllegalArgumentExceptionが発生します。
1~9の値の場合はゼロ埋めが必要になります。
どういうわけか1.6では年月日のパースの実装が変更されており
年月日の各要素の長さ判定が追加されています。
それぞれ文字列の長さが、4、2、2でないと
IllegalArgumentExceptionが投げられます。
時分秒については1.5以前のままで1桁でも大丈夫ですが
2桁にゼロ埋めして渡しておくのが無難ですね。

原因の特定にはこちらのサイトを参考にさせてもらっていました、感謝。

Jdk1.5と1.6でTimestampクラスのvalueOfメソッドの挙動が違う件|ヘビィ・SMD!


・JDK1.6でのvalueOfメソッドの挙動まとめ

import java.sql.Timestamp;

public class TestTimestamp {

    public static void main(String[] args) {
        doTest("2013-1-11 12:13:14");  // error
        doTest("2013-10-1 12:13:14");  // error
        doTest("2013-01-11 12:13:14"); // success
        doTest("2013-10-01 12:13:14"); // success
        doTest("2013-10-11 1:13:14");  // success
        doTest("2013-10-11 12:3:14");  // success
        doTest("2013-10-11 12:13:4");  // success
    }

    private static void doTest(String dateTimeString) {
        try {
            Timestamp converted = Timestamp.valueOf(dateTimeString);
            System.out.println(converted.toString());
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        }
    }

}

ちなみに実行すると以下のように出力されます。

convert error -> 2013-1-11 12:13:14
java.lang.IllegalArgumentException: Timestamp format must be yyyy-mm-dd hh:mm:ss[.fffffffff]
at java.sql.Timestamp.valueOf(Timestamp.java:194)
at test.TestTimestamp.doTest(TestTimestamp.java:19)
at test.TestTimestamp.main(TestTimestamp.java:8)
convert error -> 2013-10-1 12:13:14
java.lang.IllegalArgumentException: Timestamp format must be yyyy-mm-dd hh:mm:ss[.fffffffff]
at java.sql.Timestamp.valueOf(Timestamp.java:194)
at test.TestTimestamp.doTest(TestTimestamp.java:19)
at test.TestTimestamp.main(TestTimestamp.java:9)


2013-01-11 12:13:14.0
2013-10-01 12:13:14.0
2013-10-11 01:13:14.0
2013-10-11 12:02:14.0
2013-10-11 12:13:04.0

Javaのジェネリクスでハマった件について

抽象基底クラスにcompareToメソッドの実装を持たせた以下のような
クラス定義を行った場合にHogeListHolderがコンパイルエラーとなります。
原因はHogeクラスはComparableEntityの継承クラスではなく
ComparableHogeの継承クラスと判断されるからです。


・HogeListHolderがコンパイルエラーになったケース

public interface ComparableEntity<T> extends Comparable<T>

/* compareToメソッドの実装を持つ */
public abstract class ComparableHoge
       implements ComparableEntity<ComparableHoge>

public class Hoge extends ComparableHoge

/* compareToメソッドの実装を持つ */
public abstract class ListHolder<T extends ComparableEntity<T>>
       implements Comparable<T>

/* 型の不一致でコンパイルエラー */
public class HogeListHolder extends ListHolder<Hoge>


以下のようにComparableHogeにも型パラメーターを追加すれば
HogeListHolderの型にHogeが指定できるようになります。
型の判定が厳密になったので抽象クラスを導入する場合は注意が必要ですね。
継承ではなく、interfaceを駆使しろとの思し召しですかね・・・。


・修正版

public interface ComparableEntity<T> extends Comparable<T>

/* 型パラメーターを追加 */
public abstract class ComparableHoge<T extends ComparableHoge<T>>
       implements ComparableEntity<T>

/* 型パラメーターを指定する */
public class Hoge extends ComparableHoge<Hoge>

public abstract class ListHolder<T extends ComparableEntity<T>>
       implements Comparable<T>

/* コンパイルが通る */
public class HogeListHolder extends ListHolder<Hoge>