Archive for 2009/7/2
私はテーブルを作成するときには、いつもXMLで自分流に定義して、それを使ってテーブルをcreateする。
こうするといいことがいくつかある。
-
ドキュメントをxsltなどで自動的につくれる。
いろいろなライブラリがすでにあるテーブルからDBにアクセス用に自動的にオブジェクトを作るが、私はXMLから作成している。
それは、結局ドキュメント化されなければいけないのであれば、やっぱりドキュメントが自動のほうが嬉しい。 -
テーブルの変化(バージョン管理)もXML(ドキュメント)に含められる。
だいたい、規定していない属性、項目は処理しない。したがってそれを使ってドキュメントに使える。
また、テーブル追加や削除( alter table )もそこでやったこともあったなー。これはいらなかったかな。 -
データベースの違いを吸収できる。
まあ、AIRの場合にはSQLiteしかないから関係ないが・・・
でも、サーバでMySQLを使って、そのデータをAIRのSQLiteでキャッシュするというときなんか、同じようなフォーマットで共通化できるから便利じゃないかな。
たとえば、booleanとか。MySQLのむかーしのバージョンではなかったんだよね。(4.1から導入されたらしい)
だからこれで定義があると、tinyint(1)と書き換えていた。
でも、いいことばかりじゃない。
複雑なテーブルは作りにくい。でも、逆にいえばこれはいいことともいえる。
そして、これが一番の難題。自分で"create table foo(..... " あれ?つぎなんだっけ?と、文法を忘れる。
したがって、自分で手で作成するときにはリファレンスがないとわからない。
あと、3でも書いたが、違いを吸収してしまうので、新しくできた機能、変化を気にしなくなる。
さて、では実際にはどんな感じでXMLを書くのか?
-
<?xml version="1.0" encoding="UTF-8" ?>
-
<table name="status" dbName="its">
-
<field name="sysid" type="integer" primary="true" auto_increment="true">
-
<title>システム用ID</title>
-
</field>
-
<field name="server_id" type="integer" required="true">
-
<title>サーバID</title>
-
<description>どのサーバのデータか?</description>
-
</field>
-
<field name="uid" type="integer" required="true">
-
<title>ユーザID</title>
-
<description>サーバ上のユニークIDが入る。ただし、ローカルの場合にはsequenceを使う</description>
-
</field>
-
<field name="createdAt" type="timestamp" required="true">
-
<title>作成日時</title>
-
</field>
-
<field name="updatedAt" type="timestamp" required="true" />
-
<field name="createdBy" type="integer" required="true" />
-
<field name="updatedBy" type="integer" required="true" />
-
<field name="sync" type="integer" required="true" />
-
<field name="syncAt" type="timestamp" />
-
-
<!-- 人が管理するためのフィールド -->
-
<field name="title" type="varchar" size="255" required="true" />
-
<field name="type" type="integer" default="-1" />
-
<field name="memo" type="text" />
-
<field name="enableNew" type="boolean" required="true" />
-
<field name="isRoot" type="boolean" required="true" />
-
<field name="weight" type="integer" default="0">
-
<title>表示する際の重み</title>
-
</field>
-
<sequence name="uid" />
-
</table>
ってな感じです。
titleとか、descriptionとかはドキュメント用。
あと、sequence はcreate sequenceなんだけど、SQLiteにはないから代替の処理。
MySQLにもないけど、PostgreSQLにはあるからこの場合にはふつうにcreate sequenceにする。
/**
* Copyright (c) 2009 coltware@gmail.com
* http://www.coltware.com
*
* XMLからテーブルを作成する
*
*/
package com.coltware.cise.db
{
import flash.data.SQLConnection;
import flash.data.SQLResult;
import flash.data.SQLStatement;
import flash.events.SQLErrorEvent;
import flash.events.SQLEvent;
import mx.logging.ILogger;
import mx.logging.Log;
public class TableFactory {
private var _conn:SQLConnection;
private var _xml:XML;
private static const _log:ILogger = Log.getLogger("com.coltware.cise.db.TableFactory");
public var lastSql:String = "";
public function TableFactory() {
}
/**
*
*/
public function set connection(conn:SQLConnection):void{
this._conn = conn;
}
/**
* 定義XMLの設定
*/
public function set xml(defxml:XML):void{
this._xml = defxml;
}
public function dropIfExist():String{
var tableName:String = this._xml.@name;
lastSql = "DROP TABLE IF EXISTS " + tableName;
_log.debug("SQL : " + lastSql);
return lastSql;
}
public function drop(resultFunc:Function = null,errorFunc:Function = null):SQLStatement{
var stmt:SQLStatement = new SQLStatement();
stmt.sqlConnection = this._conn;
stmt.text = dropIfExist();
if(resultFunc != null){
stmt.addEventListener(SQLEvent.RESULT,resultFunc);
}
if(errorFunc != null){
stmt.addEventListener(SQLErrorEvent.ERROR,errorFunc);
}
stmt.execute();
return stmt;
}
public function handleCreate(e:SQLEvent):void{
var stmt:SQLStatement = e.target as SQLStatement;
var result:SQLResult = stmt.getResult();
_log.debug("handleCreate " + result.lastInsertRowID);
}
public function create(resultFunc:Function = null,errorFunc:Function = null,ifNotExists:Boolean = false):SQLStatement{
var stmt:SQLStatement = new SQLStatement();
stmt.sqlConnection = this._conn;
stmt.text = createSQL(ifNotExists);
if(resultFunc != null){
stmt.addEventListener(SQLEvent.RESULT,resultFunc);
}
if(errorFunc != null){
stmt.addEventListener(SQLErrorEvent.ERROR,errorFunc);
}
stmt.execute();
return stmt;
}
public function createSQL(ifNot:Boolean = false):String{
var sql:String;
var tableName:String = this._xml.@name;
var ifNotStr:String = "";
if(ifNot == true){
ifNotStr = " IF NOT EXISTS ";
}
sql = "CREATE TABLE " + ifNotStr + tableName + "( \n";
var pkey:Boolean = false;
var cnt:int = 0;
var pkeyList:Array = new Array();
for each(var child:XML in this._xml.field){
var name:String = child.@name;
var type:String = child.@type;
type = type.toUpperCase();
if(cnt> 0 ){
sql = sql + ",\n";
}
cnt++;
var primary:String = child.@primary;
var ai:String = child.@auto_increment;
var primaryKey:String = "";
if((primary == "true" || primary == "TRUE" )){
if(ai == "true" || ai == "TRUE"){
primaryKey = " PRIMARY KEY AUTOINCREMENT ";
pkey = true;
}
else{
pkeyList.push(name);
}
}
var nn:String = child.@required;
var notNull:String = "";
if(nn == "true" || nn == "TRUE"){
notNull = " NOT NULL ";
}
switch(type){
case "INTEGER":
case "INT":
sql = sql + name + " INTEGER " +primaryKey + notNull;
break;
case "VARCHAR":
case "CAHR":
sql = sql + name + " VARCHAR(" + child.@size + ")" + notNull;
break;
case "TEXT":
sql = sql + name + " TEXT " + notNull;
break;
case "TIMESTAMP":
sql = sql + name + " DATE " + notNull;
break;
case "BOOL":
case "BOOLEAN":
sql = sql + name + " BOOLEAN " + notNull;
break;
}
var def:String = child.@default;
if(def != null && def.length> 0 ){
sql = sql + " DEFALUT '" + def + "'";
}
}
if(pkey == false && pkeyList.length> 0){
sql = sql + ",\n PRIMARY KEY (" + pkeyList.join(",") + ")";
}
sql = sql + ");";
// シーケンスがある場合
for each(var c:XML in this._xml.sequence){
// TODO シーケンスの作成で、エラー処理がない
this.createSeq(tableName,c.@name);
}
_log.debug("sql : " + sql);
lastSql = sql;
return sql;
}
private function createSeq(tableName:String,fieldName:String):void{
var n:String = "seq_" + tableName + "_" + fieldName;
var sql:String = "CREATE TABLE IF NOT EXISTS " + n + "( seq INTEGER PRIMARY KEY AUTOINCREMENT );";
var stmt:SQLStatement = new SQLStatement();
stmt.text = sql;
stmt.sqlConnection = this._conn;
stmt.execute();
}
}
}
ってな感じです。

