2014年4月29日 星期二

Guide to Network Programming

今天讀信時在 Tsung's Blog 看到他介紹這本免費電子書 "Guide to Network Programming", 還有繁體中文版網頁 :

http://beej.netdpi.net/

剛好現在鑽研 Java 網路程式, 正惡補以前不屑一顧的 TCP/IP, 真是及時雨啊.

不過這本是用 Linux C 語言寫的, 不是 Java. 但是看看翻譯版中的網路觀念也非常有幫助.


Java 資料庫存取效能評比

完成 Java 資料庫存取測試後, 今天要對 Access(MDB), Derby, 以及 MySQL 這三個資料庫作效能評比. 方式很簡單 但可能不專業, 就是建一個資料表, 填入 5000 筆資料, 然後刪除全部資料, 最後刪除資料表, 計算所費時間與記憶體. 計算時間差的方法參考 :

# Java 複習筆記 : 日期與時間 (續)

具體方式是先手動建立一個資料庫 test, 然後用下列程式新增一個資料表 users, 內有兩個欄位 : id 與 name, 然後用一個迴圈寫入 5000 筆資料, 再用一個迴圈刪除這 5000 筆資料, 最後刪除 users 資料表, 測試是在我的 ACER D260 小筆電上進行的, 所以效能數據比較差, 下面是測試 Access 資料庫的程式 :

import java.sql.*;
import java.util.*;
public class AccessLandmark {
  public static void main(String[] args) {
    long start=System.currentTimeMillis();
    long mem1=Runtime.getRuntime().freeMemory();
    JT.connectMDB("test.mdb");
    String col="id AUTOINCREMENT PRIMARY KEY,name VARCHAR(30)";
    System.out.println("建立資料表 users : " + JT.createTable("users",col));
    for (int i=0; i<5000; i++) {
      JT.runSQL("INSERT INTO users(name) VALUES('USER" + i + "')");
      }
    System.out.println("插入資料 : OK");
    for (int i=0; i<5000; i++) {
      JT.runSQL("DELETE * FROM users");
      }
    System.out.println("刪除資料 : OK");
    System.out.println("刪除資料表 users : " + JT.dropTable("users"));
    JT.closeDB();
    long end=System.currentTimeMillis();
    long mem2=Runtime.getRuntime().freeMemory();
    System.out.println("耗費時間 : " + (end-start) + " ms");
    System.out.println("耗費記憶體 : " + (mem1-mem2) + " bytes");
    }
  }

執行三次結果 :

E:\Java\JT>java AccessLandmark
建立資料表 users : true
插入資料 : OK
刪除資料 : OK
刪除資料表 users : true
耗費時間 : 17078 ms
耗費記憶體 : 1461024 bytes

E:\Java\JT>java AccessLandmark
建立資料表 users : true
插入資料 : OK
刪除資料 : OK
刪除資料表 users : true
耗費時間 : 16625 ms
耗費記憶體 : 1461056 bytes

E:\Java\JT>java AccessLandmark
建立資料表 users : true
插入資料 : OK
刪除資料 : OK
刪除資料表 users : true
耗費時間 : 16906 ms
耗費記憶體 : 1460928 bytes

ACCESS 平均耗時 17 秒, 使用了 1.5MB 記憶體. 數據穩定, 變化不大.

下面是 Derby 資料庫的測試程式 :

import java.sql.*;
import java.util.*;
public class DerbyLandmark {
  public static void main(String[] args) {
    long start=System.currentTimeMillis();
    long mem1=Runtime.getRuntime().freeMemory();
    JT.connectDerby("test");
    String col="id INT NOT NULL GENERATED ALWAYS AS IDENTITY CONSTRAINT " +
                    "primary_key PRIMARY KEY,name VARCHAR(30)";
    System.out.println("建立資料表 users : " + JT.createTable("users",col));
    for (int i=0; i<5000; i++) {
      JT.runSQL("INSERT INTO users(name) VALUES('USER" + i + "')");
      }
    System.out.println("插入資料 : OK");
    for (int i=0; i<5000; i++) {
      JT.runSQL("DELETE FROM users");
      }
    System.out.println("刪除資料 : OK");
    System.out.println("刪除資料表 users : " + JT.dropTable("users"));
    JT.closeDB();
    long end=System.currentTimeMillis();
    long mem2=Runtime.getRuntime().freeMemory();
    System.out.println("耗費時間 : " + (end-start) + " ms");
    System.out.println("耗費記憶體 : " + (mem1-mem2) + " bytes");
    }
  }

執行三次結果 :

E:\Java\JT>java DerbyLandmark
建立資料表 users : true
插入資料 : OK
刪除資料 : OK
刪除資料表 users : true
耗費時間 : 281578 ms
耗費記憶體 : 10135448 bytes

E:\Java\JT>java DerbyLandmark
建立資料表 users : true
插入資料 : OK
刪除資料 : OK
刪除資料表 users : true
耗費時間 : 254437 ms
耗費記憶體 : 7158168 bytes

E:\Java\JT>java DerbyLandmark
建立資料表 users : true
插入資料 : OK
刪除資料 : OK
刪除資料表 users : true
耗費時間 : 281875 ms
耗費記憶體 : 11584512 bytes

Derby 平均耗時 270 秒, 使用記憶體 9MB, 簡直是龜速, 而且數據較不穩定.

最後是 MySQL, 測試程式如下 :

import java.sql.*;
import java.util.*;
public class MySQLLandmark {
  public static void main(String[] args) {
    long start=System.currentTimeMillis();
    long mem1=Runtime.getRuntime().freeMemory();
    JT.connectMySQL("test","root","mysql","localhost");
    String col="id INT NOT NULL AUTO_INCREMENT PRIMARY KEY," +
                     "name VARCHAR(30)";
    System.out.println("建立資料表 users : " + JT.createTable("users",col));
    for (int i=0; i<5000; i++) {
      JT.runSQL("INSERT INTO users(name) VALUES('USER" + i + "')");
      }
    System.out.println("插入資料 : OK");
    for (int i=0; i<5000; i++) {
      JT.runSQL("DELETE FROM users");
      }
    System.out.println("刪除資料 : OK");
    System.out.println("刪除資料表 users : " + JT.dropTable("users"));
    JT.closeDB();
    long end=System.currentTimeMillis();
    long mem2=Runtime.getRuntime().freeMemory();
    System.out.println("耗費時間 : " + (end-start) + " ms");
    System.out.println("耗費記憶體 : " + (mem1-mem2) + " bytes");
    }
  }

執行三次結果 :

E:\Java\JT>java MySQLLandmark
建立資料表 users : true
插入資料 : OK
刪除資料 : OK
刪除資料表 users : true
耗費時間 : 8688 ms
耗費記憶體 : 6042344 bytes

E:\Java\JT>java MySQLLandmark
建立資料表 users : true
插入資料 : OK
刪除資料 : OK
刪除資料表 users : true
耗費時間 : 8468 ms
耗費記憶體 : 6011544 bytes

E:\Java\JT>java MySQLLandmark
建立資料表 users : true
插入資料 : OK
刪除資料 : OK
刪除資料表 users : true
耗費時間 : 8640 ms
耗費記憶體 : 6011712 bytes

MySQL 平均 8.6 秒, 使用記憶體 6MB, 效能最優.

總之, 純 Java 打造的 Derby 效能實在慘不忍睹, 花的時間是 ACCESS 的 15 倍, MySQL 的 31 倍, 使用的記憶體是 ACCESS 的 6 倍, 所以只適合做測試練習用, 要用在公司專案簡直是噩夢. ACCESS 花的時間是 MySQL 的兩倍, 雖小輸 MySQL, 但還可接受, 堪用啦.


2014年4月28日 星期一

Java 資料庫存取 : 使用 MySQL

處理完 Java 存取 ACCESS 與 Derby 資料庫的測試, 還有一個常用的資料庫就是 MySQL. 學 PHP 時都會安裝 Apache+PHP+MySQL 套件, 因此電腦裡應該都有 MySQL 伺服器在跑, 如果沒有刻意關掉的話, 一開機就已經開啟服務, 所以相當於有一個現成的資料庫可用, 而且附有 phpMyAdmin 這個好用的 GUI 介面, 用瀏覽器就能管理資料庫. 我想測試完 MySQL 後做個效能評比. 以下所用到的 JT 類別方法詳見之前文章, 此處不再重複 :

# Java 資料庫存取 : 使用 ACCESS
# Java 資料庫存取 : 使用 ACCESS (續)
# Java 資料庫存取 : 使用 Derby
# Java 資料庫存取 : 使用 Derby (續)

MySQL 資料庫的操作與 Derby, ACCESS 類似, 但有一些 SQL 方言上的差異, 例如 ACCESS 刪除記錄是用 DELETE * FROM, 而 Derby 與 MySQL 則不需星號. 因此我想直接修改 JT.java 類別增加 connectMySQL() 連線方法如下 :

  public static void connectMySQL(String db, String user, String pwd){  //預設 localhost 
    connectMySQL(db, user, pwd, "localhost");
    }
  public static void connectMySQL(String db, String user, String pwd, String host) {
    try {
      //Class.forName("com.mysql.jdbc.Driver");  //不指定亦可
      String dsn="jdbc:mysql://" + host + ":3306/" + db;
      conn=DriverManager.getConnection(dsn, user, pwd);
      stat=conn.createStatement();
      }
    catch (Exception e) {System.out.println(e);}
    }

這裡我是利用安裝 AppServ 套件時所安裝的 MySQL 來用, MySQL 埠號為 3306, 因此只要將連線字串指向 Localhost 下的 3306 port 即可. 還有一點是 MySQL 安裝時需設定帳號密碼, 連線時也必須傳入帳號密碼, 因此此處不需為預設無帳號密碼多加一個方法多載. 反倒是 MySQL 伺服器不一定位於本機, 因此要多加一個參數 host, 預設為 localhost.

首先進入 phpMyAdmin 介面新增一個資料庫 testdb :

CREATE DATABASE `testdb` DEFAULT CHARACTER SET big5 COLLATE big5_chinese_ci;

這樣就可以在 testdb 新增資料庫了. 跟 Derby 一樣, MySQL 不允許先建立一個無欄位的空資料表 (只有 ACCESS 可以這樣), 也就是說下列這指令在 MySQL 無法執行 :

CRETATE DATABASE users

出現下列錯誤 :

com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: A table must have at
least 1 column

至少須先指定一個欄位, 其餘欄位再用 ALTER TABLE 來添加, 也就是我們的 JT.addField() 方法. 在 ACCESS 中常用的資料型態在 MySQL 中都有, 而且還更多, 但我們常用的還是 ACCESS 中的那幾個, 參見 :

# CREATE TABLE Syntax

在新建資料表時, 主要的 SQL 語法差異有兩個, 一是通常作為 id 流水號的自動增量主鍵, 在 MySQL 要這樣設 :

id INT NOT NULL  AUTO_INCREMENT PRIMARY KEY

下列範例中, 我們在

import java.sql.*;
import java.util.*;
public class mysql1 {
  public static void main(String[] args) {  
    JT.connectMySQL("testdb","root","mysql","127.0.0.1");
    String col="id INT NOT NULL AUTO_INCREMENT PRIMARY KEY";
    System.out.println(JT.createTable("users",col)); //第一個資料表 users
    System.out.println(JT.addField("users","name","VARCHAR(20)"));
    System.out.println(JT.addField("users","gender","CHAR(1)"));
    System.out.println(JT.addField("users","height","FLOAT")); 
    System.out.println(JT.addField("users","age","SMALLINT")); 
    System.out.println(JT.addField("users","birthday","DATE"));
    System.out.println(JT.addField("users","remark","LONGTEXT"));
    col="id INT NOT NULL AUTO_INCREMENT PRIMARY KEY";
    System.out.println(JT.createTable("books", col)); //第二個資料表 books
    System.out.println(JT.addField("books","name","VARCHAR(50)"));
    try {
      ResultSet rs=JT.runSQL("SELECT * FROM users");
      ResultSetMetaData md=rs.getMetaData();
      int columns=md.getColumnCount();
      System.out.println("欄位名稱\t資料型態\t顯示大小\t自動增量\t可不填");
      for (int i=1; i<=columns; i++) {
        System.out.print(md.getColumnLabel(i) + "\t");
        System.out.print(md.getColumnTypeName(i) + "\t");
        System.out.print(md.getColumnDisplaySize(i) + "\t");
        System.out.print(md.isAutoIncrement(i) + "\t");
        System.out.println(md.isNullable(i));
        }
      }
    catch (Exception e) {System.out.println(e);}
    System.out.println(Arrays.deepToString(JT.getTables()));
    JT.closeDB();
    }
  }

執行結果 :

true
true
true
true
true
true
true
true
true
欄位名稱        資料型態        顯示大小        自動增量        可不填
id      INT     11      true    0
name    VARCHAR 20      false   1
gender  CHAR    1       false   1
height  FLOAT   12      false   1
age     SMALLINT        6       false   1
birthday        DATE    10      false   1
remark  VARCHAR 1073741823      false   1
[books, users]

SQL 方言的第二個差異是儲存較長文字的欄位, 在 ACCESS 為 MEMO (65535 字元), 在 Derby 為 LONG VARCHAR, 在 MySQL 則有 LONGTEXT (短一些用 MEDIUMTEXT 或 TINYTEXT), 但我試過用 Derby 的 LONG VARCHAR 也可以, 字元長度為 8388607, 與 MEDIUMTEXT 一樣大.

資料表建好後, 接下來就可以插入紀錄了 :

import java.sql.*;
public class mysql2 {
  public static void main(String[] args) {
    JT.connectMySQL("testdb","root","mysql");
    String SQL="INSERT INTO users(name,gender,age,height,remark) VALUES " +
                         "('愛咪','女',12,157,'Amy'),('彼得','男',14,171,'Peter')," +
                         "('凱莉','女',16,165,'Kelly')";

    System.out.println(JT.runSQL(SQL)); //輸出 null
    try {
      ResultSet rs=JT.runSQL("SELECT * FROM users");
      ResultSetMetaData md=rs.getMetaData();
      int columns=md.getColumnCount();
      for (int i=1; i<=columns; i++) {
        System.out.printf(md.getColumnLabel(i) + "\t");
        }
      System.out.println("");
      while (rs.next()) {  //拜訪全部 ReseltSet 紀錄
        for (int i=1; i<=columns; i++) {
          System.out.print(rs.getString(i) + "\t");
          }
        System.out.println("");
        }
      }
    catch (Exception e) {System.out.println(e);}
    JT.closeDB();
    }
  }

執行結果 :

null
id      name    gender  height  age     birthday        remark
1       愛咪    女      157     12      null    Amy
2       彼得    男      171     14      null    Peter
3       凱莉    女      165     16      null    Kelly

接著測試資料表相關方法如下 :

import java.sql.*;
import java.util.*;
public class mysql3 {
  public static void main(String[] args) {
    JT.connectMySQL("testdb","root","mysql");
    System.out.println(JT.findTable("users")); //輸出 true
    System.out.println(JT.findTable("user"));   //輸出 false
    System.out.println(Arrays.deepToString(JT.getFieldNames("users")));
    System.out.println(Arrays.deepToString(JT.getFieldTypes("users")));
    System.out.println(Arrays.deepToString(JT.getFieldSizes("users")));
    System.out.println(JT.getFieldType("users","name")); //輸出 VARCHAR
    System.out.println(JT.getFieldSize("users","name"));  //輸出 20
    System.out.println(JT.findField("users","gender"));     //輸出 false
    System.out.println(JT.findField("users","email"));        //輸出 false
    System.out.println(JT.isTextField("users","gender"));  //輸出 true
    System.out.println(JT.isTextField("users","age"));       //輸出 false
    JT.closeDB();
    }
  }

執行結果 :

true
false
[id, name, gender, height, age, birthday, remark]
[INT, VARCHAR, CHAR, FLOAT, SMALLINT, DATE, VARCHAR]
[11, 20, 1, 12, 6, 10, 8388607]
VARCHAR
20
false
false
true
false

可見 MySQL 傳回的欄位名稱不像 Derby 那樣一律為大寫, 但欄位型態則一律大寫. 很奇怪的是, 明明有 gender 這個欄位, 但 findField() 卻傳回 false, WHY?

下面是 UPDATE 與 DELETE 的操作 :

import java.sql.*;
import java.util.*;
public class mysql4 {
  public static void main(String[] args) {
    JT.connectMySQL("test","root","mysql");
    String SQL="UPDATE users SET remark='凱莉',name='Kelly' WHERE id=3";
    System.out.println(JT.runSQL(SQL)); //輸出 null
    SQL="DELETE FROM users WHERE id=2";
    System.out.println(JT.runSQL(SQL)); //輸出 null
    try {
      ResultSet rs=JT.runSQL("SELECT * FROM users");
      ResultSetMetaData md=rs.getMetaData();
      int columns=md.getColumnCount();
      for (int i=1; i<=columns; i++) {
        System.out.printf(md.getColumnLabel(i) + "\t");
        }
      System.out.println("");
      while (rs.next()) {  //拜訪全部 ReseltSet 紀錄
        for (int i=1; i<=columns; i++) {
          System.out.print(rs.getString(i) + "\t");
          }
        System.out.println("");
        }
      }
    catch (Exception e) {System.out.println(e);}
    JT.closeDB();
    }
  }

執行結果 :

null
null
id      name    gender  height  age     birthday        remark
1       愛咪    女      157     12      null    Amy
3       Kelly   女      165     16      null    凱莉

這裡要注意, MySQL 與 Derby 一樣, 在 DELETE 指令不可以用 DELETE * FROM, 要去掉 * 號最後測試 dropField() 與 dropTable() :

import java.sql.*;
import java.util.*;
public class mysql5 {
  public static void main(String[] args) {
    JT.connectMySQL("test","root","mysql");
    System.out.println(JT.dropField("users","remark"));   //輸出 true
    System.out.println(JT.dropTable("books"));                //輸出 true
    try {
      ResultSet rs=JT.runSQL("SELECT * FROM users");
      ResultSetMetaData md=rs.getMetaData();
      int columns=md.getColumnCount();
      System.out.println("欄位名稱\t資料型態\t顯示大小\t自動增量\t可不填");
      for (int i=1; i<=columns; i++) {
        System.out.print(md.getColumnLabel(i) + "\t");
        System.out.print(md.getColumnTypeName(i) + "\t");
        System.out.print(md.getColumnDisplaySize(i) + "\t");
        System.out.print(md.isAutoIncrement(i) + "\t");
        System.out.println(md.isNullable(i));
        }
      }
    catch (Exception e) {System.out.println(e);}
    System.out.println(Arrays.deepToString(JT.getTables()));
    JT.closeDB();
    }

  }

執行結果 :

true
true
欄位名稱        資料型態        顯示大小        自動增量        可不填
id      INT     11      true    0
name    VARCHAR 20      false   1
gender  CHAR    1       false   1
height  FLOAT   12      false   1
age     SMALLINT        6       false   1
birthday        DATE    10      false   1
[users]

可見欄位 remark 與資料表 books 都成功刪除了. 除了上面 gender 欄位用 findField() 傳回 false 的問題待釐清外, Java 資料庫測試至此全部結束.


Derby 資料庫執行 SQL 批次檔與備份

測試完 Derby 資料庫後, 發現還有兩個重要功能還沒試, 一是 Derby 提供執行 SQL 批次檔功能 (使用 ij 介面的 run), 二是反過來將資料內容 dump 為 SQL 指令檔. 參考 :

# Step 2: ij Basics

首先準備一個 users.sql 批次檔放在當前目錄下, 內容如下 :

CREATE TABLE users(name VARCHAR(20),gender CHAR(1),age SMALLINT,height FLOAT);
INSERT INTO users(name,gender,age,height) VALUES ('愛咪','女',12,157);
INSERT INTO users(name,gender,age,height) VALUES ('彼得','男',14,171);
INSERT INTO users(name,gender,age,height) VALUES ('凱莉','女',16,165);

進入 ij 介面, 連線 testdb 資料庫, 執行 run 'users.sql'; 即可 :

H:\Java\JT>ij
ij 版本 10.8
ij> connect 'jdbc:derby:testdb;create=true';
警告 01J01:未建立資料庫 'testdb',會改成建立現有資料庫的連線。
ij> run 'users.sql';
ij> CREATE TABLE users(name VARCHAR(20),gender CHAR(1),age SMALLINT,height FLOAT
);
已插入/更新/刪除 0 列
ij> INSERT INTO users(name,gender,age,height) VALUES ('愛咪','女',12,157);
已插入/更新/刪除 1 列
ij> INSERT INTO users(name,gender,age,height) VALUES ('彼得','男',14,171);
已插入/更新/刪除 1 列
ij> INSERT INTO users(name,gender,age,height) VALUES ('凱莉','女',16,165);
已插入/更新/刪除 1 列
ij> select * from users;
NAME                |GEN&|AGE   |HEIGHT
-------------------------------------------------------
愛咪                  |女   |12    |157.0
彼得                  |男   |14    |171.0
凱莉                  |女   |16    |165.0

已選取 3 列

可見三筆紀錄已經成功寫入.

其次是備份資料庫, 這要跳出 ij 介面, 直接使用 Derby 的 dblook 指令. 參考 :

dblook examples

要在任何目錄使用這指令, 必須將 JDK 安裝目錄下的 db\bin 加入環境變數 path 之中, 以我的電腦為例是在 :

C:\Program Files (x86)\Java\jdk1.7.0_55\db\bin

如果是在 Derby 資料庫所在位置 (即資料庫目錄的上一層), 例如我的資料庫位於 H:\java\JT\testdb 下, 則可以在 H:\java\JT 直接下 :

H:\java\JT>dblook -d jdbc:derby:testdb -o testdb.sql

這樣就會在 H:\java\JT 目錄下產生 testdb.sql 備份檔, 其內容如下 :

-- ============================

-- 此檔案以 Derby 的 dblook 公用程式建立。
-- 時間戳記:2014-04-28 12:49:23.967
-- 原始檔資料庫為:testdb
-- 連線 URL 為:jdbc:derby:testdb
-- appendLogs: false

-- ----------------------------------------------
-- DDL 表格陳述式
-- ----------------------------------------------

CREATE TABLE "APP"."USERS" ("NAME" VARCHAR(20), "GENDER" CHAR(1), "AGE" SMALLINT, "HEIGHT" DOUBLE);

CREATE TABLE "APP"."BOOKS" ("ID" INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1), "GENDER" CHAR(1), "NAME" VARCHAR(50));

-- ----------------------------------------------
-- DDL 鍵值陳述式
-- ----------------------------------------------

-- 主要/唯一
ALTER TABLE "APP"."BOOKS" ADD CONSTRAINT "BOOKS_KEY" PRIMARY KEY ("ID");

可見, 資料庫 testdb 裡面的全部資料表都會 dump 出來.

關於法律風險

今日看到這篇 :

# 導演臉書轉新聞 遭恐嚇:家人遇不測

感想如下 :
  1. 網路恐嚇涉及刑責, 不要在網路上謾罵, 否則有被告誹謗與恐嚇的法律風險.
  2. 不要在臉書或部落格 po 家人或個人照片, 否則可能陷入被肉搜的風險.
現在網路發達, 手機就能上網, 但是很多人卻很容易陷入法律風險而不自知, 就像很多人貪圖方便, 路邊雙排停車一樣, 自陷於極大的法律風險卻不自覺. 有律師朋友的問問看就知道, 他們會不會雙排停車? 只要有個摩托車不小心撞上雙排停車的車尾, 後果如何? 明明是車被撞, 但雙排停車就是你不對, 準備籌錢跟跑法院吧. 我每次看到有人雙排停車, 就會感到既羨慕又嫉妒, 羨慕的是這個人比我有閑 (有空跑法院); 忌妒的是這個人比我有錢 (多到不在乎)! 

同樣地, 在別人的臉書亂下評論, 當事人提告, 網路警察就會根據 ip 查出你的位址, 不要以為虛擬世界中找不到. 所以每次我看到有人在臉書或部落格猛貼自拍照, 家庭照, 或者個人帥到不行的美照, 也是感到既羨慕又忌妒; 羨慕的是這個人比我有勇氣 (不怕詐騙集團跟黑道); 忌妒的是~~~這傢伙長得比我帥! 

網路中其實都有一些特殊的眼睛在看, 有接觸的也知道所有的網路資訊都會經過某些幹管時被 XO .... 總之, 在網路裏要盡量保持浮潛姿勢.


2014年4月27日 星期日

回顧車諾比核事故

核四問題反核擁核似乎公說公有理, 婆說婆有理, 擁核的說廢核就會缺電, 漲電價你受得了嗎? 那我要反問, 一旦發生核災, 你受得了嗎? 台灣沒發生過當然不知道, 就一起來回顧一下車諾比核災吧!

# 車諾比核事故

摘要如下 :
  1. 事故地點 : 車諾比核電廠位於烏克蘭北方邊境, 鄰近白俄羅斯與俄羅斯, 距首都基輔 110 公里.
  2. 出事機組 : 第四號核反應爐
  3. 出事時間 : 1986 年 4 月 26 日凌晨 1 點 23 分
  4. 輻射污染 : 廣島原子彈 * 400 倍
  5. 經濟損失 : 2000 億美元 (史上最貴的災難損失)
  6. 事故原因 : (1) 設計缺失 (2) 管理員操控失當
我說的沒錯吧, 核電廠是可控的, 雖然不會像原子彈那樣爆炸, 但是若管理控制不當發生核災, 其影響遠比一顆廣島原子彈還大. 問題不在技術, 是在管理. 就像南韓歲月號沉沒事件, 人類的航海技術早就不是問題 (都已經上月球了不是嗎), 問題都出在管理.

核反應爐的安全控制系統中的要角是抽水機, 用來維持冷卻水循環以免爐心過熱, 正常情況下這抽水機是由反應爐驅動的渦輪發電機供給動力, 為了確保此抽水機電力不虞缺乏, 還需要柴油發電機作為備用緊急電力. 但是柴油發電機沒辦法瞬間啟動輸出電力, 因此當反應爐停機時, 還是得靠已經與反應爐分離的渦輪發電機慣性動力供電給抽水機撐一下, 車諾比核災的起因就是電廠當局要趁著 4 號反應爐進行停機歲修, 輸出功率下降時, 順便測試一下渦輪發電機是否能正常供電給抽水機. 不試還好, 一試就出事.

由於操作員太快降低反應爐輸出功率, 導致爐中 "氙-135" 遽增, 為了克服此問題並完成此項測試, 操作員拔出過量的控制棒, 雖然渦輪發電機成功地驅動了抽水機, 但卻使反應爐功率增加使控制棒管道變形, 結果在欲插回全部控制棒以便停機時, 控制棒因控制棒管道變形而插不回去了, 這下問題大了, 反應爐無法控制了, 輸出了 10 倍的額定功率, 高熱使的爐心熔化, 而遽增的水蒸氣在爐頂爆出一個大洞, 於是爐心熔毀產生的大量輻射物質隨著氣爆飛散進入大氣中 ...

"爆炸發生後,四號反應爐廠房被炸掉一半,反應爐核心直接暴露在大氣中,.核心中央一道藍白光線射向夜空,那是暴露的放射性物質發出的切連科夫輻射。" .

這藍白光線就是死亡之光吧 ! 

這是因為值班人員打瞌睡造成的意外嗎? 不是耶! 是既定的歲修停爐操作啊! 不是說都有 SOP 跟雙重保險機制嗎? 這樣也能出問題, 想像不到吧? 車諾比之後全球核電廠應該都記取教訓了吧? 那怎麼又會出現福島核災呢? 政府官員這回又打包票說, 福島核災的教訓我們都記取了, 絕對不會發生同樣的事故 .... 天知道.

 

妖言惑眾 part2

今天看到中時這篇 :

# 郁:核電廠停半年 「看看行不行」

我把它定位為 "妖言惑眾之 part 2" , 證據如下 :

郁慕明說 : "核四爭議沉痾已30幾年,但核電的問題不能全推給馬政府,「民主政治也是責任政治」,就算馬要負責,也要追根究柢,拍板的前總統李登輝、執政過的民進黨,都有責任。"

這句話的問題出在, 郁慕明是根據自身一貫的反李登輝路線說話, 而不是根據歷史事實. 一般人看了這段新聞後, 會以為原來核四是在李登輝任內拍板決定要蓋的, 而民進黨則是在野反核, 上台擁核的.

查一下 wiki 的記載就能馬上戳破郁慕明的妖言惑眾 :

# 核四龍門電廠

"核能第四發電廠計畫,台電於1980年5月提出,經行政院原子能委員會選址於貢寮。在1982年至1986年的中央政府總預算中也編列新台幣110億元,並執行31億餘元。1985年5月因貢寮鄉民強烈反對,總統蔣經國指示行政院暫緩興建。1986年發生車諾比事件,隨後民主進步黨與台灣環境保護聯盟成立,台灣反核活動由此展開;同年7月,未執行的79億預算遭立法院凍結。"

看到沒? 核四計畫是 1980 年提出的, 時任總統蔣經國拍版定案, 郁慕明為何跳過蔣經國, 直指李登輝? 郁慕明當真把人民看成歷史文盲? 當年貢寮鄉民反對, 蔣經國就暫緩興建, 可見蔣氏晚期相對較重視民意. 但蔣氏於 1988 年死於總統任上後, 繼任的李登輝繼續執行核四計畫, 直到 2000 年陳水扁上臺兌現反核支票, 卻被國親新擁核勢力反撲而失敗. 這 12 年才是李登輝的責任所在. 把核四說成是李登輝拍板決定, 是別有居心.

核電並非乾淨的能源, 不是不能用核電, 而是要看你有沒有那個條件. 它需要海水來冷卻, 所以沒辦法蓋在沙漠那種鳥不生蛋的地方, 但蓋在海邊又怕地震引起的海嘯 (福島核災就是這樣). 更重要的是, 產生的高階核廢料 (使用過的燃料棒) 比碳排放更難搞, 需要總統級的照護, 核電廠廠區放滿後要放哪? 我建議, 擁核立委與官員, 每個人至少抱一瓶核廢料鋼桶回家放後院, 那我就贊成核四.

# 世界各國對高階核廢料貯存與處置方式的分析
# 核能與科技:核廢料存放

瀉藥不是不能吃, 但沒那個屁股卻吃那個瀉藥, 是會要命的. 除非你真的想徹底消滅中華民國, 那就幹吧.


2014年4月26日 星期六

整理書櫃

姐姐今天跟同學約好去左新準備會考, 我載她過去順便還書. 下午繼續整理書櫃 part2, 這回針對最上層的語言學書籍, 翻了一下最厚的那本碩士論文, 呵呵, 原來我是 2004.2.28 辦理離校手續, 轉頭跟姐姐說, 我是 228 那天畢業的, 轉眼已經十年了! 對呀, 那年菁菁才滿一歲呢, 倏忽十年一晃, 她已小五矣!

二哥在客廳聽到我們的談話, 可能沒聽清楚, 竟問我說, 爸, 你有經歷 228 事件喔! 哇咧, 有的話我現在至少也 80 歲囉! 二哥問問題前也該先 screen 一下吧!

這些當年教科書自從畢業後就放在那兒, 當初決定不報考博士班後, 除了語音學的書因為返校授課有拿來複習外, 其他的都沒再看, 幾乎全部忘光光, 語義學, 句法學, 心理語言學, 還有優選論等等, 都還給老師了, 這些書以後應該也不會再看, 所以通通裝箱運回鄉下. 我是念舊的人, 捨不得把它們秤斤賣掉. 因為翻閱舊書時偶而會看到以前留下的隻字片語, 彷彿像穿越劇一樣, 頓時掉進時光隧道, 回到了從前.

經過兩天清理後, 書房的架子清爽不少, 準備給二哥跟姐姐放參考書. 還有一整排日文跟英文書, 以及一整套的南懷瑾文集, 這些都還會看, 所以只是清清灰塵, 不須挪動.


2014年4月24日 星期四

Win7 開始功能表中程式的圖示消失問題

早上在整理 win7 電腦時, 發現開始功能表中的 OFFICE 2010 各程式的 ICON 變成醜醜的不知名圖檔, 於是用 "win7 開始 清單 圖示 消失" 查詢, 得知可能是昨日尚未 UPDATE 結束就關機下班造成, 解決辦法參考如下 :

#  [Windows7] 修復桌面與開始功能表中錯亂或是消失的捷徑圖示
# Windows 7 – 解決捷徑圖示不見的問題


Java 資料庫存取 : 使用 Derby (續)

晚上整理書房, 把一堆已經不會再看的書裝箱帶回鄉下, 例如線性代數, 機率論, 工程數學等等, 現在還要看這些書幹啥? 只是徒然佔據書架空間而已. 如果可以挖個洞的話, 真想把這些書放進時空膠囊裡埋藏地下呢! 這些都是唸電機必修的教科書, 但老實說, 除了當教授外, 一點屁用都沒有. 就算是做統計, 其實也不需要先讀機率論, 因為都是一些枯燥乏味的定理證明.

扯遠了, 幹完雜事, 看到架上的 Java, 又想起 Derby 資料庫還沒搞定 (上回只是用 ij 指令玩玩而已).  參考 :

# Java 資料庫存取 : 使用 Derby

於是乎把書上的範例檔原封不動執行看看 :

import java.sql.*;
public class mytest {
  public static void main(String[] args) {    
    String dsn="jdbc:derby:testdb;create=true";
    String user="", pwd="";  
    try {
      //Class.forName("org.apache.derby.jdbc.EmbeddedDriver"); //不需要
      Connection conn=DriverManager.getConnection(dsn, user, pwd);
      Statement stat=conn.createStatement();
      System.out.println("Derby connected!");
      conn.close();
      }
    catch (Exception e) {System.out.println(e);}
    }
  }

執行結果 :

H:\Java\JT>java mytest
java.lang.ClassNotFoundException: org.apache.derby.jdbc.EmbeddedDriver

找不到 JDK 的內建 Derby 驅動程式, 原來是必須在環境變數 CLASSPATH 中加入 JDK 安裝目錄下的 db\lib\derby.jar 才會載入驅動程式, 參考  :

# Configure Embedded Derby

以我的電腦而言, 是在 C:\Program Files (x86)\Java\jdk1.7.0_55\db\lib\derby.jar :


設定好後按確定, 一定要重新開啟命令提示字元視窗才會套用新的環境變數, 再次執行上面的程式就 ok 了 :

H:\Java\JT>java mytest
Derby connected!

注意, Class.forName() 不需要設定也是可以的 (對於 ACCESS 的 MDB 檔案也是一樣, 都不需要了). 接下來就可以開始操作資料庫了, 參考 Derby 的 SQL 手冊 :

# http://db.apache.org/derby/docs/10.6/ref/
# Derby Data types

下面的範例是新增資料表與紀錄 :

import java.sql.*;
public class mytest {
  public static void main(String[] args) {     
    String dsn="jdbc:derby:testdb;create=true";
    String user="", pwd=""; 
    String SQL;
    try {
      Connection conn=DriverManager.getConnection(dsn, user, pwd);
      Statement stat=conn.createStatement();
      System.out.println("Derby connected!");
      SQL="CREATE TABLE users(name VARCHAR(20),age SMALLINT)";
      stat.executeUpdate(SQL);
      SQL="INSERT INTO users(name,age) VALUES ('愛咪',12)," +
                "('彼得',14),('凱莉',16)";
      stat.executeUpdate(SQL);
      System.out.println("Records inserted!");
      conn.close();
      }
    catch (Exception e) {System.out.println(e);}
    }
  }

打開另一個 DOS 視窗用 ij 去查詢資料表, 可見紀錄已寫入資料表中 :

G:\Java\JT>ij
ij 版本 10.8
ij> connect 'jdbc:derby:testdb';
ij> select * from users;
NAME                |AGE
---------------------------
愛咪                  |12
彼得                  |14
凱莉                  |16

已選取 3 列

注意, ij 介面用完需退出, 否則程式無法連線, 出現下列錯誤 :

G:\Java\JT>java mytest
java.sql.SQLException: 無法使用類別載入器 sun.misc.Launcher$AppClassLoader@e3280
2 啟動資料庫 'testdb',請參閱下個異常狀況,以取得詳細資料。

下面的範例是用 ResultSet 來儲存查詢資料 :

import java.sql.*;
public class mytest {
  public static void main(String[] args) {     
    String dsn="jdbc:derby:testdb;create=true";
    String user="", pwd=""; 
    String SQL;
    try {
      Connection conn=DriverManager.getConnection(dsn, user, pwd);
      Statement stat=conn.createStatement();
      SQL="SELECT * FROM users";
      ResultSet rs=stat.executeQuery(SQL);
      ResultSetMetaData md=rs.getMetaData();
      int columns=md.getColumnCount();
      for (int i=1; i<=columns; i++) {
        System.out.printf(md.getColumnLabel(i) + "\t"); 
        }
      System.out.println("");
      while (rs.next()) {  //拜訪全部 ReseltSet 紀錄
        for (int i=1; i<=columns; i++) { 
          System.out.print(rs.getString(i) + "\t"); 
          }
        System.out.println("");
        }
      }
    catch (Exception e) {System.out.println(e);}
    }
  }

G:\Java\JT<java mytest
NAME    AGE
愛咪    12
彼得    14
凱莉    16


下面的範例是利用 ResultSetMetaData 顯示資料表的欄位資訊 :

import java.sql.*;
public class mytest {
  public static void main(String[] args) {     
    String dsn="jdbc:derby:testdb;create=true";
    String user="", pwd=""; 
    String SQL;
    try {
      Connection conn=DriverManager.getConnection(dsn, user, pwd);
      Statement stat=conn.createStatement();
      SQL="SELECT * FROM users";
      ResultSet rs=stat.executeQuery(SQL);
      ResultSetMetaData md=rs.getMetaData();
      int columns=md.getColumnCount();
      System.out.println("欄位名稱\t資料型態\t顯示大小\t自動增量\t可不填");
      for (int i=1; i<=columns; i++) {
        System.out.print(md.getColumnLabel(i) + "\t");
        System.out.print(md.getColumnTypeName(i) + "\t");
        System.out.print(md.getColumnDisplaySize(i) + "\t");
        System.out.print(md.isAutoIncrement(i) + "\t");
        System.out.println(md.isNullable(i));
        }
      conn.close();
      }
    catch (Exception e) {System.out.println(e);}
    }
  }

G:\Java\JT>java mytest
欄位名稱        資料型態        顯示大小        自動增量        可不填
NAME    VARCHAR 20      false   1
AGE     SMALLINT        6       false   1

看來是 OK 的, 下面將修改 JT.java 常用函式庫, 把 Derby 納入 :

import java.io.*;
import java.sql.*;
import java.util.*;
import java.util.Date; 
import java.text.*;
import java.util.regex.*;
import java.security.MessageDigest;
public class JT {
  static Connection conn;
  static Statement stat;
  public static void connectMDB(String db) {
    connectMDB(db, "", "");
    }
  public static void connectMDB(String db, String user, String pwd) {
    try {
      String dsn="jdbc:odbc:Driver={Microsoft Access Driver (*.mdb)};" +
                 "DBQ=" + db + ";READONLY=false}";
      conn=DriverManager.getConnection(dsn, user, pwd);
      stat=conn.createStatement();
      }
    catch (Exception e) {System.out.println(e);}
    }
  public static void connectDerby(String db){
    connectDerby(db, "", "");
    }
  public static void connectDerby(String db, String user, String pwd){
    try {
      String dsn="jdbc:derby:" + db + ";create=true";
      conn=DriverManager.getConnection(dsn, user, pwd);
      stat=conn.createStatement();
      }
    catch (Exception e) {System.out.println(e);}
    }
 public static void closeDB() {
    try {conn.close();}
    catch (Exception e) {System.out.println(e);}
    conn=null;
    stat=null;
    }
  public static ResultSet runSQL(String SQL) {
    ResultSet rs=null;
    try { 
      if (SQL.indexOf("SELECT")==-1) {stat.executeUpdate(SQL);}
      else {rs=stat.executeQuery(SQL);}
      }
    catch (Exception e) {System.out.println(e);}
    return rs;
    }
 }

這樣就可以用 JT 類別來縮短程式碼了 :

import java.sql.*;
public class derby1 {
  public static void main(String[] args) {     
    JT.connectDerby("testdb");
    try {
      ResultSet rs=JT.runSQL("SELECT * FROM users");
      ResultSetMetaData md=rs.getMetaData();
      int columns=md.getColumnCount();
      for (int i=1; i<=columns; i++) {
        System.out.printf(md.getColumnLabel(i) + "\t"); 
        }
      System.out.println("");
      while (rs.next()) {  //拜訪全部 ReseltSet 紀錄
        for (int i=1; i<=columns; i++) { 
          System.out.print(rs.getString(i) + "\t"); 
          }
        System.out.println("");
        }
      }
    catch(Exception e) {System.out.println(e);}
    JT.closeDB();
    }
  }

只要把 JT.connectMDB() 改為 JT.connectDerby() 就可以立刻改為連線 Derby 資料庫了.

接下來要參考上回 ACCESS 那篇來逐一測試那些為 ACCESS 寫的函式是否需要修改 :

#  Java 資料庫存取 : 使用 ACCESS (續)

先來試試 dropTable(), 把上面建立的 users 資料表刪除後再重建 :

import java.sql.*;
public class derby2 {
  public static void main(String[] args) {    
    JT.connectDerby("testdb");
    System.out.println(JT.dropTable("users")); //輸出 true (成功刪除)
    JT.closeDB();
    }
  }

可見資料表已經成功刪除了. 如果再次執行 java derby2, 就會報錯, 因為 users 已經不存在啦 :

H:\Java\JT<java derby2
java.sql.SQLSyntaxErrorException: 因為 'DROP TABLE' 不存在,所以無法在 'USERS'
上執行它。
false

how to create table if it doesnt exist using Derby Db
Creating the database and running SQL
Java DB (Derby) - Creating New Tables
Derby "create table if not exists"

但原先給 ACCESS 用的 JT.createTable() 在 Derby 不能用, 使用原先的 JT.createTable("users") 會出現語法錯誤 :

java.sql.SQLSyntaxErrorException: 語法錯誤:Encountered "" at line 1, colum
n 18。

看來 Derby 不能像 ACCESS 那樣先用 CREATE TABLE users 建立一個空的 users 資料表, 而是必須至少指定一個欄位才行, 所以我修改了 JT.java, 添加一個可傳入欄位定義參數的多載方法 :

  public static boolean createTable(String table) { //僅 ACCESS 使用, Derby 不能用
    try {stat.executeUpdate("CREATE TABLE " + table);}
    catch (Exception e) {System.out.println(e);return false;}
    return true;
    }
  public static boolean createTable(String table, String columns) { //ACCESS, Derby 均可用
    try {stat.executeUpdate("CREATE TABLE " + table + " (" + columns + ")");}
    catch (Exception e) {System.out.println(e);return false;}
    return true;
    }

參考 Derby 的欄位型態定義如下 :

# Derby Data types

我們在 ACCESS 資料庫常使用的欄位型態與 Derby 幾乎相同, 差異最大的是作為流水號的自動增量主鍵, 在 ACCESS 是用 :

id AUTOINCREMENT PRIMARY KEY

但在 Derby 就比較囉唆, 如下是指定 id 欄位為自動增量主鍵, 其鍵名為 primary_key (各資料表之主鍵名為唯一, 須各自命名) :

id INT NOT NULL GENERATED ALWAYS AS IDENTITY CONSTRAINT primary_key PRIMARY KEY



其次是儲存較長文字的欄位, 在 ACCESS 是用 MEMO, 可儲存 65536 個字元; 但在 Derby 則是使用 LONG VARCHAR, 可儲存 32700 個字元, 僅為 ACCESS 的一半. 其他常用形態, SMALLINT, CHAR, VARCHAR, DATE 等用法都一樣. 故原來為 ACCESS 寫的 JT.addField() 不用修改, 還是可用 :

  public static boolean addField(String table, String field, String type) {
    String SQL="ALTER TABLE " + table + " ADD " + field + " " + type;
    try {stat.executeUpdate(SQL);}
    catch (Exception e) {System.out.println(e);return false;}
    return true;
    }

下面範例即利用 addField() 於 createTable() 後添加欄位 :

import java.sql.*;
import java.util.*;
public class derby3 {
  public static void main(String[] args) {
    JT.connectDerby("testdb");
    String col="id INT NOT NULL GENERATED ALWAYS AS IDENTITY " +
                       "CONSTRAINT users_key PRIMARY KEY,gender CHAR(1)";
    System.out.println(JT.createTable("users", col)); //第一個資料表 users
    System.out.println(JT.addField("users","name","VARCHAR(20)")); //輸出 true
    System.out.println(JT.addField("users","height","FLOAT"));  //輸出 true
    System.out.println(JT.addField("users","age","SMALLINT"));  //輸出 true
    System.out.println(JT.addField("users","birthday","DATE")); //輸出 true
    System.out.println(JT.addField("users","remark","LONG VARCHAR"));
    col="id INT NOT NULL GENERATED ALWAYS AS IDENTITY " +
           "CONSTRAINT books_key PRIMARY KEY,gender CHAR(1)";
    System.out.println(JT.createTable("books", col)); //第二個資料表 books
    System.out.println(JT.addField("books","name","VARCHAR(50)")); //輸出 true
    try {
      ResultSet rs=JT.runSQL("SELECT * FROM users");
      ResultSetMetaData md=rs.getMetaData();
      int columns=md.getColumnCount();
      System.out.println("欄位名稱\t資料型態\t顯示大小\t自動增量\t可不填");
      for (int i=1; i<=columns; i++) {
        System.out.print(md.getColumnLabel(i) + "\t");
        System.out.print(md.getColumnTypeName(i) + "\t");
        System.out.print(md.getColumnDisplaySize(i) + "\t");
        System.out.print(md.isAutoIncrement(i) + "\t");
        System.out.println(md.isNullable(i));
        }
      }
    catch (Exception e) {System.out.println(e);}
    System.out.println(Arrays.deepToString(JT.getTables()));
    JT.closeDB();
    }
  }

執行結果 :

true
true
true
true
true
true
true
true
欄位名稱        資料型態        顯示大小        自動增量        可不填
ID      INTEGER 11      true    0
GENDER  CHAR    1       false   1
NAME    VARCHAR 20      false   1
HEIGHT  DOUBLE  22      false   1
AGE     SMALLINT        6       false   1
BIRTHDAY        DATE    10      false   1
REMARK  LONG VARCHAR    32700   false   1
[USERS,BOOKS]

可見 float 最終也是以 double 儲存. 上例也顯示 getTables() 函式不需要修改, 可正常顯示所建立的兩個資料表.

接著要把資料寫進資料表中 :

import java.sql.*;
public class derby4 {
  public static void main(String[] args) {
    JT.connectDerby("testdb");
    String SQL="INSERT INTO users(name,gender,age,height,remark) VALUES " +
                       "('愛咪','女',12,157,'Amy'),('彼得','男',14,171,'Peter')," +
                       "('凱莉','女',16,165,'Kelly')";
    System.out.println(JT.runSQL(SQL)); //輸出 null
    try {
      ResultSet rs=JT.runSQL("SELECT * FROM users");
      ResultSetMetaData md=rs.getMetaData();
      int columns=md.getColumnCount();
      for (int i=1; i<=columns; i++) {
        System.out.printf(md.getColumnLabel(i) + "\t");
        }
      System.out.println("");
      while (rs.next()) {  //拜訪全部 ReseltSet 紀錄
        for (int i=1; i<=columns; i++) {
          System.out.print(rs.getString(i) + "\t");
          }
        System.out.println("");
        }
      }
    catch (Exception e) {System.out.println(e);}
    JT.closeDB();
    }
  }

結果輸出 :

null
ID      GENDER  NAME    HEIGHT  AGE     BIRTHDAY        REMARK
1       女      愛咪    157.0   12      null    Amy
2       男      彼得    171.0   14      null    Peter
3       女      凱莉    165.0   16      null    Kelly

此處我們一次寫入三筆紀錄, 每筆用逗號隔開. 下面繼續測試 dropField(), FindTable() 等方法 :

import java.sql.*;
import java.util.*;
public class derby5 {
  public static void main(String[] args) {
    JT.connectDerby("testdb");
    System.out.println(JT.findTable("users")); //輸出 false
    System.out.println(JT.findTable("user"));   //輸出 false
    System.out.println(Arrays.deepToString(JT.getFieldNames("users")));
    System.out.println(Arrays.deepToString(JT.getFieldTypes("users")));
    System.out.println(Arrays.deepToString(JT.getFieldSizes("users")));
    System.out.println(JT.getFieldType("users","name"));
    System.out.println(JT.getFieldSize("users","name"));  //輸出 0
    System.out.println(JT.findField("users","gender"));     //輸出 false
    System.out.println(JT.findField("users","email"));        //輸出 false
    System.out.println(JT.isTextField("users","gender"));  //輸出 false
    System.out.println(JT.isTextField("users","age"));       //輸出 false
    JT.closeDB();
    }
  }

輸出結果 :

false
false
[ID, GENDER, NAME, HEIGHT, AGE, BIRTHDAY, REMARK]
[INTEGER, CHAR, VARCHAR, DOUBLE, SMALLINT, DATE, LONG VARCHAR]
[11, 1, 20, 22, 6, 10, 32700]

0
false
false
false
false

明明有 "users" 資料表, 怎會輸出 false 呢? 觀察其輸出可發現, Derby 不管你的資料表或欄位名稱用大寫或小寫, 它都一律改成大寫, 如下修改 derby5.java 即知 :

import java.sql.*;
import java.util.*;
public class derby5 {
  public static void main(String[] args) {
    JT.connectDerby("testdb");
    System.out.println(JT.findTable("USERS")); //輸出 true
    System.out.println(JT.findTable("USER"));  //輸出 false
    System.out.println(Arrays.deepToString(JT.getFieldNames("USERS")));
    System.out.println(Arrays.deepToString(JT.getFieldTypes("USERS")));
    System.out.println(Arrays.deepToString(JT.getFieldSizes("USERS")));
    System.out.println(JT.getFieldType("USERS","NAME"));
    System.out.println(JT.getFieldSize("USERS","NAME"));
    System.out.println(JT.findField("USERS","GENDER"));    //輸出 true
    System.out.println(JT.findField("USERS","EMAIL"));        //輸出 FALSE
    System.out.println(JT.isTextField("USERS","GENDER")); //輸出 true
    System.out.println(JT.isTextField("USERS","AGE"));         //輸出 FALSE
    JT.closeDB();
    }
  }

輸出結果 :

true
false
[ID, GENDER, NAME, HEIGHT, AGE, BIRTHDAY, REMARK]
[INTEGER, CHAR, VARCHAR, DOUBLE, SMALLINT, DATE, LONG VARCHAR]
[11, 1, 20, 22, 6, 10, 32700]
VARCHAR
20
true
false
true
false

我不打算修改 JT.java 來處理 Derby 大寫問題, 只要記得使用 Derby 時, table 與 field 名稱都要用大寫來判斷. 下面是 UPDATE 與 DELETE 的測試 :

import java.sql.*;
import java.util.*;
public class derby6 {
  public static void main(String[] args) {
    JT.connectDerby("testdb");
    String SQL="UPDATE users SET remark='凱莉',name='Kelly' WHERE id=3";
    System.out.println(JT.runSQL(SQL)); //輸出 null
    SQL="DELETE FROM users WHERE id=2";
    System.out.println(JT.runSQL(SQL)); //輸出 null
    try {
      ResultSet rs=JT.runSQL("SELECT * FROM users");
      ResultSetMetaData md=rs.getMetaData();
      int columns=md.getColumnCount();
      for (int i=1; i<=columns; i++) {
        System.out.printf(md.getColumnLabel(i) + "\t");
        }
      System.out.println("");
      while (rs.next()) {  //拜訪全部 ReseltSet 紀錄
        for (int i=1; i<=columns; i++) {
          System.out.print(rs.getString(i) + "\t");
          }
        System.out.println("");
        }
      }
    catch (Exception e) {System.out.println(e);}
    JT.closeDB();
    }
  }

輸出結果 :

null
null
ID      GENDER  NAME    HEIGHT  AGE     BIRTHDAY        REMARK
1       女      愛咪    157.0   12      null    Amy
3       女      Kelly   165.0   16      null    凱莉

注意, 這裡我們一次更新兩個欄位之值, 中間用逗號隔開. 其次, Derby 的 DELETE 語法不可像 ACCESS 那樣加 * 號. 最後測試 dropField() 與 dropTable() :

import java.sql.*;
import java.util.*;
public class derby7 {
  public static void main(String[] args) {
    JT.connectDerby("testdb");
    System.out.println(JT.dropField("users","remark")); //輸出 true
    System.out.println(JT.dropTable("books"));            //輸出 true
    try {
      ResultSet rs=JT.runSQL("SELECT * FROM users");
      ResultSetMetaData md=rs.getMetaData();
      int columns=md.getColumnCount();
      System.out.println("欄位名稱\t資料型態\t顯示大小\t自動增量\t可不填");
      for (int i=1; i<=columns; i++) {
        System.out.print(md.getColumnLabel(i) + "\t");
        System.out.print(md.getColumnTypeName(i) + "\t");
        System.out.print(md.getColumnDisplaySize(i) + "\t");
        System.out.print(md.isAutoIncrement(i) + "\t");
        System.out.println(md.isNullable(i));
        }
      }
    catch (Exception e) {System.out.println(e);}
    System.out.println(Arrays.deepToString(JT.getTables()));
    JT.closeDB();
    }
  }

輸出結果 :

true
true
欄位名稱        資料型態        顯示大小        自動增量        可不填
ID      INTEGER 11      true    0
GENDER  CHAR    1       false   1
NAME    VARCHAR 20      false   1
HEIGHT  DOUBLE  22      false   1
AGE     SMALLINT        6       false   1
BIRTHDAY        DATE    10      false   1
[USERS]

可見 users 資料表的 remark 欄位被刪除了; books 資料表也被刪除了. Derby 測試結束.

參考資料 :

Apache Derby: Documentation
Step 2: ij Basics
Apache Derby: Quick Start
# dblook examples
# Connecting to database


Bullzip 虛擬印表機

公司電腦 Win7 重裝後要安裝常用小工具, 其中虛擬印表機是列印網頁所必需, 一般 Office 檔案已經內建輸出 pdf 功能, 因此不用煩惱, 但網頁就不行了, 一般瀏覽器沒有列印為 pdf 檔功能, 必須另外安裝虛擬印表機.

在網路上找到兩款, 一為 BullZip, 另一為 doPDF, 都有免費版, 比較之後選擇安裝 BullZip, 因為它具有設定密碼功能, 而且不僅可輸出 pdf, 也可以輸出 png, tiff 格式等等 :
  1. [免費] Bullzip PDF Printer v9.7.0 支援任意文件格式的 PDF 轉檔工具 [下載免費版]
  2. [免費] doPDF v7.3 將任何格式的文件轉成PDF檔 [download]


2014年4月23日 星期三

幾個好站

因為要準備下午的授課, 所以上網找了一下 roaming 的資料, 發現了下面幾個很不錯的網站 :
  1. raufakram's knowledge portal :
    其中的 SCCP (SIGNALING CONNECTION CONTROL PART) 這篇寫得真是詳細, 圖文並茂.
  2. what-when-how : in-depth tutorials and information :
    這個網站包羅萬象, Ajax, 8051 等各式知識依英文字母排序. 
上完這兩堂課, 終於可以專心寫程式了.


2014年4月22日 星期二

JDK 8 不支援 ODBC:JDBC

我辦公桌的電腦上回升 WIN7 時, 跳過了某個環節, 導致 OFFICE 竟然無法通過認證, 向 MIS 求救也是束手無策, 最後只好重灌一途. 所以今天又花了一整天在搞電腦, 真是浪費時間, 而且 Java, Python 等開發環境又要重新安裝, 調整設定最傷腦筋.

重裝 JDK 時想說試試看 JDK 8 吧, 之前發現 XP 無法安裝 JDK8 (不再支援), WIN7 沒有問題, 但是安裝好後執行 ACCESS 資料庫存取, 卻出現 NullPointer (cannot find odbc-jdbc driver) 的錯誤, 找了一下網路, 原來 JDK 8 已不支援 ODBC-JDBC 驅動程式, 詳見 :

Manipulating an Access database from Java without ODBC

原因主要是 :
  1. 以 ODBC 存取 ACCESS 資料庫時, 當處理的 UNICODE 超過 \u00ff 時可能會出錯.
  2. ACCESS ODBC 驅動程式只能用在 WINDOWS, 而且分成 32/64 位元, 很麻煩.
第一項我沒用過, 但處理中文從沒出現問題. 第二項就是我之前遇到的麻煩, 如果在 64 位元電腦上安裝 64 位元的 JDK 7, 就無法用 ODBC-JDBC 連線 ACCESS 資料庫; 必須要用 32 位元的 JDK 7 才行, 參見 :

# Java 程式連線 ACCESS 資料庫問題

所以只好刪除 JDK 8, 重新下載 JDK 7 (X86) 安裝後, 果然就 OK 了. 看來若要繼續使用 ACCESS 資料庫, 則 X86 版的 JDK7U55 與 JRE 要保存起來比較好.

2016-07-26 補充 :

今天找到這篇, 以後有時間回頭玩 Java 時再試試看 :

# Connecting to MS Access Files via JDBC in 64-bit Java


TCP/IP 閱讀筆記 (一)

本周要開始學習以前被我視為 Low-end 而不肯用心學習的網路技術. 以下是閱讀 "CCNA 網路探索指南 (培生)" 這本書的摘要筆記 :

LAN, WAN 與 Internet :

  1. 依服務層面與涵蓋區域, 網路可分為 : LAN < WAN < Internet
  2. LAN 網路中的終端設備與終端使用者受到一個共同的管理機制所控制.
  3. WAN 唯一的目的是用來連接相距較遠的 LAN, 在 WAN 上沒有任何終端使用者.
  4. Internet 是以 WAN 連接兩個以上 LAN 所形成的互聯網路.
  5. NIC (Network Interface card) 網路介面卡是連接電腦與 LAN 的實體介面, 每一片 NIC 卡都有一個唯一的實體位址以資識別. 

網路協定與參考模型 :

網路協定定義下列內容 :

  1. 訊息格式 (e.g. 每個 segment 可包含多少資料)
  2. 中繼設備共享路徑資訊的方式
  3. 中繼設備之間更新訊息的方法
  4. 起始與終止主機之間通訊的程序

網路工程師以兩種模型來描述網路 : 協定模型 (TCP/IP) + 參考模型 (OSI 七層模型)

  1. OSI 參考模型並非一個實作的規格, 也未定義明確之網路服務, 只是一個抽象的概念, 用來協助了解網路的功能與程序. TCP/IP 則是一個開放的協定標準.

  2. TCP/IP 協定模型分為四層 :
    應用層 : 呈現應用資料給使用者, 例如 HTTP
    傳輸層 : 負責設備之間的通訊, 執行錯誤修正 (TCP)
    網路層 : 尋找最佳路徑將封包傳送到目的地 (IP)
    存取層 : 專司媒體存取 (Ethernet)
  3. 傳輸層會將應用層的資料分段 (segmentation) 與封裝 (encapsulation) 後, 交給網路層傳送到目的地. 所謂封裝就是在資料前面添加該層的標頭 (header).

資料單元 (PDU) :
  1. 網路模型的每一層都有其協定資料單元 (Protocol Data Unit) :
    應用層 : Data
    傳輸層 : Segment
    網路層 : Packet
    鏈路層 : Frame
    實體層 : Bits
  2. 網路位址 :
    OSI 模型中有三層具有位址資訊 : 鏈路層, 網路層, 傳輸層, 來源端的這三層的 PDU 都會添加其位址資訊, 供目的端的同層 (Peer) 解讀. 其中傳輸層使用 port 號碼來識別目的主機上接收此資訊的程序 (應用程式), 而其他兩層 (網路層與鏈路層) 的位址資訊則是用來確保找到正確的主機. 第 2 層位址資訊稱為 MAC (Media Access Control) 位址, 用來定址區域網路中的主機 (實體位址), 因此 MAC 位址在區網中必須是唯一的; 而第 3 層位址資訊為 IP 位址, 用來給中繼網路設備 (即路由器) 尋找 Internet 上的主機, 將資訊從一個區域網路傳遞到另一個區域網路.

~未完待續~








2014年4月21日 星期一

李光耀談未來十年

今天收到同事寄來的分享, 是李光耀對各個主要國家的分析, 其中關於中東的看法與我相同, 我認為世界是否爆發核戰, 關鍵在於美國是否能牢牢約制以色列, 不要輕率地攻擊伊朗. 這世界若是從迦南地開始創世紀, 那麼滅世紀也可能會從那裏開始.

李氏的看法節錄如下 :

"往後十年,世界將會面臨最大的問題是什麼?
第一,是歐元區。如果希臘債務危機不能處理得當,將波及葡萄牙、西班牙和義大利。接下來就會出現連鎖反應,不僅傷害歐洲的經濟,也傷害到美國和中國的經濟。
第二,是北朝鮮這個長年不斷的問題。年輕的金正恩上台了,試圖向全世界展現他和先人一樣大膽、敢冒險。
第三,是日本的停滯,這間接影響到整個亞太地區。日本社會老化已妨礙經濟。日本因為希望維持民族血統純正,也不接受外來移民。
第四,是中東可能因為伊朗正在發展核彈而爆發衝突,這勢必對市場產生災難性的重大衝擊。伊朗的核子計畫是全世界最可能搞砸了的挑戰。中國和俄羅斯不太可能執行聯合國的制裁行動,並且如果伊朗覺得他們執行制裁行動的可能性不高,伊朗將當成默許而繼續研製核彈。不論美國是否支持,以色列在某個時點將被迫決定,是否要試圖摧毀伊朗固若金湯的地下掩體。如果伊朗有了核彈,沙烏地阿拉伯將向巴基斯坦買核彈,埃及也會向別人買核彈,接下來是中東各國競相核武化。再來,這個地區爆發核子戰爭只是時間上的問題。"


2014年4月20日 星期日

Java 的 MD5 加密

現在公司上上下下在瘋資安, 所有的資訊系統都要進行管控, 以往登入比對都是用明碼 (趕時間上線, 能用就行), 管理員都知道每一個人的密碼, 為了怕瓜田李下, 我都要人家隨便用 123 就好, 反正內部網路, 也不怕外部入侵. 但資安來了就不能便宜行事, 所以登入密碼都要加密.

最常用的加密系統是 md5, 例如 PHP 就提供了 md5() 函式, 非常方便.

# http://www.w3school.com.cn/php/func_string_md5.asp


那 Java 該怎麼做呢? 這要用到 java.security.MessageDigest 這個抽象類別, 呼叫其 getInstance() 並傳入 "MD5" 字串指定其加密演算法就會傳回一個實作此演算法的 MessageDigest 物件, 呼叫此物件的 digest() 方法, 並將欲加密的字串以 byte 陣列方式傳入, 就會傳回以 MD5 加密之 byte 陣列. 其 API 見 :

# http://docs.oracle.com/javase/7/docs/api/java/security/MessageDigest.html

以下範例是我參考 "Java 程式開發 349 例" 一書中的例子改寫的, 

import java.security.MessageDigest;
public class mytest {
  public static void main(String[] args) {
    String str="I love you";
    System.out.println(md5(str));  //輸出 E4F58A805A6E1FD0F6BEF58C86F9CEB3
    }
  public static String md5(String str) {
    String md5=null;
    try {
      MessageDigest md=MessageDigest.getInstance("MD5");
      byte[] barr=md.digest(str.getBytes());  //將 byte 陣列加密
      StringBuffer sb=new StringBuffer();  //將 byte 陣列轉成 16 進制
      for (int i=0; i < barr.length; i++) {sb.append(byte2Hex(barr[i]));}
      String hex=sb.toString();
      md5=hex.toUpperCase(); //一律轉成大寫
      }
    catch(Exception e) {e.printStackTrace();}
    return md5;
    }
  public static String byte2Hex(byte b) {
    String[] h={"0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"};
    int i=b;
    if (i < 0) {i += 256;}
    return h[i/16] + h[i%16];
    }
  }

這裡的 byte2Hex() 方法是用來將經過 md5 加密的每一個 byte 轉成 16 進位表示, 所以 "I love you" 經加密運算後會變成 "E4F58A805A6E1FD0F6BEF58C86F9CEB3". 依據 Wiki 的 MD5 說明, MD5 是一種雜湊演算法, 它會固定生成 32 位的 16 進制數字 (即 128 位元). 即使原文只變動一點點, 演算出來的值卻變動相當大. 例如我們在上面例子中加個驚嘆號 "I love you!", 其 MD5 演算結果變成 "690A8CDA8894E37A6FFF4D1790D53B33", 幾乎完全不一樣了.

最重要的特性是, MD5 是不可逆的, 也就是沒辦法從 "E4F58A805A6E1FD0F6BEF58C86F9CEB3" 反推算出 "I love you", 除非利用超級電腦, 但那也需要很久的時間, 所以只能把它的對應關係記下來. 我在網路上找到下面這個號稱能反解譯的網站, 大約 5 秒就說解出 "I love you" 了, 但要看結果要給錢, 哇咧.

# md5在线查询破解,md5解密加密


據說 MD5 在 2009 年被破解了, 在一般電腦只需幾秒鐘, 搞不好這網站是利用這種破解法來做生意的.


關於丁守中

最近國民黨北市長初選落幕, 丁守中敗給了連勝文, 我想以後大概沒機會再選了. 我對丁守中沒啥特殊印象, 就是中規中矩的政治人物而已, 出身高雄市, 父親是來台軍醫, 母親是客家人, 妻子來頭較大, 是前聯勤總司令溫哈熊的女兒. 此君志在成為台北市長, 從挑戰黃大洲開始, 到馬英九, 郝龍斌, 每次初選都敗北, 屢敗屢戰, 毅力驚人.

因為此次選舉, 我注意到丁守中與眾不同之處, 在於其反核立場. 他是國民黨內, 敢違背黨意投下反核一票的極稀有政治人物, 參看 Wiki 敘述 :

"丁守中表示自己雖然是黨內少數,但不怕黨內異樣眼光,「我對核四的立場在黨內雖是少數,但我的聲音大!」。並且指出經過深入研究後發現,核四廠問題叢生,如果發生意外,後果不堪設想,因此認同應該停止核四廠的興建,重新檢討核電政策的妥適性。"

政治人物有無堅持, 就看這些地方. 如果只是為了自己的前途, 怕被黨與當權者拋棄, 而不顧子孫萬代安危, 那就是靠政治吃飯的政客. 比起那個講話時眼睛一直在眨眼 (請查一下面相術的書) 的跟那個從來沒做過啥事的官 (富) 三代, 以當台北市長為志的丁君應該是不錯的人選, 但他終究被拋棄了.

塞翁失馬焉知非福, 人生的得失豈是一時可定論. 陳水扁若沒有在北市長連任失利, 又怎會選上總統? 若非選上總統, 又怎會晚年繫獄? 馬英九如果不是選上總統, 現在還是全國婦女同胞最愛的小馬哥呢!

十年河東十年河西, 不到最後, 還真不知禍福呢! 這是我給丁守中最誠摯的祝福.

2014年4月19日 星期六

求星期幾的計算法

今天翻閱 "Java 程式開發 349 例" 這本書時, 在字串那章看到一個輸入日期, 求星期幾的方法. 雖然可以用 Calendar 類別很快就可以求得, 但純用演算法求取倒很有趣, 說不定 Calendar 裡面也是這麼算的 :

public class test1 {
  public static void main(String[] args) {
    System.out.println(getDay(2014,4,19));  //輸出  : 六
    }
  public static String getDay(int y, int m, int d) {
    if (m==1||m==2) {m += 12; y--;}
    int week=d + 2*m + 3*(m+1)/5 + y + y/4 - y/100 + y/400 + 1;
    week=week % 7;
    String w="日一二三四五六".substring(week, week+1);
    return w;
    }
  }


樹苺派安裝 PiOS

自從前幾天更新樹苺派系統後, SSH 連線常無緣無故中斷, ping 有時好有時壞, 一時也不清楚哪裏出問題. 乾脆重裝 Rasbian 好了, 反正從來沒用過. 剛好找到硬派製作介紹的 PiOS, 所以找張 SD 卡來裝看看 :

# 下載PiOS作業系統

參考之前的文章 :

# 樹苺派的 SD 卡與作業系統安裝

還要下載 Win32 Disk Imager 來燒錄映像檔 :

# Win32 Disk Imager 安裝版  

在燒錄 PiOS 映像檔到 SD 卡之前, 最好先用 SD Formatter 將其格式化 :

# 下載 SD Formatter   下載使用說明書 (繁中版)  


妖言惑眾

今天看到聯合報這篇, 突然領悟, 學生不在宿舍吹冷氣滑手機, 卻要跑去搞學運的原因, 就是人民對馬卡茸所帶領的 "馬上好" 詐騙集團已經忍無可忍, 馬集團最大的本事就是, 顛倒黑白, 妖言惑眾, 像這個經濟部長當不好被降成政委, 不好好搞經濟, 卻喜歡跑來跟我們搶生意寫部落格的人就是一例 :

# 尹啟銘:民進黨當年編448億建核四

人類對核能技術的掌握, 其實在上個世紀末已經非常成熟了, 了解物理學的人應該毫無疑問. 但核四的問題跟原子能技術一點關係都沒有, 我反對的理由是, 完全是管理上的隱憂, 以及核廢料無法解決的緣故. 核電廠絕對不會像廣島的原子彈那樣爆炸, 但是因為管理不善所造成的核災, 其影響卻比一顆廣島原子彈還深遠.

但馬卡茸詐騙集團一再曲解歷史的作為卻令人憤怒. 核四這個錢坑當年張俊雄下令停建就 OVER 的話, 今天就不會變成超大型錢坑了. 是誰讓它起死回生?

節錄維基百科如下 :
  1. 1985年5月因貢寮鄉民強烈反對, 蔣經國指示行政院暫緩興建. 
  2. 1986年發生車諾比事件, 預算遭立法院凍結.
  3. 2000年, 陳水扁指示「核四再評估」, 經濟部長林信義表示真正損失金額約為400億. 行政院長唐飛因主張續建核四請辭, 張俊雄繼任, 宣佈核四停建, 國民黨, 親民黨, 新黨要求監察院彈劾行政院, 連署「罷免總統副總統案」送交立法院
  4. 2001 年, 立院國民黨, 親民黨, 新黨等提案決議, 反對行政院停建核四, 要求復工. 在巨大壓力下, 行政院長張俊雄與立法院長王金平簽署協議書, 達成核四復工協議. 台電預計核四 2006 年商轉. 
很清楚, 當年民進黨政府想要兌現廢核四政治支票, 但遭到國親新聯合反對, 以罷免陳水扁與彈劾張俊雄為武器, 逼使陳水扁妥協, 廢核四弄得灰頭土臉, 只好繼續編預算塞錢坑. 這是朝小野大的政治現實, 但是這位 "光風霽月" 的尹先生卻把這段歷史解釋為民進黨當政時也編列預算贊成核四, 那真是妖言惑眾, 如果民進黨當年上台後, 真的將反核理念棄置不顧, 變成擁核, 那它現在真的沒資格再高唱反核了. 馬卡茸詐騙集團指鹿為馬, 指茸為毛的功力實在令人嘆為觀止.

"2011年6月13日 立法院審查100年度總預算附屬單位營業及非營業部分預算案,其中包含核四140億追加預算。在國民黨團人數優勢之下,民進黨提案全數遭到否決,通過140億追加預算。" (維基百科 : 龍門核能發電廠)

用過的燃料棒是高階核廢料, 還會繼續發熱與釋放輻射能, 原本要用濕式儲存方式來減低其禍害, 但儲存池很快就爆滿, 建不勝建, 而且這個人口密度超標的島國還有哪裡可建? 甚至美國也有這個問題, 於是發展出乾式貯存技術, 但那不是把剛從反應爐取出的廢燃料棒直接放在容器中置放, 而是把以前放在貯存池中很久的廢燃料棒取出, 放到乾式貯存槽中, 然後騰空的位置就放新出來的廢料. 乾式貯存槽還要不斷換氣才行 (毒氣!), 否則會發生氫爆, 只有美國這種地廣人稀的國家才有辦法. 請參考 :

# 〈廢核救家園〉乾式貯存 台灣不可行

廢核成本從林信義那時的 400 億, 膨脹到現在的數千億, 是誰搞成這樣的? 尹啟銘還有臉指責民進黨嗎? 現在廢核要花數千億, 一旦發生核災, 要花萬萬億 (好像也沒辦法花耶! 因為中央政府全部陣亡, 僅存的中部東部縣市都很窮). 擁核是自私的行為, 把核廢料送到別國處理就是以鄰為壑, 把核廢料留給下一代不是很自私嗎?

擁核者常說廢核就會缺電漲價, 如果要漲電價, 那就漲吧! 核災發生後就沒機會再漲了. 記住, 原子能是上帝的權柄. 核災即使只有億萬分之一的機率, 都不容許發生.

2014年4月18日 星期五

樹苺派編輯器


# Install Sublime text editor
# 在 Windows 設置 Raspberry Pi (樹莓派) 遠端編輯環境
# 第十章、vim 程式編輯器
10個最實用的Linux指令 

樹苺派的 shell

跟市圖借的 "Linux Shell 程式設計實務" 已放在書架月餘, 趁著昨日 "心猿意馬症" 發作, 突然把玩樹苺派, 於是拿起來翻閱, 最前面講到 Linex Shell 版本, 馬上連線到主機看看, 果然 Rasbian 預設 Shell 是 Bash, 昨晚更新後為 4.2.37(1) 版 :

login as: pi
pi@192.168.2.107's password:
Linux raspberrypi 3.10.25+ #622 PREEMPT Fri Jan 3 18:41:00 GMT 2014 armv6l

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Fri Apr 18 00:08:35 2014 from 192.168.2.108

首先檢查預設是哪一種 shell :

pi@raspberrypi ~ $ echo $SHELL
/bin/bash

是 bash 沒錯, 但 list /bin/sh 卻顯示 dash, 而非 bash, why?

pi@raspberrypi ~ $ ls -la /bin/sh
lrwxrwxrwx 1 root root 4 Jan  1  1970 /bin/sh -> dash


檢查 bash 版本 :

pi@raspberrypi ~ $ echo $BASH_VERSION
4.2.37(1)-release

是 4.2.37 版. 

pi@raspberrypi ~ $ pwd
/home/pi

檢查 Linux 更新來源 (在 /etc/apt/) : 

pi@raspberrypi ~ $ ls -l /etc/apt
total 36
drwxr-xr-x 2 root root 4096 Apr 17 23:07 apt.conf.d
drwxr-xr-x 2 root root 4096 Mar 21  2013 preferences.d
-rw-r--r-- 1 root root   82 Sep 26  2013 sources.list
drwxr-xr-x 2 root root 4096 Jan  1  1970 sources.list.d
-rw------- 1 root root 1200 Sep 26  2013 trustdb.gpg
-rw------- 1 root root 4669 Sep 26  2013 trusted.gpg
-rw------- 1 root root 2424 Sep 26  2013 trusted.gpg~
drwxr-xr-x 2 root root 4096 Mar 21  2013 trusted.gpg.d

顯示 sources.list 內容為連線 Rasbian 官網更新 wheezy :

pi@raspberrypi ~ $ cat /etc/apt/sources.list
deb http://mirrordirector.raspbian.org/raspbian/ wheezy main contrib non-free rpi

以下一邊做閱讀整理, 一邊實際操作 RPi 看看 :

Shell 是啥?

Shell=使用者與 Linux kernel 之間的溝通介面, 例如 DOS 是 Windows 的 shell, 但它只是命令直譯器 + 批次檔功能, 而 Linux shell 選擇性多, 且是一種程式語言, 是 Linux 系統管理程式語言的工具, 也是 Linux 作業系統的三個部份之一 :
  1. Kernel
  2. Shell
  3. 工具程式
常見的 shell 有 :
  1. bash shell (Linux 套件多使用此)
  2. Korn shell (OpenBSD/NetBSD 採用)
  3. C shell (FreeBSD 採用)
Shell 的發明者 : Steve Bourne, 其 Bourne shell 在 UNIX 第七版開始使用 (1979). 而 Linux 所用的 Bash shell 是其變種 (1988), 原作者 Brian Fox, 其官網在 :

# http://www.gnu.org/software/bash

Bash 有兩種工作模式 :
  1. 互動模式 (人工操作模式)
  2. Shell script 模式 (自動化模式)

列出 RPi 根目錄下的檔案 : ls /

pi@raspberrypi ~ $ ls /
bin   dev  home  lost+found  mnt  proc  run   selinux  sys  usr
boot  etc  lib   media       opt  root  sbin  srv      tmp  var

第一個 shell script :

照書中說明用 vi 或 nano 編輯一個最簡單的 shell script :

echo '哈囉!世界, hello world'

存成 hello.sh, 但不能直接執行 hello.sh, 因為它沒有執行權限, 會出現 command not found :

pi@raspberrypi ~ $ hello.sh
-bash: hello.sh: command not found

須用 chmod 改為可執行 :

pi@raspberrypi ~ $ chmod +x hello.sh
pi@raspberrypi ~ $ ls -l
total 44
drwxr-xr-x 2 pi pi 4096 Jan  1  1970 Desktop
drwxr-xr-x 3 pi pi 4096 Feb 13 19:50 Documents
-rwxr-xr-x 1 pi pi   34 Apr 18 22:49 hello.sh

輸入 hello.sh 還是 command not found, 因為必須在前面加上 ./ 表示 hello.sh 是在目前目錄下 :

pi@raspberrypi ~ $ ./hello.sh
哈囉!世界, hello world

原來 shell script 這麼簡單 ! (還沒遇到難的吧?)

2014年4月17日 星期四

樹苺派安裝 PHP5+MySQL

我照葉難的這篇文章給我的樹苺派安裝了 PHP5+MySQL :

# Raspberry Pi:安裝Apache、MySQL、PHP

首先用下列指令更新套件 :

$ sudo apt-get update; sudo apt-get dist-upgrade -y

這指令執行下去, 會更新 Debian "Weezy", 花了很久時間, 讓我懷疑是否有必要做更新, 或許直接安裝 PHP+MySQL 就好了. 整個更新記錄放在下面 :

# http://mybidrobot.allalla.com/linux/raspberry-update-20140417.txt


至於空間不用擔心, 我的 SD 卡是 8G 的, 絕對夠裝 PHP 與 MySQL.

2017-01-25 補充 :

今天看到這篇有頭無尾的舊文章才知道原來上一次嘗試在樹莓派上安裝 LAMP 已經是快三年前的事了. 昨天才真正完成這項作業, 參見 :

樹莓派架設 PHP 網站伺服器

樹苺派安裝 Derby 資料庫

我的樹苺派好久沒開機了, 今天給它換了一個 1A 的小電源頭, 因為原先買的那個 2A 的供電時會有高頻雜音. 1A 的驅動沒問題, 因為 USB 只插一個無線網卡而已, 若再插個隨身碟不知能否撐得下去.

我在樹苺派下 ij 指令無法執行, 可知其 Linux 雖內建 JDK, 但卻沒有安裝 Derby 資料庫. 網路上找到下面這篇教導如何安裝 Derby, 但卻是德文, 用 Google 翻譯約略可看懂 :

# http://dev.uhilger.de/dok/ulrich/Artikel/Serverbetrieb/Serveranleitung

不過卻找到一個安裝 SQLite 的教學 :

# Set up an SQLite database on a Raspberry Pi

只要是免費好用都可以. 下面這篇也是建議安裝 SQLite :

# Database on Raspberry pi

下面這篇提到, Derby 只支援 Java, 移植性差.

# http://jyhshin.pixnet.net/blog/post/31913253-sqlite-%E5%88%9D%E9%AB%94%E9%A9%97



~ 未完待續 ~


2014年4月16日 星期三

授課

本周都在忙著寫簡報, 因為今日下午要授課, 趁此時機, 把研究規約的心得好好地畫成幾張超棒的圖, 連自己都覺得很有成就感.  但是一談到規約, 大家都昏昏欲睡, 我抬頭一看, 僅小貓兩三隻還有在聽, 我覺淂已經很賣力演出, 但為何大家還是睡得著?

下班時一個同事稱讚說我對規約很熟, 呵呵, 總算有人聽進去了. 但其實我很心虛, 我的努力根本不夠. 前年去上海研習時, 聽到ㄧ個大陸學員說他規約都讀了不下七八遍, 哇咧, 我讀完一遍都覺淂尾巴快翹起來了, 人家卻是七遍以上, 對岸的真的很拼啊!

最近隨著 Java 複習進度前進到網路程式設計, 也開始要好好認真研究 TCP/IP 了, 因為公司的系統已經邁向全 IP 化了, 不搞懂這些不行.


水電裝修

上週某一天, 管理員通知我到我樓下七樓住戶家, 說是房間屋頂滲水, 疑似來自我家後陽台, 要我一同會勘. 我看後同意樓下說法, 論位置確實與洗衣機大約相同, 故答應找時間去特力屋問問看, 是否能來看看係何原因以及改善之道. 一週來每天一下班我就到後陽台觀察, 發現只要有洗衣服, 漏水口就會潮濕, 因此懷疑跟洗衣機漏水口水管有關, 或者是水龍頭滴水導致. 特力屋說沒有幫人改善滲水問題, 建議找水電.

由於廚房水龍頭也年久會滴水了, 故晚上再去特力屋, 買了廚房的雙管水龍頭一支 ($990, 和成的 $1990), 兩支小龍頭, 蓮蓬頭, 曬衣架等, 花了 3000 元, 哇咧, DIY 光是材料就不少錢了, 如果找人安裝, 工錢佳車馬費至少要加 $1000, 做水電工其實也不錯 (唸電機的還好意思找人安裝嗎).

2014年4月13日 星期日

錯誤的解讀

最近反服貿雖然落幕了, 但台灣各角落對於這事件的看法卻是南轅北轍, 相當兩極. 即便是學界或財經界人士, 看法也是 ... 南轅北轍.

同事中有人指責學生, 說不懂服貿還在亂反, 說得我也心慌慌, 無法反駁, 沒錯, 我懂嗎? 我真的懂嗎? 但反過來, 你懂嗎? 你真的懂嗎? 連財經專家都各唱各的調, 這說明什麼? 這不是純經濟問題, 是政治問題. 同事說, 這根本不是反服貿, 是反馬英九. 對, 也不對. 我認為既反服貿, 當然也反馬英九, 他是始作庸者, 不是嗎? 去年九月馬王鬥的背後原因, 證諸今日的局勢發展, 不是脈絡分明嗎?

正確解讀社會現象, 是精算政治趨勢的要件之一. 錯誤的解讀終將導致錯誤的決策, 未來的變局也勢必一發不可收拾. 罵反服貿者可能從單純反馬來解讀, 更把占議場視為不守法, 破壞體制來斥責. 其實我常在想, 所謂政治問題, 其實不是法律所能處理的, 甚至可能是違反法律的, 法律只能處理制度性問題, 無法解決民之所向問題, 民之所向要靠選票, 但如果人民已經忍無可忍, 怎麼辦? 這就是政治問題.

當年阿扁的貪腐問題, 導致紅衫軍靜坐, 示威, 圍城, 逾期不解散, 這不是違反集會遊行法嗎? 破壞古蹟台北賓館, 甚至國慶日當天驚擾外賓, 破壞國家慶典, 那又是該當何罪呢? 罹患歷史健忘症的, 請回顧一下這段歷史 :

# 百萬人民倒扁運動

"10月10日雙十節,當天的國慶大典上,國民黨立委於總統致詞時,在觀禮台上高呼倒扁口號及比劃倒扁手勢,因而與民進黨立委發生肢體衝突。而親民黨主席宋楚瑜則穿著紅西裝帶領親民黨立委於典禮中途退席,在府前的重慶南路上遊走,立委劉文雄則趁三軍儀隊正通過觀禮台時突然竄入隊伍中,與安全人員大玩追逐遊戲。最後由國民黨立委手持布條自成一隊,高呼倒扁口號經過觀禮台,與三軍儀隊交錯而過。觀禮台上的外賓則是非常訝異,紛紛拍照留念。事後親民黨立委黃義交認為,不如此激情演出,不足以表達在野黨的強硬立場。"

"事後行政院長蘇貞昌批評紅衫軍沒有遵守向立法院長王金平所作的「國慶不鬧場」之承諾,同時更讓外賓進出車輛遭拍打驚嚇。民進黨主席游錫堃批評泛藍和倒扁群眾刻意破壞國家慶典,讓國慶日變成國恥日。負責籌備國慶活動的立法院長王金平則說,這是台灣社會多元化的一面,不必太大驚小怪,大家過了就好。而美國在台協會台北辦事處處長楊甦棣應邀出席國慶大典,對此大鬧國慶大典的現象感到相當不可思議,並反問:「大家不都是應該要有禮貌嗎?」。"

看看王金平當時的回答, 呵呵, 用多元化一語帶過, 這個人真的是老 ..... 先覺, 國家尊嚴算個屁. 在外賓面前破壞國慶, 這已經不是占領立法院足堪比擬了, 為了丟陳水扁的臉, 連國家的臉也一起丟, 此種功力, 你林飛帆, 陳為廷比起來算是遜咖了. 那這樣孔子是不是該出來道歉一下, "抱歉, 我們沒有把學生教好" 呢?

這就是政治問題, 是否該用法律處理? 有哪一條法律可以處理呢? 陳水扁當時有處理嗎? 怎麼處理的呢? 蒐證, 採集指紋? 依法辦理?

最後看看民調, 了解一下所謂的 "沉默的多數" 到底有多多呢 ?

# 臺灣對於太陽花學運的反應

"TVBS民調中心在3月24日抽問866位20歲以上臺灣民眾,其中有63%的人認為應該要退回《海峽兩岸服務貿易協議》重新審查,另外分別有18%以及9%的人認為不必要或者沒有意見。而對於學生佔領立法院的舉動其中有51%的贊成者,而反對人數與無意見人數則分別佔38%以及11%。而對於佔領行政院的舉動則有30%的人支持,但是有58%的人不支持並且有12%的人沒意見。而《蘋果日報》在3月26日抽問抽問818名臺灣民眾後,有69.8%民眾同意學生所提的《兩岸協議監督條例》先立法後再協議《海峽兩岸服務貿易協議》,20%的受訪者則認為應該可以直接審審理《海峽兩岸服務貿易協議》,另外有10.15%的民眾則對此沒有意見。而商業週刊《今周刊》調查大學經濟系系主任、經濟相關智庫院長等27份問卷,其中有12位表示應該簽署《海峽兩岸服務貿易協議》、另外1位則表示不應該,不過其中也有2位學者強調「應該簽,但並非目前執政黨推出的版本」。而在對於民眾的調查中有84%的人認為馬英九政府事前與民眾的溝通不足,另外還有56%的民眾不支持政府和中國簽署《海峽兩岸服務貿易協議》。"

愚以為, 將政治問題錯誤地解讀為純經貿問題有時並非笨, 而可能是 .... 聰明.

PS : 天下圍攻的所謂理想 : "黨產條例草案" 最終命運如何了呢?

(小提示 : 2008 年馬英九上台後就撤回, 至今無蹤影)

所謂的理想, 呵呵.



2014年4月12日 星期六

Java 資料庫存取 : 使用 Derby

完成 ACCESS 資料庫函式庫從 Javascript 版改寫為 Java 版後, 本來整個常用函式庫移植工作就告一段落, 可以應用在公司主機系統的資料自動擷取與分析了, 但今日陪姐姐去南二中考試時, 看完幾乎半本 "Java 網路程式設計 (學貫, 謝兆賢)", 裡面有一章介紹 JavaDB (Derby), 看完覺得還不錯用, 因為 Derby 是在安裝完 JDK 時就已內建了, 所以可以當成內嵌的資料庫來用, 如果伺服器有裝 JDK, 對於小型使用量而言, 那就可以直接使用, 不需安裝其他資料庫了.

其實吸引我興趣的是, Derby 沒有 2GB 限制! 這正是我目前使用的 ACCESS 的最大缺憾, 例如公司的工作日誌系統資料庫大約兩三年左右就會到達 2G 的天險而爆掉, 每次都耗掉我一整天的時間去搞升版. ACCESS 的好處是單一檔案, 離線處理後上傳伺服器很方便, 而且有 GUI 介面, 比較親民些. 關於 Derby 與 ACCESS 的比較, 參見 :

# Derby vs. ACCESS

關於 Derby 的限制, 參見 :

# Limitations for database values in Derby

Derby 也是英國中部的一個城市 (英格蘭), 在英文則是一種禮帽.


Apache 取之作為資料庫名稱 (Apache DB Project), 官網參見 :

# http://db.apache.org/derby/

使用方法 :

# Derby Tutorial
# ij Basics
# https://builds.apache.org/job/Derby-docs/lastSuccessfulBuild/artifact/trunk/out/getstart/index.html

Java 將 Derby 納入作為內建資料庫, 安裝 JDK 時一併安裝, 位置在 JDK 目錄的 db 資料夾下面, 以我的 XP 電腦的 JDK 1.7.0 來說, 其路徑為 :

C:\Program Files\Java\jdk1.7.0\db

整個目錄僅 4.8M, 比起 ACCESS 簡直是小巫見大巫, 但功能卻絲毫不遜色. 不過 Derby 沒有 GUI 介面, 完全必須利用命令行下指令, 故最好設定 PATH, 以便在任意路徑下都能方便使用. 以我的電腦來說, 是在 "控制台/系統/進階/環境變數", 在 PATH 變數尾端加入 :

C:\Program Files\Java\jdk1.7.0\db\bin;

確定後打開命令提示字元視窗, 輸入 ij 就可以進入 Derby 了 :


要跳出 Derby 可按 Ctrl+C, 回答 Y 即離開 Derby 命令行.

在 Derby 中建立資料庫需使用 connect 指令 :

connect 'jdbc:derby:testdb;create=true';

注意, 不可以用雙引號, 必須用單引號, 否則無法建立資料庫連線 :


如果在目前目錄下是第一次連線此資料庫, 那麼上面的連線指令實際上是建立一個空白資料庫, 並建立連線; 如果已經建立過同名資料庫, 那麼就只是建立連線而已 :

ij> connect 'jdbc:derby:testdb;create=true';
警告 01J01:未建立資料庫 'testdb',會改成建立現有資料庫的連線

資料庫連線建立成功後, 會在目前目錄下產生該資料庫之目錄, 由於上面我是在 D:\java 底下以 ij 指令進入 Derby, 因此 connect 指令會在 D:\java 下建立 testdb 目錄, 大小只有 1.75MB :


其架構請參考 :

http://db.apache.org/derby/docs/10.0/manuals/develop/develop13.html

注意喲, 在這篇文章中有一段提到 :

"There is no drop database command. To drop a database, delete the database directory with operating system commands. "

也就是 Derby 沒有 DROP DATABASE 的指令, 要刪除資料庫直接將其目錄刪除即可. 如果在 ij 介面用下列指令會顯示錯誤, 表示無法刪除資料庫目錄 :

ij> connect 'jdbc:derby:testdb;drop=true';
錯誤 XBM0I:無法移除 testdb 目錄。

但在下面這篇文章, 作者表示若資料庫建在 memory 下則可以刪除 memory 下的資料庫, 但 memory 仍存在 :

# You can drop a file-system database from a directory named "memory"

我實際測試結果卻還是一樣, 不會刪除 testdb 資料庫.

接下來第一個操作是建立資料表, 參考我們在 ACCESS 資料庫所用的 users 資料表 :

# Java 資料庫存取 : 使用 ACCESS
# Java 資料庫存取 : 使用 ACCESS (續)

我們要建立 users 資料表, 內有 id, name, 與 gender 三個欄位, 在 ACCESS 用的 SQL 是 :

create table users(id AUTOINCREMENT PRIMARY KEY, name varchar(20), gender varchar(5))

但這在 Derby 不能用, 主要是自動增量欄位 id 的宣告方式 :

D:\Java>ij
ij 版本 10.8
ij> connect 'jdbc:derby:testdb;create=true';
警告 01J01:未建立資料庫 'testdb',會改成建立現有資料庫的連線。
ij> create table users(id AUTOINCREMENT PRIMARY KEY, name varchar(20), gender va
rchar(5));
錯誤 42X94:TYPE 'AUTOINCREMENT' 不存在。

所以 Derby 不認得 AUTOINCREMENT. 參考下列文章 :

# http://www.binarytides.com/create-autoincrement-columnfield-in-apache-derby/

得知 SQL 要改為如下格式 :

ij> create table users(id INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1), name varchar(20), gender varchar(5), CONSTRAINT primary_key PRIMARY KEY (id));
已插入/更新/刪除 0 列

可見僅顯示 insert/update/delete 訊息, 新增資料表是否成功也看不出來, 反正沒有 error 就是成功啦.

第二個操作是新增紀錄到資料表中. SQL 格式與 ACCESS 是一樣的 :

ij> insert into users(name, gender) values('愛咪','女');
已插入/更新/刪除 1 列
ij> insert into users(name, gender) values('彼得','男');
已插入/更新/刪除 1 列
ij> insert into users(name, gender) values('凱莉','女');
已插入/更新/刪除 1 列

第三個操作是顯示資料表內容 :

ij> select * from users;
ID         |NAME                |GEND&
--------------------------------------
2          |愛咪                  |女
3          |彼得                  |男
4          |凱莉                  |女

已選取 3 列

可見欄位名稱最多顯示五個字元, 但很奇怪地, id 怎會從 2 開始編? why?  

第四個操作是更新記錄 :

ij> update users set name='湯尼' where id=2;
已插入/更新/刪除 1 列
ij> select * from users;
ID         |NAME                |GEND&
--------------------------------------
2          |湯尼                  |女
3          |彼得                  |男
4          |凱莉                  |女

第五個操作是刪除記錄 :

ij> delete from users where id=2;
已插入/更新/刪除 1 列
ij> select * from users;
ID         |NAME                |GEND&
--------------------------------------
3          |彼得                  |男
4          |凱莉                  |女

已選取 2 列

注意, 這裡跟 ACCESS 不同的是不需要 *.

如果沒有加 where 條件, 則全部紀錄會被刪除, 變成空資料表 :

ij> delete from users;
已插入/更新/刪除 2 列
ij> select * from users;
ID         |NAME                |GEND&
--------------------------------------

已選取 0 列

最後一個操作是刪除資料表 :

ij> drop table users;
已插入/更新/刪除 0 列

若要顯示目前的連線, 可用

ij> show connections;
CONNECTION0* -  jdbc:derby:testdb
* = 現行連線

其他指令可下 help 顯示 :

ij> help;

參考 Derby 文件 :

# https://builds.apache.org/job/Derby-docs/lastSuccessfulBuild/artifact/trunk/out/getstart/twwdactivity2.html
# Connect to Apache Derby (Java DB) via JDBC