[AIR] SQLiteを使ってDBに挑戦してみる

2010/04/8

こんにちは。きんくまです。
今回はAIRを使ってDBに挑戦してみようと思います。
私のDBスペックは、DBは業務で全く使ったことがなく、ちょびっと知識をかじったぐらい。

↓今回つくるやつ。テキスト入力欄にSQL文を書いてSQL実行ボタンを押すと一覧が表示されます。

sqlquery1

今回の参考書です。
>> SQLの書き方のツボとコツがゼッタイにわかるドリル本―最初からそう教えてくれればいいのに!
>> Adobe AIR クックブック ―プロフェッショナルに学ぶRIAプログラミングの実践

データベースって何?

・簡単にいうと、表組みデータ(エクセルみたいの)。
・それを検索しやすくしたもの。

だと理解。

AIRには、データベースのSQLiteを使うことができるので、それを使って学習してみようと
思いました。

データベースを使う方法

1)データベースを作る(SQLiteの場合は1つのファイル)
2)データベースに接続する
3)テーブルを作る(1つのデータベースにいくつでも作れる)
4)テーブルにデータを挿入・更新・検索する

となります。それで、これをためせるプログラムを作りました。
AIR本を参考にいろいろと書き加えました。
Flex4が出てネット上でもニュースになってるので、
家にあったもののほとんど使ってなかったFlex3を使ってみました。
なんでmxmlです。
table情報を取得する方法がAIR本に載ってなかったので、ASDocから調べたらなんかできました。
loadSchema()やってから、getSchemaResult()やるみたい。
普通のSQLだとtable情報を調べるのもSQL文を書くんだけど、AIRはちょっとばかし
勝手が違うみたい。

■データベースのユーティリティクラス(ArrayCollectionをArrayに変更すれば普通のflaでも使用できます)

package
{
	import flash.data.SQLConnection;
	import flash.data.SQLResult;
	import flash.data.SQLSchemaResult;
	import flash.data.SQLStatement;
	import flash.data.SQLTableSchema;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.SQLErrorEvent;
	import flash.events.SQLEvent;
	import flash.filesystem.File;
	
	import mx.collections.ArrayCollection;

	public class DBUtil extends EventDispatcher
	{
		private var _myDB:File;
		private var _isOpen:Boolean = false;
		private var _dbConn:SQLConnection;
		private var _resultArrayCollection:ArrayCollection;
		
		public static const ASYNC_CONNECT_COMPLETE:String = "async_connect_complete";
		public static const ASYNC_SQL_COMPLETE:String = "async_sql_complete";
		public static const SCHEMA_COMPLETE:String = "schema_complete";
		
		public function get myDB():File
		{
			return _myDB;
		} 
		
		public function get isOpen():Boolean
		{
			return _isOpen;
		}
		
		public function get resultArrayCollection():ArrayCollection
		{
			return _resultArrayCollection;
		}
		
		public function DBUtil()
		{
			createLocalDB();
		}
		
		public function createLocalDB():void
		{
			var folder:File = File.applicationStorageDirectory.resolvePath('db');
			folder.createDirectory();
			_myDB = folder.resolvePath('myDBFile.db');
			openLocalDB(_myDB, true);
		}
		
		public function openLocalDB(dbFile:File, isAsync:Boolean):void
		{
			_dbConn = new SQLConnection();
			
			if(isAsync){
				_dbConn.openAsync(dbFile);
				
				_dbConn.addEventListener(SQLEvent.OPEN, sqlOpenHD);
				_dbConn.addEventListener(SQLErrorEvent.ERROR, sqlOpenErrorHD);
				
			}else{
				
				try{
					_dbConn.open(dbFile);
				}catch(e:SQLErrorEvent){
					trace('SQL Error: ' + e.error.message);
					trace('SQL Error Detail: ' + e.error.details);
				}
			}
		}
		
		public function sqlOpenHD(e:SQLEvent):void
		{
			_isOpen = true;
			dispatchEvent(new Event(ASYNC_CONNECT_COMPLETE));
		}
		
		public function sqlOpenErrorHD(e:SQLErrorEvent):void
		{
			_dbConn.removeEventListener(SQLEvent.OPEN, sqlOpenHD);
			_dbConn.removeEventListener(SQLErrorEvent.ERROR, sqlOpenErrorHD);		
			//trace('SQL Error: ' + e.error.message);
			//trace('SQL Error Detail: ' + e.error.details);
			dispatchEvent(e);
		}
		
		public function sqlExcuteErrorHD(e:SQLErrorEvent):void
		{
			var state:SQLStatement = e.target as SQLStatement;
			state.removeEventListener(SQLEvent.RESULT, sqlStatementResultHD);
			state.removeEventListener(SQLErrorEvent.ERROR, sqlExcuteErrorHD);
			//trace('SQL Error: ' + e.error.message);
			//trace('SQL Error Detail: ' + e.error.details);
			dispatchEvent(e);
		}
		
		public function executeSQLState(sqlText:String):void
		{
			if(isOpen == false){
				throw new Error('DB is not connected.');
				return;
			}
			var state:SQLStatement = new SQLStatement();
			state.sqlConnection = _dbConn;
			state.text = sqlText;
			state.addEventListener(SQLEvent.RESULT, sqlStatementResultHD);
			state.addEventListener(SQLErrorEvent.ERROR, sqlExcuteErrorHD);
			state.execute();
		}
		
		private function sqlStatementResultHD(e:SQLEvent):void
		{
			var state:SQLStatement = e.target as SQLStatement;
			state.removeEventListener(SQLEvent.RESULT, sqlStatementResultHD);
			state.removeEventListener(SQLErrorEvent.ERROR, sqlExcuteErrorHD);
			
			var result:SQLResult = state.getResult();
			var temp:Array = result.data is Array ? result.data : [{rows:result.rowsAffected}];
			_resultArrayCollection = new ArrayCollection(temp);
			dispatchEvent(new Event(ASYNC_SQL_COMPLETE));
		}
		
		public function getTableInfo():void
		{
			_dbConn.addEventListener(SQLEvent.SCHEMA, sqlSchemaCompHD);
			_dbConn.addEventListener(SQLErrorEvent.ERROR, sqlSchemaErrorHD);
			_dbConn.loadSchema();
		}
		
		private function sqlSchemaCompHD(e:SQLEvent):void
		{
			_dbConn.removeEventListener(SQLEvent.SCHEMA, sqlSchemaCompHD);
			_dbConn.removeEventListener(SQLErrorEvent.ERROR, sqlSchemaErrorHD);
			
			var result:SQLSchemaResult = _dbConn.getSchemaResult();
			var tables:Array = result.tables;
			var temp:Array = [];
			for each(var tableSchema:SQLTableSchema in tables){
				temp.push({
					database:tableSchema.database, 
					name:tableSchema.name, 
					sql:tableSchema.sql,
					columns:tableSchema.columns.length
					});
			}
			_resultArrayCollection = new ArrayCollection(temp);
			dispatchEvent(new Event(SCHEMA_COMPLETE));
		}
		
		private function sqlSchemaErrorHD(e:SQLErrorEvent):void
		{
			_dbConn.removeEventListener(SQLEvent.SCHEMA, sqlSchemaCompHD);
			_dbConn.removeEventListener(SQLErrorEvent.ERROR, sqlSchemaErrorHD);
			//trace('SQL Error: ' + e.error.message);
			//trace('SQL Error Detail: ' + e.error.details);
			dispatchEvent(e);
		}
	}
}

■そして、これを使う用のMXML

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="620" height="420">
	<mx:Script>
		<![CDATA[
			import mx.controls.Alert;
			import mx.collections.ArrayCollection;
			
			private var _dbUtil:DBUtil;
			
			[Bindable]
			private var _myResultAC:ArrayCollection;
			
			private function init():void
			{
				excuteBtn.addEventListener(MouseEvent.CLICK, excuteSQLBtnClickHD);
				tableInfoBtn.addEventListener(MouseEvent.CLICK, tableInfoBtnClickHD);
				
				_dbUtil = new DBUtil();
				_dbUtil.addEventListener(DBUtil.ASYNC_CONNECT_COMPLETE, dbAsyncCompHD);
			}
			
			private function tableInfoBtnClickHD(e:MouseEvent):void
			{
				_dbUtil.addEventListener(DBUtil.SCHEMA_COMPLETE, dbSchemaCompHD);
				_dbUtil.addEventListener(SQLErrorEvent.ERROR, dbSchemaErrorHD);
				_dbUtil.getTableInfo();
			}
			
			private function dbSchemaErrorHD(e:SQLErrorEvent):void
			{
				_dbUtil.removeEventListener(DBUtil.SCHEMA_COMPLETE, dbSchemaCompHD);
				_dbUtil.removeEventListener(SQLErrorEvent.ERROR, dbSchemaErrorHD);
				Alert.show(e.error.message + '\n' + e.error.details);
			}
			
			private function dbSchemaCompHD(e:Event):void
			{
				_dbUtil.removeEventListener(DBUtil.SCHEMA_COMPLETE, dbSchemaCompHD);
				_dbUtil.removeEventListener(SQLErrorEvent.ERROR, dbSchemaErrorHD);
				_myResultAC = _dbUtil.resultArrayCollection;
				//Alert.show('table schema read complete');
			}
			
			private function excuteSQLBtnClickHD(e:MouseEvent):void
			{				
				_dbUtil.addEventListener(DBUtil.ASYNC_SQL_COMPLETE, dbSQLCompleteHD);
				_dbUtil.addEventListener(SQLErrorEvent.ERROR, dbSQLErrorHD);
				_dbUtil.executeSQLState(sqlInputTxt.text);
			}
			
			private function dbSQLErrorHD(e:SQLErrorEvent):void
			{
				_dbUtil.removeEventListener(DBUtil.ASYNC_SQL_COMPLETE, dbSQLCompleteHD);
				_dbUtil.removeEventListener(SQLErrorEvent.ERROR, dbSQLErrorHD);
				Alert.show(e.error.message + '\n' + e.error.details);
			}
			
			private function dbSQLCompleteHD(e:Event):void
			{
				_dbUtil.removeEventListener(DBUtil.ASYNC_SQL_COMPLETE, dbSQLCompleteHD);
				_dbUtil.removeEventListener(SQLErrorEvent.ERROR, dbSQLErrorHD);
				_myResultAC = _dbUtil.resultArrayCollection;
				//Alert.show('SQL excuted !');
			}
			
			private function dbAsyncCompHD(e:Event):void
			{
				Alert.show("SQL connection complete !");
			}
		]]>
	</mx:Script>
	<mx:Panel x="0" y="0" width="588" height="388" layout="absolute" title="SQLTest" initialize="init();">
		<mx:DataGrid x="10" y="111" width="548" height="227" id="myDG" dataProvider="{_myResultAC}"/>
		<mx:Button x="10" y="81" label="SQL実行" id="excuteBtn"/>
		<mx:Button x="88" y="81" label="table情報" id="tableInfoBtn"/>
		<mx:TextArea x="10" y="10" width="548" height="63" id="sqlInputTxt" wordWrap="true" editable="true"/>
	</mx:Panel>
</mx:WindowedApplication>

解説

1)データベースを作る(SQLiteの場合は1つのファイル)
2)データベースに接続する

の部分は、

		public function createLocalDB():void
		{
			var folder:File = File.applicationStorageDirectory.resolvePath('db');
			folder.createDirectory();
			_myDB = folder.resolvePath('myDBFile.db');
			openLocalDB(_myDB, true);
		}
		
		public function openLocalDB(dbFile:File, isAsync:Boolean):void
		{
			_dbConn = new SQLConnection();
			
			if(isAsync){
				_dbConn.openAsync(dbFile);
				
				_dbConn.addEventListener(SQLEvent.OPEN, sqlOpenHD);
				_dbConn.addEventListener(SQLErrorEvent.ERROR, sqlOpenErrorHD);
				
			}else{
				
				try{
					_dbConn.open(dbFile);
				}catch(e:SQLErrorEvent){
					trace('SQL Error: ' + e.error.message);
					trace('SQL Error Detail: ' + e.error.details);
				}
			}
		}

で、1つのDB用のファイル(myDBFile.db)を作って、同期か非同期で開くという感じです。
mxmlの方は起動したら、すでにここまでやってある状態にしました。

3)テーブルを作る(1つのデータベースにいくつでも作れる)
はテキスト入力欄に

CREATE TALBE test1(
 id INTEGER PRIMARY KEY,
 name TEXT,
 phone TEXT
);

とか入力して、SQL実行ボタンを押せばテーブルが作成されます。
それで、ここでもAIR仕様があって、AIRのSQLiteでは使用可能な型がちょびっと変わってるみたいです。

>> ローカルデータベースでの SQL サポート – Adobe® Flex™ 3.2 リファレンスガイド データ型のサポート

SQL本によれば、INT型とかVARCHAR型とかいうのがあるみたいなんですけど、
AIRのSQLiteだとINTEGER型、TEXT型というふうになっとります。

tableをうまく作成したら、table情報ボタンをおします。そしたら、↓の画面のようになったらOK

sqlquery3

4)テーブルにデータを挿入・更新・検索する

テーブルができたんで、データをいれてみましょう。

INSERT INTO test1 VALUES(null, '佐藤', '0123-45-6789');

とやってSQL実行ボタンをおして、エラーがでなければ成功

データを確認します。

SELECT * FROM test1;

とやって、一覧がでてきたらOKです。

注意点

AIRのSQLは他のSQLとは違う部分があるので、SQL文がうまく通らないなあと
思ったときは、下の仕様書のページで確認してみたほうがよさそうです。

>> ローカルデータベースでの SQL サポート


自作iPhoneアプリ 好評発売中!
フォルメモ - シンプルなフォルダつきメモ帳
ジッピー電卓 - 消費税や割引もサクサク計算!

ページトップへ戻る