Archive for 2009/7/2

私はテーブルを作成するときには、いつもXMLで自分流に定義して、それを使ってテーブルをcreateする。
こうするといいことがいくつかある。

  1. ドキュメントをxsltなどで自動的につくれる。

    いろいろなライブラリがすでにあるテーブルからDBにアクセス用に自動的にオブジェクトを作るが、私はXMLから作成している。
    それは、結局ドキュメント化されなければいけないのであれば、やっぱりドキュメントが自動のほうが嬉しい。

  2. テーブルの変化(バージョン管理)もXML(ドキュメント)に含められる。

    だいたい、規定していない属性、項目は処理しない。したがってそれを使ってドキュメントに使える。
    また、テーブル追加や削除( alter table )もそこでやったこともあったなー。これはいらなかったかな。

  3. データベースの違いを吸収できる。

    まあ、AIRの場合にはSQLiteしかないから関係ないが・・・
    でも、サーバでMySQLを使って、そのデータをAIRのSQLiteでキャッシュするというときなんか、同じようなフォーマットで共通化できるから便利じゃないかな。
    たとえば、booleanとか。MySQLのむかーしのバージョンではなかったんだよね。(4.1から導入されたらしい)
    だからこれで定義があると、tinyint(1)と書き換えていた。

でも、いいことばかりじゃない。
複雑なテーブルは作りにくい。でも、逆にいえばこれはいいことともいえる。
そして、これが一番の難題。自分で"create table foo(..... " あれ?つぎなんだっけ?と、文法を忘れる。
したがって、自分で手で作成するときにはリファレンスがないとわからない。
あと、3でも書いたが、違いを吸収してしまうので、新しくできた機能、変化を気にしなくなる。

さて、では実際にはどんな感じでXMLを書くのか?

XML:
  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <table name="status" dbName="its">
  3.     <field name="sysid"     type="integer" primary="true" auto_increment="true">
  4.         <title>システム用ID</title>
  5.     </field>
  6.     <field name="server_id" type="integer" required="true">
  7.         <title>サーバID</title>
  8.         <description>どのサーバのデータか?</description>
  9.     </field>
  10.     <field name="uid"     type="integer" required="true">
  11.         <title>ユーザID</title>
  12.         <description>サーバ上のユニークIDが入る。ただし、ローカルの場合にはsequenceを使う</description>
  13.     </field>
  14.     <field name="createdAt" type="timestamp"    required="true">
  15.         <title>作成日時</title>
  16.     </field>
  17.     <field name="updatedAt" type="timestamp"    required="true" />
  18.     <field name="createdBy" type="integer"   required="true" />
  19.     <field name="updatedBy" type="integer"   required="true" />
  20.     <field name="sync"    type="integer"       required="true" />
  21.     <field name="syncAt"    type="timestamp"    />
  22.    
  23.     <!--  人が管理するためのフィールド -->
  24.     <field name="title"     type="varchar" size="255" required="true" />
  25.     <field name="type"      type="integer"      default="-1" />
  26.     <field name="memo"    type="text"   />
  27.     <field name="enableNew" type="boolean"      required="true" />
  28.     <field name="isRoot"    type="boolean"      required="true" />
  29.     <field name="weight"    type="integer"    default="0">
  30.         <title>表示する際の重み</title>
  31.     </field>
  32.     <sequence name="uid" />
  33. </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();
		}
	}
}

ってな感じです。

RSS
Add to Google

カスタム検索
ソフトウェア&ライブラリ


ライブラリ
airxmail(en)
AIR版メール送受信ライブラリ
airxzip
AIR版ZIP圧縮・解凍ライブラリ
カレンダー
2009年7月
« 6月   8月 »
 12345
6789101112
13141516171819
20212223242526
2728293031  
アーカイブ
カテゴリ
にほんブログ村 IT技術ブログへ